mirror of
https://github.com/isar/libmdbx.git
synced 2025-04-01 14:52:57 +08:00
mdbx: добавление mdbx_cursor_close2()
в API (backport).
This commit is contained in:
parent
33ceba0a5a
commit
753b2270fd
30
mdbx.h
30
mdbx.h
@ -5210,12 +5210,17 @@ LIBMDBX_API int mdbx_cursor_reset(MDBX_cursor *cursor);
|
||||
* \retval MDBX_EINVAL An invalid parameter was specified. */
|
||||
LIBMDBX_API int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **cursor);
|
||||
|
||||
/** \brief Close a cursor handle.
|
||||
/** \brief Closes a cursor handle without returning error code.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
* The cursor handle will be freed and must not be used again after this call,
|
||||
* but its transaction may still be live.
|
||||
*
|
||||
* This function returns `void` but panic in case of error. Use \ref mdbx_cursor_close2()
|
||||
* if you need to receive an error code instead of an app crash.
|
||||
*
|
||||
* \see mdbx_cursor_close2
|
||||
*
|
||||
* \note In contrast to LMDB, the MDBX required that any opened cursors can be
|
||||
* reused and must be freed explicitly, regardless ones was opened in a
|
||||
* read-only or write transaction. The REASON for this is eliminates ambiguity
|
||||
@ -5226,6 +5231,29 @@ LIBMDBX_API int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **curs
|
||||
* or \ref mdbx_cursor_create(). */
|
||||
LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
|
||||
|
||||
/** \brief Closes a cursor handle with returning error code.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
* The cursor handle will be freed and must not be used again after this call,
|
||||
* but its transaction may still be live.
|
||||
*
|
||||
* \see mdbx_cursor_close
|
||||
*
|
||||
* \note In contrast to LMDB, the MDBX required that any opened cursors can be
|
||||
* reused and must be freed explicitly, regardless ones was opened in a
|
||||
* read-only or write transaction. The REASON for this is eliminates ambiguity
|
||||
* which helps to avoid errors such as: use-after-free, double-free, i.e.
|
||||
* memory corruption and segfaults.
|
||||
*
|
||||
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open()
|
||||
* or \ref mdbx_cursor_create().
|
||||
* \returns A non-zero error value on failure and 0 on success,
|
||||
* some possible errors are:
|
||||
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
|
||||
* by current thread.
|
||||
* \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.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
|
5
mdbx.h++
5
mdbx.h++
@ -4545,7 +4545,10 @@ public:
|
||||
}
|
||||
|
||||
/// \brief Explicitly closes the cursor.
|
||||
void close();
|
||||
inline void close() {
|
||||
error::success_or_throw(::mdbx_cursor_close2(handle_));
|
||||
handle_ = nullptr;
|
||||
}
|
||||
|
||||
cursor_managed(cursor_managed &&) = default;
|
||||
cursor_managed &operator=(cursor_managed &&other) noexcept {
|
||||
|
@ -93,6 +93,10 @@ int mdbx_cursor_unbind(MDBX_cursor *mc) {
|
||||
/* TODO: реализовать при переходе на двусвязный список курсоров */
|
||||
return LOG_IFERR(MDBX_EINVAL);
|
||||
|
||||
int rc = check_txn(mc->txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return LOG_IFERR(rc);
|
||||
|
||||
if (unlikely(!mc->txn || mc->txn->signature != txn_signature)) {
|
||||
ERROR("Wrong cursor's transaction %p 0x%x", __Wpedantic_format_voidptr(mc->txn), mc->txn ? mc->txn->signature : 0);
|
||||
return LOG_IFERR(MDBX_PROBLEM);
|
||||
@ -137,42 +141,63 @@ int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) {
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
void mdbx_cursor_close(MDBX_cursor *mc) {
|
||||
if (likely(mc)) {
|
||||
ENSURE(nullptr, mc->signature == cur_signature_live || mc->signature == cur_signature_ready4dispose);
|
||||
MDBX_txn *const txn = mc->txn;
|
||||
if (!mc->backup) {
|
||||
mc->txn = nullptr;
|
||||
/* Unlink from txn, if tracked. */
|
||||
if (mc->next != mc) {
|
||||
ENSURE(txn->env, check_txn(txn, 0) == MDBX_SUCCESS);
|
||||
const size_t dbi = (kvx_t *)mc->clc - txn->env->kvs;
|
||||
tASSERT(txn, dbi < txn->n_dbi);
|
||||
if (dbi < txn->n_dbi) {
|
||||
MDBX_cursor **prev = &txn->cursors[dbi];
|
||||
while (*prev) {
|
||||
ENSURE(txn->env, (*prev)->signature == cur_signature_live || (*prev)->signature == cur_signature_wait4eot);
|
||||
if (*prev == mc)
|
||||
break;
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
tASSERT(txn, *prev == mc);
|
||||
*prev = mc->next;
|
||||
}
|
||||
mc->next = mc;
|
||||
}
|
||||
mc->signature = 0;
|
||||
osal_free(mc);
|
||||
} else {
|
||||
/* Cursor closed before nested txn ends */
|
||||
tASSERT(txn, mc->signature == cur_signature_live);
|
||||
ENSURE(txn->env, check_txn_rw(txn, 0) == MDBX_SUCCESS);
|
||||
be_poor(mc);
|
||||
mc->signature = cur_signature_wait4eot;
|
||||
}
|
||||
void mdbx_cursor_close(MDBX_cursor *cursor) {
|
||||
if (likely(cursor)) {
|
||||
int err = mdbx_cursor_close2(cursor);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
mdbx_panic("%s:%d error %d (%s) while closing cursor", __func__, __LINE__, err, mdbx_liberr2str(err));
|
||||
}
|
||||
}
|
||||
|
||||
int mdbx_cursor_close2(MDBX_cursor *mc) {
|
||||
if (unlikely(!mc))
|
||||
return LOG_IFERR(MDBX_EINVAL);
|
||||
|
||||
if (mc->signature == cur_signature_ready4dispose) {
|
||||
if (unlikely(mc->txn || mc->backup))
|
||||
return LOG_IFERR(MDBX_PANIC);
|
||||
cursor_drown((cursor_couple_t *)mc);
|
||||
mc->signature = 0;
|
||||
osal_free(mc);
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_live))
|
||||
return LOG_IFERR(MDBX_EBADSIGN);
|
||||
|
||||
MDBX_txn *const txn = mc->txn;
|
||||
int rc = check_txn(txn, MDBX_TXN_FINISHED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return LOG_IFERR(rc);
|
||||
|
||||
if (mc->backup) {
|
||||
/* Cursor closed before nested txn ends */
|
||||
cursor_reset((cursor_couple_t *)mc);
|
||||
mc->signature = cur_signature_wait4eot;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
if (mc->next != mc) {
|
||||
const size_t dbi = cursor_dbi(mc);
|
||||
cASSERT(mc, dbi < mc->txn->n_dbi);
|
||||
cASSERT(mc, &mc->txn->env->kvs[dbi].clc == mc->clc);
|
||||
if (likely(dbi < txn->n_dbi)) {
|
||||
MDBX_cursor **prev = &txn->cursors[dbi];
|
||||
while (/* *prev && */ *prev != mc) {
|
||||
ENSURE(txn->env, (*prev)->signature == cur_signature_live || (*prev)->signature == cur_signature_wait4eot);
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
tASSERT(txn, *prev == mc);
|
||||
*prev = mc->next;
|
||||
}
|
||||
mc->next = mc;
|
||||
}
|
||||
cursor_drown((cursor_couple_t *)mc);
|
||||
mc->signature = 0;
|
||||
osal_free(mc);
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest) {
|
||||
if (unlikely(!src))
|
||||
return LOG_IFERR(MDBX_EINVAL);
|
||||
|
@ -252,9 +252,9 @@ MDBX_cursor *cursor_eot(MDBX_cursor *mc, MDBX_txn *txn, const bool merge) {
|
||||
osal_free(bk);
|
||||
} else {
|
||||
ENSURE(mc->txn->env, stage == cur_signature_live);
|
||||
cursor_drown((cursor_couple_t *)mc);
|
||||
mc->next = mc;
|
||||
mc->signature = cur_signature_ready4dispose /* Cursor may be reused */;
|
||||
mc->next = mc;
|
||||
cursor_drown((cursor_couple_t *)mc);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
@ -1590,15 +1590,6 @@ __cold bool txn::rename_map(const ::std::string &old_name, const ::std::string &
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void cursor_managed::close() {
|
||||
if (MDBX_UNLIKELY(!handle_))
|
||||
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_EINVAL);
|
||||
::mdbx_cursor_close(handle_);
|
||||
handle_ = nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
__cold ::std::ostream &operator<<(::std::ostream &out, const slice &it) {
|
||||
out << "{";
|
||||
if (!it.is_valid())
|
||||
|
Loading…
x
Reference in New Issue
Block a user