diff --git a/mdbx.h b/mdbx.h index 2c062316..68493003 100644 --- a/mdbx.h +++ b/mdbx.h @@ -4803,6 +4803,27 @@ LIBMDBX_API int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, * or \ref mdbx_cursor_create(). */ LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor); +/** \brief Unbind or closes all cursors of a given transaction. + * \ingroup c_cursors + * + * Unbinds either closes all cursors associated (opened or renewed) with + * a given transaction in a bulk with minimal overhead. + * + * \see mdbx_cursor_unbind() + * \see mdbx_cursor_close() + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] unbind If non-zero, unbinds cursors and leaves ones reusable. + * Otherwise close and dispose cursors. + * + * \returns A negative error value on failure or the number of closed cursors + * on success, some possible errors are: + * \retval MDBX_THREAD_MISMATCH Given transaction is not owned + * by current thread. + * \retval MDBX_BAD_TXN Given transaction is invalid or has + * a child/nested transaction transaction. */ +LIBMDBX_API int mdbx_txn_release_all_cursors(const MDBX_txn *txn, bool unbind); + /** \brief Renew a cursor handle for use within the given transaction. * \ingroup c_cursors * diff --git a/mdbx.h++ b/mdbx.h++ index e3607b61..216c0631 100644 --- a/mdbx.h++ +++ b/mdbx.h++ @@ -3841,6 +3841,15 @@ public: /// \brief Opens cursor for specified key-value map handle. inline cursor_managed open_cursor(map_handle map); + /// \brief Unbind or close all cursors. + inline size_t release_all_cursors(bool unbind) const; + + /// \brief Close all cursors. + inline size_t close_all_cursors() const { return release_all_cursors(false); } + + /// \brief Unbind all cursors. + inline size_t unbind_all_cursors() const { return release_all_cursors(true); } + /// \brief Open existing key-value map. inline map_handle open_map( const char *name, @@ -5466,6 +5475,13 @@ inline cursor_managed txn::open_cursor(map_handle map) { return cursor_managed(ptr); } +inline size_t txn::release_all_cursors(bool unbind) const { + int err = ::mdbx_txn_release_all_cursors(handle_, unbind); + if (MDBX_UNLIKELY(err < 0)) + MDBX_CXX20_UNLIKELY error::throw_exception(err); + return size_t(err); +} + inline ::mdbx::map_handle txn::open_map(const char *name, const ::mdbx::key_mode key_mode, const ::mdbx::value_mode value_mode) const { diff --git a/src/core.c b/src/core.c index 8e4b364f..293b4aed 100644 --- a/src/core.c +++ b/src/core.c @@ -19019,6 +19019,32 @@ void mdbx_cursor_close(MDBX_cursor *mc) { } } +int mdbx_txn_release_all_cursors(const MDBX_txn *txn, bool unbind) { + int rc = check_txn(txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD); + if (likely(rc == MDBX_SUCCESS)) { + for (size_t i = FREE_DBI; i < txn->mt_numdbs; ++i) { + while (txn->mt_cursors[i]) { + MDBX_cursor *mc = txn->mt_cursors[i]; + ENSURE(NULL, mc->mc_signature == MDBX_MC_LIVE && + (mc->mc_flags & C_UNTRACK) && !mc->mc_backup); + rc = likely(rc < INT_MAX) ? rc + 1 : rc; + txn->mt_cursors[i] = mc->mc_next; + if (unbind) { + mc->mc_signature = MDBX_MC_READY4CLOSE; + mc->mc_flags = 0; + } else { + mc->mc_signature = 0; + mc->mc_next = mc; + osal_free(mc); + } + } + } + } else { + eASSERT(nullptr, rc < 0); + } + return rc; +} + MDBX_txn *mdbx_cursor_txn(const MDBX_cursor *mc) { if (unlikely(!mc || mc->mc_signature != MDBX_MC_LIVE)) return NULL;