From 465459dc58b2ee2ae2976c4bbfc0ce44c156374a Mon Sep 17 00:00:00 2001 From: Leo Yuriev Date: Tue, 6 Jun 2017 17:05:30 +0300 Subject: [PATCH] mdbx: add MDBX_txn.mt_owner and MDBX_THREAD_MISMATCH. --- libmdbx.files | 1 + mdbx.h | 8 +++-- src/bits.h | 1 + src/mdbx.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/osal.c | 8 ----- src/osal.h | 9 ++++- 6 files changed, 104 insertions(+), 16 deletions(-) diff --git a/libmdbx.files b/libmdbx.files index 3f51a9b5..b6b1d7a2 100644 --- a/libmdbx.files +++ b/libmdbx.files @@ -2,6 +2,7 @@ AUTHORS LICENSE Makefile README.md +TODO.md mdbx.h src/bits.h src/defs.h diff --git a/mdbx.h b/mdbx.h index f696d0a3..ec38193c 100644 --- a/mdbx.h +++ b/mdbx.h @@ -414,10 +414,14 @@ typedef enum MDBX_cursor_op { * when mdbx_cursor_put() called with MDBX_CURRENT option. */ #define MDBX_EKEYMISMATCH (-30418) -/* Database is too large for current system, i.e. could NOT be mapped into RAM. - */ +/* Database is too large for current system, + * e.g. could NOT be mapped into RAM. */ #define MDBX_TOO_LARGE (-30417) +/* A thread has attempted to use a not owned object, + * e.g. a transaction that started by another thread. */ +#define MDBX_THREAD_MISMATCH (-30416) + /* Statistics for a database in the environment */ typedef struct MDBX_stat { uint32_t ms_psize; /* Size of a database page. diff --git a/src/bits.h b/src/bits.h index de87d85d..3753f7c0 100644 --- a/src/bits.h +++ b/src/bits.h @@ -527,6 +527,7 @@ struct MDBX_txn { * dirtylist into mt_parent after freeing hidden mt_parent pages. */ unsigned mt_dirtyroom; mdbx_canary mt_canary; + mdbx_tid_t mt_owner; /* thread ID that owns this transaction */ }; /* Enough space for 2^32 nodes with minimum of 2 keys per node. I.e., plenty. diff --git a/src/mdbx.c b/src/mdbx.c index 14ae7d45..31720305 100644 --- a/src/mdbx.c +++ b/src/mdbx.c @@ -690,8 +690,11 @@ static const char *__mdbx_strerr(int errnum) { return "MDBX_EKEYMISMATCH: The given key value is mismatched to the " "current cursor position"; case MDBX_TOO_LARGE: - return "Database is too large for current system, i.e.could NOT be mapped " - "into RAM."; + return "MDBX_TOO_LARGE: Database is too large for current system, " + "e.g. could NOT be mapped into RAM"; + case MDBX_THREAD_MISMATCH: + return "MDBX_THREAD_MISMATCH: A thread has attempted to use a not " + "owned object, e.g. a transaction that started by another thread"; default: return NULL; } @@ -968,13 +971,13 @@ static void mdbx_audit(MDBX_txn *txn) { int mdbx_cmp(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, const MDBX_val *b) { - mdbx_ensure(NULL, txn->mt_signature == MDBX_MT_SIGNATURE); + mdbx_assert(NULL, txn->mt_signature == MDBX_MT_SIGNATURE); return txn->mt_dbxs[dbi].md_cmp(a, b); } int mdbx_dcmp(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, const MDBX_val *b) { - mdbx_ensure(NULL, txn->mt_signature == MDBX_MT_SIGNATURE); + mdbx_assert(NULL, txn->mt_signature == MDBX_MT_SIGNATURE); return txn->mt_dbxs[dbi].md_dcmp(a, b); } @@ -2394,6 +2397,9 @@ int mdbx_txn_renew(MDBX_txn *txn) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!F_ISSET(txn->mt_flags, MDBX_TXN_RDONLY | MDBX_TXN_FINISHED))) return MDBX_EINVAL; @@ -2434,6 +2440,9 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, if (unlikely(parent->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EINVAL; + if (unlikely(parent->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + /* Nested transactions: Max 1 child, write txns only, no writemap */ flags |= parent->mt_flags; if (unlikely(flags & (MDBX_RDONLY | MDBX_WRITEMAP | MDBX_TXN_BLOCKED))) { @@ -2510,6 +2519,7 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, if (txn != env->me_txn0) free(txn); } else { + txn->mt_owner = mdbx_thread_self(); txn->mt_signature = MDBX_MT_SIGNATURE; *ret = txn; mdbx_debug("begin txn %" PRIaTXN "%c %p on env %p, root page %" PRIaPGNO "", @@ -2593,7 +2603,7 @@ static int mdbx_txn_end(MDBX_txn *txn, unsigned mode) { mdbx_coherent_barrier(); txn->mt_numdbs = 0; /* prevent further DBI activity */ txn->mt_flags |= MDBX_TXN_FINISHED; - + txn->mt_owner = 0; } else if (!F_ISSET(txn->mt_flags, MDBX_TXN_FINISHED)) { pgno_t *pghead = env->me_pghead; @@ -2621,6 +2631,7 @@ static int mdbx_txn_end(MDBX_txn *txn, unsigned mode) { env->me_pglast = 0; env->me_txn = NULL; + txn->mt_owner = 0; txn->mt_signature = 0; mode = 0; /* txn == env->me_txn0, do not free() it */ @@ -2640,6 +2651,7 @@ static int mdbx_txn_end(MDBX_txn *txn, unsigned mode) { if (mode & MDBX_END_FREE) { mdbx_ensure(env, txn != env->me_txn0); + txn->mt_owner = 0; txn->mt_signature = 0; free(txn); } @@ -2654,6 +2666,9 @@ int mdbx_txn_reset(MDBX_txn *txn) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + /* This call is only valid for read-only txns */ if (unlikely(!(txn->mt_flags & MDBX_TXN_RDONLY))) return MDBX_EINVAL; @@ -2669,6 +2684,9 @@ int mdbx_txn_abort(MDBX_txn *txn) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (F_ISSET(txn->mt_flags, MDBX_TXN_RDONLY)) /* LY: don't close DBI-handles in MDBX mode */ return mdbx_txn_end(txn, MDBX_END_ABORT | MDBX_END_UPDATE | MDBX_END_SLOT | @@ -3133,6 +3151,9 @@ int mdbx_txn_commit(MDBX_txn *txn) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + MDBX_env *env = txn->mt_env; if (unlikely(env->me_pid != mdbx_getpid())) { env->me_flags |= MDBX_FATAL_ERROR; @@ -5256,6 +5277,9 @@ int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))) return MDBX_EINVAL; @@ -7162,6 +7186,9 @@ int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_VALID))) return MDBX_EINVAL; @@ -7197,6 +7224,9 @@ int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *mc) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(mc->mc_signature != MDBX_MC_SIGNATURE && mc->mc_signature != MDBX_MC_READY4CLOSE)) return MDBX_EINVAL; @@ -7231,6 +7261,9 @@ int mdbx_cursor_count(MDBX_cursor *mc, size_t *countp) { if (unlikely(mc->mc_signature != MDBX_MC_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(mc->mc_txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(mc->mc_txn->mt_flags & MDBX_TXN_BLOCKED)) return MDBX_BAD_TXN; @@ -8054,6 +8087,9 @@ int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))) return MDBX_EINVAL; @@ -8543,6 +8579,9 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))) return MDBX_EINVAL; @@ -9236,6 +9275,9 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags, if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) return MDBX_BAD_TXN; @@ -9368,6 +9410,9 @@ int __cold mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *arg, if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_VALID))) return MDBX_EINVAL; @@ -9422,6 +9467,9 @@ int mdbx_dbi_flags(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_VALID))) return MDBX_EINVAL; @@ -9528,6 +9576,9 @@ int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, int del) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))) return MDBX_EINVAL; @@ -9602,6 +9653,9 @@ int mdbx_set_compare(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cmp_func *cmp) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))) return MDBX_EINVAL; @@ -9616,6 +9670,9 @@ int mdbx_set_dupsort(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cmp_func *cmp) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))) return MDBX_EINVAL; @@ -9887,6 +9944,9 @@ int mdbx_txn_straggler(MDBX_txn *txn, int *percent) if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!txn->mt_ro_reader)) return -1; @@ -10041,9 +10101,13 @@ int __cold mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor, void *user) { if (unlikely(!txn)) return MDBX_BAD_TXN; + if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + mdbx_walk_ctx_t ctx; ctx.mw_txn = txn; ctx.mw_user = user; @@ -10069,6 +10133,9 @@ int mdbx_canary_put(MDBX_txn *txn, const mdbx_canary *canary) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) return MDBX_BAD_TXN; @@ -10097,9 +10164,13 @@ int mdbx_canary_put(MDBX_txn *txn, const mdbx_canary *canary) { int mdbx_canary_get(MDBX_txn *txn, mdbx_canary *canary) { if (unlikely(txn == NULL || canary == NULL)) return MDBX_EINVAL; + if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + *canary = txn->mt_canary; return MDBX_SUCCESS; } @@ -10197,6 +10268,9 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *new_data, if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(old_data->iov_base == NULL && old_data->iov_len)) return MDBX_EINVAL; @@ -10332,6 +10406,9 @@ int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))) return MDBX_EINVAL; @@ -10396,6 +10473,9 @@ int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr) { if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(txn->mt_flags & MDBX_TXN_RDONLY)) return MDBX_RESULT_FALSE; @@ -10457,6 +10537,9 @@ int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result, if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) return MDBX_EBADSIGN; + if (unlikely(txn->mt_owner != mdbx_thread_self())) + return MDBX_THREAD_MISMATCH; + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))) return MDBX_EINVAL; diff --git a/src/osal.c b/src/osal.c index 4b56be70..7196cf6c 100644 --- a/src/osal.c +++ b/src/osal.c @@ -615,14 +615,6 @@ void mdbx_thread_rthc_set(mdbx_thread_key_t key, const void *value) { #endif } -mdbx_tid_t mdbx_thread_self(void) { -#if defined(_WIN32) || defined(_WIN64) - return GetCurrentThreadId(); -#else - return pthread_self(); -#endif -} - int mdbx_thread_create(mdbx_thread_t *thread, THREAD_RESULT(THREAD_CALL *start_routine)(void *), void *arg) { diff --git a/src/osal.h b/src/osal.h index ee776358..6265ea14 100644 --- a/src/osal.h +++ b/src/osal.h @@ -439,7 +439,6 @@ int mdbx_thread_create(mdbx_thread_t *thread, THREAD_RESULT(THREAD_CALL *start_routine)(void *), void *arg); int mdbx_thread_join(mdbx_thread_t thread); -mdbx_tid_t mdbx_thread_self(void); int mdbx_thread_key_create(mdbx_thread_key_t *key); void mdbx_thread_key_delete(mdbx_thread_key_t key); void *mdbx_thread_rthc_get(mdbx_thread_key_t key); @@ -465,6 +464,14 @@ static __inline mdbx_pid_t mdbx_getpid(void) { #endif } +static __inline mdbx_tid_t mdbx_thread_self(void) { +#if defined(_WIN32) || defined(_WIN64) + return GetCurrentThreadId(); +#else + return pthread_self(); +#endif +} + void mdbx_osal_jitter(bool tiny); /*----------------------------------------------------------------------------*/