diff --git a/mdbx.h b/mdbx.h index ecaa3e18..fdc9c6e5 100644 --- a/mdbx.h +++ b/mdbx.h @@ -5269,15 +5269,12 @@ LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor); * \retval MDBX_EINVAL An invalid parameter was specified. */ LIBMDBX_API int mdbx_cursor_close2(MDBX_cursor *cursor); -/** \brief Unbind or closes all cursors of a given transaction. +/** \brief Unbind or closes all cursors of a given transaction and of all + * its parent transactions if ones are. * \ingroup c_cursors * - * Unbinds either closes all cursors associated (opened or renewed) with - * a given transaction in a bulk with minimal overhead. - * - * A transaction should not be nested, since in this case no way to restore - * state if this nested transaction will be aborted, nor impossible to define - * the expected behavior. + * Unbinds either closes all cursors associated (opened, renewed or binded) with + * the given transaction in a bulk with minimal overhead. * * \see mdbx_cursor_unbind() * \see mdbx_cursor_close() @@ -5292,19 +5289,16 @@ LIBMDBX_API int mdbx_cursor_close2(MDBX_cursor *cursor); * some possible errors are: * \retval MDBX_THREAD_MISMATCH Given transaction is not owned * by current thread. - * \retval MDBX_BAD_TXN Given transaction is invalid, nested or has + * \retval MDBX_BAD_TXN Given transaction is invalid or has * a child/nested transaction transaction. */ LIBMDBX_API int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind, size_t *count); -/** \brief Unbind or closes all cursors of a given transaction. +/** \brief Unbind or closes all cursors of a given transaction and of all + * its parent transactions if ones are. * \ingroup c_cursors * - * Unbinds either closes all cursors associated (opened or renewed) with - * a given transaction in a bulk with minimal overhead. - * - * A transaction should not be nested, since in this case no way to restore - * state if this nested transaction will be aborted, nor impossible to define - * the expected behavior. + * Unbinds either closes all cursors associated (opened, renewed or binded) with + * the given transaction in a bulk with minimal overhead. * * \see mdbx_cursor_unbind() * \see mdbx_cursor_close() @@ -5317,7 +5311,7 @@ LIBMDBX_API int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind * some possible errors are: * \retval MDBX_THREAD_MISMATCH Given transaction is not owned * by current thread. - * \retval MDBX_BAD_TXN Given transaction is invalid, nested or has + * \retval MDBX_BAD_TXN Given transaction is invalid or has * a child/nested transaction transaction. */ LIBMDBX_INLINE_API(int, mdbx_txn_release_all_cursors, (const MDBX_txn *txn, bool unbind)) { return mdbx_txn_release_all_cursors_ex(txn, unbind, NULL); diff --git a/src/api-cursor.c b/src/api-cursor.c index b930f36c..d4ce3beb 100644 --- a/src/api-cursor.c +++ b/src/api-cursor.c @@ -234,28 +234,43 @@ int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind, size_t *co int rc = check_txn(txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD); if (unlikely(rc != MDBX_SUCCESS)) return LOG_IFERR(rc); - if (unlikely(txn->parent)) { - rc = MDBX_BAD_TXN; - ERROR("%s, err %d", "must not unbind or close cursors for a nested txn", rc); - return rc; - } size_t n = 0; - TXN_FOREACH_DBI_FROM(txn, i, MAIN_DBI) { - while (txn->cursors[i]) { - ++n; - MDBX_cursor *mc = txn->cursors[i]; - ENSURE(nullptr, mc->signature == cur_signature_live && (mc->next != mc) && !mc->backup); - txn->cursors[i] = mc->next; - mc->next = mc; - mc->signature = cur_signature_ready4dispose; - cursor_drown((cursor_couple_t *)mc); - if (!unbind) { - mc->signature = 0; - osal_free(mc); + do { + TXN_FOREACH_DBI_FROM(txn, i, MAIN_DBI) { + MDBX_cursor *mc = txn->cursors[i], *next = nullptr; + if (mc) { + txn->cursors[i] = nullptr; + do { + next = mc->next; + if (mc->signature == cur_signature_live) { + mc->signature = cur_signature_wait4eot; + cursor_drown((cursor_couple_t *)mc); + } else + ENSURE(nullptr, mc->signature == cur_signature_wait4eot); + if (mc->backup) { + MDBX_cursor *bk = mc->backup; + mc->next = bk->next; + mc->backup = bk->backup; + mc->backup = nullptr; + bk->signature = 0; + bk = bk->next; + osal_free(bk); + } else { + mc->signature = cur_signature_ready4dispose; + mc->next = mc; + ++n; + if (!unbind) { + mc->signature = 0; + osal_free(mc); + } + } + } while ((mc = next) != nullptr); } } - } + txn = txn->parent; + } while (txn); + if (count) *count = n; return MDBX_SUCCESS;