From 87de3fc25fa906253c3fb361396d8b43057b2729 Mon Sep 17 00:00:00 2001 From: Leonid Yuriev Date: Mon, 3 Aug 2020 12:56:57 +0300 Subject: [PATCH] mdbx: add MDBX_TXN_RDONLY_PREPARE. Change-Id: I95647d1679b69d1e97514a45f20d7373174244d5 --- mdbx.h | 8 +++++++ src/core.c | 59 +++++++++++++++++++++++++++---------------------- src/internals.h | 10 +++++---- 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/mdbx.h b/mdbx.h index db0ac8e1..5a981abc 100644 --- a/mdbx.h +++ b/mdbx.h @@ -1010,6 +1010,14 @@ enum MDBX_txn_flags_t { * block each other and a write transactions. */ MDBX_TXN_RDONLY = MDBX_RDONLY, + /** Prepare but not start readonly transaction. + * + * Transaction will not be started immediately, but created transaction handle + * will be ready for use with \ref mdbx_txn_renew(). This flag allows to + * preallocate memory and assign a reader slot, thus avoiding these operations + * at the next start of the transaction. */ + MDBX_TXN_RDONLY_PREPARE = MDBX_TXN_RDONLY | MDBX_NOMEMINIT, + /** Do not block when starting a write transaction. */ MDBX_TXN_TRY = UINT32_C(0x10000000), diff --git a/src/core.c b/src/core.c index 3a712d93..95ca7966 100644 --- a/src/core.c +++ b/src/core.c @@ -5909,8 +5909,6 @@ typedef struct { static bind_rslot_result bind_rslot(MDBX_env *env, const uintptr_t tid) { mdbx_assert(env, env->me_lck); - mdbx_assert(env, (env->me_flags & (MDBX_NOTLS | MDBX_ENV_TXKEY)) == - MDBX_ENV_TXKEY); mdbx_assert(env, env->me_lck->mti_magic_and_version == MDBX_LOCK_MAGIC); mdbx_assert(env, env->me_lck->mti_os_and_format == MDBX_LOCK_FORMAT); @@ -6043,7 +6041,7 @@ __cold int mdbx_thread_unregister(MDBX_env *env) { } /* Common code for mdbx_txn_begin() and mdbx_txn_renew(). */ -static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) { +static int mdbx_txn_renew0(MDBX_txn *txn, const unsigned flags) { MDBX_env *env = txn->mt_env; int rc; @@ -6067,10 +6065,9 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) { STATIC_ASSERT(offsetof(MDBX_lockinfo, mti_readers) % MDBX_CACHELINE_SIZE == 0); - mdbx_assert(env, (flags & ~(MDBX_TXN_BEGIN_FLAGS | MDBX_TXN_SPILLS | - MDBX_WRITEMAP)) == 0); const uintptr_t tid = mdbx_thread_self(); if (flags & MDBX_TXN_RDONLY) { + mdbx_assert(env, (flags & ~(MDBX_TXN_RO_BEGIN_FLAGS | MDBX_WRITEMAP)) == 0); txn->mt_flags = MDBX_TXN_RDONLY | (env->me_flags & (MDBX_NOTLS | MDBX_WRITEMAP)); MDBX_reader *r = txn->to.reader; @@ -6096,6 +6093,17 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) { return brs.err; r = brs.rslot; } + txn->to.reader = r; + if (flags & (MDBX_TXN_RDONLY_PREPARE - MDBX_TXN_RDONLY)) { + mdbx_assert(env, r->mr_txnid.inconsistent >= SAFE64_INVALID_THRESHOLD); + mdbx_assert(env, txn->mt_txnid == 0); + mdbx_assert(env, txn->mt_owner == 0); + mdbx_assert(env, txn->mt_numdbs == 0); + mdbx_assert(env, r->mr_snapshot_pages_used == 0); + r->mr_snapshot_pages_used = 0; + txn->mt_flags = MDBX_TXN_RDONLY | MDBX_TXN_FINISHED; + return MDBX_SUCCESS; + } /* Seek & fetch the last meta */ while (1) { @@ -6142,12 +6150,13 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) { goto bailout; } mdbx_assert(env, txn->mt_txnid >= *env->me_oldest); - txn->to.reader = r; txn->mt_dbxs = env->me_dbxs; /* mostly static anyway */ mdbx_ensure(env, txn->mt_txnid >= /* paranoia is appropriate here */ *env->me_oldest); txn->mt_numdbs = env->me_numdbs; } else { + mdbx_assert(env, (flags & ~(MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_SPILLS | + MDBX_WRITEMAP)) == 0); if (unlikely(txn->mt_owner == tid)) return MDBX_BUSY; MDBX_lockinfo *const lck = env->me_lck; @@ -6352,24 +6361,21 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, return MDBX_EINVAL; *ret = NULL; + if (unlikely((flags & ~MDBX_TXN_RW_BEGIN_FLAGS) && + (flags & ~MDBX_TXN_RO_BEGIN_FLAGS))) + return MDBX_EINVAL; + int rc = check_env(env); if (unlikely(rc != MDBX_SUCCESS)) return rc; -#if !defined(_WIN32) && !defined(_WIN64) - /* Don't check env->me_map until lock to - * avoid race with re-mapping for shrinking */ - if (unlikely(!env->me_map)) - return MDBX_EPERM; -#endif /* Windows */ - - if (unlikely(flags & ~MDBX_TXN_BEGIN_FLAGS)) - return MDBX_EINVAL; - if (unlikely(env->me_flags & MDBX_RDONLY & ~flags)) /* write txn in RDONLY env */ return MDBX_EACCESS; + if (unlikely(!env->me_map)) + return MDBX_EPERM; + flags |= env->me_flags & MDBX_WRITEMAP; if (parent) { @@ -6379,12 +6385,7 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, if (unlikely(rc != MDBX_SUCCESS)) return rc; -#if defined(_WIN32) || defined(_WIN64) - if (unlikely(!env->me_map)) - return MDBX_EPERM; -#endif /* Windows */ - - flags |= parent->mt_flags & (MDBX_TXN_BEGIN_FLAGS | MDBX_TXN_SPILLS); + flags |= parent->mt_flags & (MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_SPILLS); /* Child txns save MDBX_pgstate and use own copy of cursors */ size = env->me_maxdbs * (sizeof(MDBX_db) + sizeof(MDBX_cursor *) + 1); size += tsize = sizeof(MDBX_txn); @@ -6477,10 +6478,16 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, if (txn != env->me_txn0) mdbx_free(txn); } else { - mdbx_assert(env, - (txn->mt_flags & ~(MDBX_NOTLS | MDBX_TXN_RDONLY | - MDBX_WRITEMAP | MDBX_SHRINK_ALLOWED | - MDBX_NOMETASYNC | MDBX_SAFE_NOSYNC)) == 0); + if (flags & (MDBX_TXN_RDONLY_PREPARE - MDBX_TXN_RDONLY)) + mdbx_assert(env, txn->mt_flags == (MDBX_TXN_RDONLY | MDBX_TXN_FINISHED)); + else if (flags & MDBX_TXN_RDONLY) + mdbx_assert(env, (txn->mt_flags & + ~(MDBX_NOTLS | MDBX_TXN_RDONLY | MDBX_WRITEMAP | + /* Win32: SRWL flag */ MDBX_SHRINK_ALLOWED)) == 0); + else + mdbx_assert(env, + (txn->mt_flags & ~(MDBX_WRITEMAP | MDBX_SHRINK_ALLOWED | + MDBX_NOMETASYNC | MDBX_SAFE_NOSYNC)) == 0); txn->mt_signature = MDBX_MT_SIGNATURE; *ret = txn; mdbx_debug("begin txn %" PRIaTXN "%c %p on env %p, root page %" PRIaPGNO diff --git a/src/internals.h b/src/internals.h index 74e74fe5..07a84880 100644 --- a/src/internals.h +++ b/src/internals.h @@ -720,8 +720,9 @@ struct MDBX_txn { /* Transaction Flags */ /* mdbx_txn_begin() flags */ -#define MDBX_TXN_BEGIN_FLAGS \ - (MDBX_TXN_NOMETASYNC | MDBX_TXN_NOSYNC | MDBX_TXN_RDONLY | MDBX_TXN_TRY) +#define MDBX_TXN_RO_BEGIN_FLAGS (MDBX_TXN_RDONLY | MDBX_TXN_RDONLY_PREPARE) +#define MDBX_TXN_RW_BEGIN_FLAGS \ + (MDBX_TXN_NOMETASYNC | MDBX_TXN_NOSYNC | MDBX_TXN_TRY) /* Additional flag for mdbx_sync_locked() */ #define MDBX_SHRINK_ALLOWED UINT32_C(0x40000000) @@ -739,8 +740,9 @@ struct MDBX_txn { (MDBX_TXN_FINISHED | MDBX_TXN_ERROR | MDBX_TXN_DIRTY | MDBX_TXN_SPILLS | \ MDBX_TXN_HAS_CHILD) -#if (TXN_FLAGS & MDBX_TXN_BEGIN_FLAGS) || \ - ((MDBX_TXN_BEGIN_FLAGS | TXN_FLAGS) & MDBX_SHRINK_ALLOWED) +#if (TXN_FLAGS & (MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_RO_BEGIN_FLAGS)) || \ + ((MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_RO_BEGIN_FLAGS | TXN_FLAGS) & \ + MDBX_SHRINK_ALLOWED) #error "Oops, some flags overlapped or wrong" #endif