mdbx: add MDBX_TXN_RDONLY_PREPARE.

Change-Id: I95647d1679b69d1e97514a45f20d7373174244d5
This commit is contained in:
Leonid Yuriev 2020-08-03 12:56:57 +03:00
parent 1e7a1da14e
commit 87de3fc25f
3 changed files with 47 additions and 30 deletions

8
mdbx.h
View File

@ -1010,6 +1010,14 @@ enum MDBX_txn_flags_t {
* block each other and a write transactions. */ * block each other and a write transactions. */
MDBX_TXN_RDONLY = MDBX_RDONLY, 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. */ /** Do not block when starting a write transaction. */
MDBX_TXN_TRY = UINT32_C(0x10000000), MDBX_TXN_TRY = UINT32_C(0x10000000),

View File

@ -5909,8 +5909,6 @@ typedef struct {
static bind_rslot_result bind_rslot(MDBX_env *env, const uintptr_t tid) { static bind_rslot_result bind_rslot(MDBX_env *env, const uintptr_t tid) {
mdbx_assert(env, env->me_lck); 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_magic_and_version == MDBX_LOCK_MAGIC);
mdbx_assert(env, env->me_lck->mti_os_and_format == MDBX_LOCK_FORMAT); 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(). */ /* 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; MDBX_env *env = txn->mt_env;
int rc; 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 == STATIC_ASSERT(offsetof(MDBX_lockinfo, mti_readers) % MDBX_CACHELINE_SIZE ==
0); 0);
mdbx_assert(env, (flags & ~(MDBX_TXN_BEGIN_FLAGS | MDBX_TXN_SPILLS |
MDBX_WRITEMAP)) == 0);
const uintptr_t tid = mdbx_thread_self(); const uintptr_t tid = mdbx_thread_self();
if (flags & MDBX_TXN_RDONLY) { if (flags & MDBX_TXN_RDONLY) {
mdbx_assert(env, (flags & ~(MDBX_TXN_RO_BEGIN_FLAGS | MDBX_WRITEMAP)) == 0);
txn->mt_flags = txn->mt_flags =
MDBX_TXN_RDONLY | (env->me_flags & (MDBX_NOTLS | MDBX_WRITEMAP)); MDBX_TXN_RDONLY | (env->me_flags & (MDBX_NOTLS | MDBX_WRITEMAP));
MDBX_reader *r = txn->to.reader; MDBX_reader *r = txn->to.reader;
@ -6096,6 +6093,17 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) {
return brs.err; return brs.err;
r = brs.rslot; 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 */ /* Seek & fetch the last meta */
while (1) { while (1) {
@ -6142,12 +6150,13 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) {
goto bailout; goto bailout;
} }
mdbx_assert(env, txn->mt_txnid >= *env->me_oldest); mdbx_assert(env, txn->mt_txnid >= *env->me_oldest);
txn->to.reader = r;
txn->mt_dbxs = env->me_dbxs; /* mostly static anyway */ txn->mt_dbxs = env->me_dbxs; /* mostly static anyway */
mdbx_ensure(env, txn->mt_txnid >= mdbx_ensure(env, txn->mt_txnid >=
/* paranoia is appropriate here */ *env->me_oldest); /* paranoia is appropriate here */ *env->me_oldest);
txn->mt_numdbs = env->me_numdbs; txn->mt_numdbs = env->me_numdbs;
} else { } else {
mdbx_assert(env, (flags & ~(MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_SPILLS |
MDBX_WRITEMAP)) == 0);
if (unlikely(txn->mt_owner == tid)) if (unlikely(txn->mt_owner == tid))
return MDBX_BUSY; return MDBX_BUSY;
MDBX_lockinfo *const lck = env->me_lck; 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; return MDBX_EINVAL;
*ret = NULL; *ret = NULL;
if (unlikely((flags & ~MDBX_TXN_RW_BEGIN_FLAGS) &&
(flags & ~MDBX_TXN_RO_BEGIN_FLAGS)))
return MDBX_EINVAL;
int rc = check_env(env); int rc = check_env(env);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; 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 & if (unlikely(env->me_flags & MDBX_RDONLY &
~flags)) /* write txn in RDONLY env */ ~flags)) /* write txn in RDONLY env */
return MDBX_EACCESS; return MDBX_EACCESS;
if (unlikely(!env->me_map))
return MDBX_EPERM;
flags |= env->me_flags & MDBX_WRITEMAP; flags |= env->me_flags & MDBX_WRITEMAP;
if (parent) { if (parent) {
@ -6379,12 +6385,7 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags,
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(rc != MDBX_SUCCESS))
return rc; return rc;
#if defined(_WIN32) || defined(_WIN64) flags |= parent->mt_flags & (MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_SPILLS);
if (unlikely(!env->me_map))
return MDBX_EPERM;
#endif /* Windows */
flags |= parent->mt_flags & (MDBX_TXN_BEGIN_FLAGS | MDBX_TXN_SPILLS);
/* Child txns save MDBX_pgstate and use own copy of cursors */ /* Child txns save MDBX_pgstate and use own copy of cursors */
size = env->me_maxdbs * (sizeof(MDBX_db) + sizeof(MDBX_cursor *) + 1); size = env->me_maxdbs * (sizeof(MDBX_db) + sizeof(MDBX_cursor *) + 1);
size += tsize = sizeof(MDBX_txn); 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) if (txn != env->me_txn0)
mdbx_free(txn); mdbx_free(txn);
} else { } else {
mdbx_assert(env, if (flags & (MDBX_TXN_RDONLY_PREPARE - MDBX_TXN_RDONLY))
(txn->mt_flags & ~(MDBX_NOTLS | MDBX_TXN_RDONLY | mdbx_assert(env, txn->mt_flags == (MDBX_TXN_RDONLY | MDBX_TXN_FINISHED));
MDBX_WRITEMAP | MDBX_SHRINK_ALLOWED | else if (flags & MDBX_TXN_RDONLY)
MDBX_NOMETASYNC | MDBX_SAFE_NOSYNC)) == 0); 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; txn->mt_signature = MDBX_MT_SIGNATURE;
*ret = txn; *ret = txn;
mdbx_debug("begin txn %" PRIaTXN "%c %p on env %p, root page %" PRIaPGNO mdbx_debug("begin txn %" PRIaTXN "%c %p on env %p, root page %" PRIaPGNO

View File

@ -720,8 +720,9 @@ struct MDBX_txn {
/* Transaction Flags */ /* Transaction Flags */
/* mdbx_txn_begin() flags */ /* mdbx_txn_begin() flags */
#define MDBX_TXN_BEGIN_FLAGS \ #define MDBX_TXN_RO_BEGIN_FLAGS (MDBX_TXN_RDONLY | MDBX_TXN_RDONLY_PREPARE)
(MDBX_TXN_NOMETASYNC | MDBX_TXN_NOSYNC | MDBX_TXN_RDONLY | MDBX_TXN_TRY) #define MDBX_TXN_RW_BEGIN_FLAGS \
(MDBX_TXN_NOMETASYNC | MDBX_TXN_NOSYNC | MDBX_TXN_TRY)
/* Additional flag for mdbx_sync_locked() */ /* Additional flag for mdbx_sync_locked() */
#define MDBX_SHRINK_ALLOWED UINT32_C(0x40000000) #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_FINISHED | MDBX_TXN_ERROR | MDBX_TXN_DIRTY | MDBX_TXN_SPILLS | \
MDBX_TXN_HAS_CHILD) MDBX_TXN_HAS_CHILD)
#if (TXN_FLAGS & MDBX_TXN_BEGIN_FLAGS) || \ #if (TXN_FLAGS & (MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_RO_BEGIN_FLAGS)) || \
((MDBX_TXN_BEGIN_FLAGS | TXN_FLAGS) & MDBX_SHRINK_ALLOWED) ((MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_RO_BEGIN_FLAGS | TXN_FLAGS) & \
MDBX_SHRINK_ALLOWED)
#error "Oops, some flags overlapped or wrong" #error "Oops, some flags overlapped or wrong"
#endif #endif