mirror of
https://github.com/isar/libmdbx.git
synced 2025-04-01 02:32: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. */
|
* \retval MDBX_EINVAL An invalid parameter was specified. */
|
||||||
LIBMDBX_API int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **cursor);
|
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
|
* \ingroup c_cursors
|
||||||
*
|
*
|
||||||
* The cursor handle will be freed and must not be used again after this call,
|
* The cursor handle will be freed and must not be used again after this call,
|
||||||
* but its transaction may still be live.
|
* 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
|
* \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
|
* reused and must be freed explicitly, regardless ones was opened in a
|
||||||
* read-only or write transaction. The REASON for this is eliminates ambiguity
|
* 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(). */
|
* or \ref mdbx_cursor_create(). */
|
||||||
LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
|
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.
|
/** \brief Unbind or closes all cursors of a given transaction.
|
||||||
* \ingroup c_cursors
|
* \ingroup c_cursors
|
||||||
*
|
*
|
||||||
|
5
mdbx.h++
5
mdbx.h++
@ -4545,7 +4545,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Explicitly closes the cursor.
|
/// \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(cursor_managed &&) = default;
|
||||||
cursor_managed &operator=(cursor_managed &&other) noexcept {
|
cursor_managed &operator=(cursor_managed &&other) noexcept {
|
||||||
|
@ -93,6 +93,10 @@ int mdbx_cursor_unbind(MDBX_cursor *mc) {
|
|||||||
/* TODO: реализовать при переходе на двусвязный список курсоров */
|
/* TODO: реализовать при переходе на двусвязный список курсоров */
|
||||||
return LOG_IFERR(MDBX_EINVAL);
|
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)) {
|
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);
|
ERROR("Wrong cursor's transaction %p 0x%x", __Wpedantic_format_voidptr(mc->txn), mc->txn ? mc->txn->signature : 0);
|
||||||
return LOG_IFERR(MDBX_PROBLEM);
|
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;
|
return MDBX_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mdbx_cursor_close(MDBX_cursor *mc) {
|
void mdbx_cursor_close(MDBX_cursor *cursor) {
|
||||||
if (likely(mc)) {
|
if (likely(cursor)) {
|
||||||
ENSURE(nullptr, mc->signature == cur_signature_live || mc->signature == cur_signature_ready4dispose);
|
int err = mdbx_cursor_close2(cursor);
|
||||||
MDBX_txn *const txn = mc->txn;
|
if (unlikely(err != MDBX_SUCCESS))
|
||||||
if (!mc->backup) {
|
mdbx_panic("%s:%d error %d (%s) while closing cursor", __func__, __LINE__, err, mdbx_liberr2str(err));
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest) {
|
||||||
if (unlikely(!src))
|
if (unlikely(!src))
|
||||||
return LOG_IFERR(MDBX_EINVAL);
|
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);
|
osal_free(bk);
|
||||||
} else {
|
} else {
|
||||||
ENSURE(mc->txn->env, stage == cur_signature_live);
|
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->signature = cur_signature_ready4dispose /* Cursor may be reused */;
|
||||||
|
mc->next = mc;
|
||||||
|
cursor_drown((cursor_couple_t *)mc);
|
||||||
}
|
}
|
||||||
return next;
|
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) {
|
__cold ::std::ostream &operator<<(::std::ostream &out, const slice &it) {
|
||||||
out << "{";
|
out << "{";
|
||||||
if (!it.is_valid())
|
if (!it.is_valid())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user