diff --git a/src/proto.h b/src/proto.h index 989bf59c..0216f36b 100644 --- a/src/proto.h +++ b/src/proto.h @@ -39,16 +39,6 @@ static inline void dxb_sanitize_tail(MDBX_env *env, MDBX_txn *txn) { #endif /* ENABLE_MEMCHECK || __SANITIZE_ADDRESS__ */ /* txn.c */ -MDBX_INTERNAL bool txn_refund(MDBX_txn *txn); -MDBX_INTERNAL txnid_t txn_snapshot_oldest(const MDBX_txn *const txn); -MDBX_INTERNAL int txn_abort(MDBX_txn *txn); -MDBX_INTERNAL int txn_renew(MDBX_txn *txn, unsigned flags); -MDBX_INTERNAL int txn_ro_park(MDBX_txn *txn, bool autounpark); -MDBX_INTERNAL int txn_ro_unpark(MDBX_txn *txn); -MDBX_INTERNAL int txn_check_badbits_parked(const MDBX_txn *txn, int bad_bits); -MDBX_INTERNAL void txn_done_cursors(MDBX_txn *txn); -MDBX_INTERNAL int txn_shadow_cursors(const MDBX_txn *parent, const size_t dbi); - #define TXN_END_NAMES \ {"committed", "pure-commit", "abort", "reset", "fail-begin", "fail-begin-nested", "ousted", nullptr} enum { @@ -66,21 +56,36 @@ enum { TXN_END_FREE = 0x20 /* free txn unless it is env.basal_txn */, TXN_END_SLOT = 0x40 /* release any reader slot if NOSTICKYTHREADS */ }; -MDBX_INTERNAL int txn_end(MDBX_txn *txn, unsigned mode); -MDBX_INTERNAL MDBX_txn *txn_alloc(const MDBX_txn_flags_t flags, MDBX_env *env); -MDBX_INTERNAL MDBX_txn *txn_basal_create(const size_t max_dbi); -MDBX_INTERNAL void txn_basal_destroy(MDBX_txn *txn); -MDBX_INTERNAL int txn_nested_create(MDBX_txn *parent, const MDBX_txn_flags_t flags); -MDBX_INTERNAL void txn_nested_abort(MDBX_txn *nested); struct commit_timestamp { uint64_t start, prep, gc, audit, write, sync, gc_cpu; }; + +MDBX_INTERNAL bool txn_refund(MDBX_txn *txn); +MDBX_INTERNAL txnid_t txn_snapshot_oldest(const MDBX_txn *const txn); +MDBX_INTERNAL int txn_check_badbits_parked(const MDBX_txn *txn, int bad_bits); +MDBX_INTERNAL void txn_done_cursors(MDBX_txn *txn); +MDBX_INTERNAL int txn_shadow_cursors(const MDBX_txn *parent, const size_t dbi); + +MDBX_INTERNAL MDBX_txn *txn_alloc(const MDBX_txn_flags_t flags, MDBX_env *env); +MDBX_INTERNAL int txn_abort(MDBX_txn *txn); +MDBX_INTERNAL int txn_renew(MDBX_txn *txn, unsigned flags); +MDBX_INTERNAL int txn_end(MDBX_txn *txn, unsigned mode); + +MDBX_INTERNAL int txn_nested_create(MDBX_txn *parent, const MDBX_txn_flags_t flags); +MDBX_INTERNAL void txn_nested_abort(MDBX_txn *nested); MDBX_INTERNAL int txn_nested_join(MDBX_txn *txn, struct commit_timestamp *ts); + +MDBX_INTERNAL MDBX_txn *txn_basal_create(const size_t max_dbi); +MDBX_INTERNAL void txn_basal_destroy(MDBX_txn *txn); +MDBX_INTERNAL int txn_basal_start(MDBX_txn *txn, unsigned flags); MDBX_INTERNAL int txn_basal_commit(MDBX_txn *txn, struct commit_timestamp *ts); MDBX_INTERNAL int txn_basal_end(MDBX_txn *txn, unsigned mode); -MDBX_INTERNAL int txn_ro_end(MDBX_txn *txn, unsigned mode); + +MDBX_INTERNAL int txn_ro_park(MDBX_txn *txn, bool autounpark); +MDBX_INTERNAL int txn_ro_unpark(MDBX_txn *txn); MDBX_INTERNAL int txn_ro_start(MDBX_txn *txn, unsigned flags); +MDBX_INTERNAL int txn_ro_end(MDBX_txn *txn, unsigned mode); /* env.c */ MDBX_INTERNAL int env_open(MDBX_env *env, mdbx_mode_t mode); diff --git a/src/txn-basal.c b/src/txn-basal.c index 733fcf22..f12824d3 100644 --- a/src/txn-basal.c +++ b/src/txn-basal.c @@ -89,6 +89,47 @@ __cold void txn_basal_destroy(MDBX_txn *txn) { osal_free(txn); } +int txn_basal_start(MDBX_txn *txn, unsigned flags) { + MDBX_env *const env = txn->env; + + txn->wr.troika = meta_tap(env); + const meta_ptr_t head = meta_recent(env, &txn->wr.troika); + uint64_t timestamp = 0; + while ("workaround for https://libmdbx.dqdkfa.ru/dead-github/issues/269") { + int err = coherency_fetch_head(txn, head, ×tamp); + if (likely(err == MDBX_SUCCESS)) + break; + if (unlikely(err != MDBX_RESULT_TRUE)) + return err; + } + eASSERT(env, meta_txnid(head.ptr_v) == txn->txnid); + txn->txnid = safe64_txnid_next(txn->txnid); + if (unlikely(txn->txnid > MAX_TXNID)) { + ERROR("txnid overflow, raise %d", MDBX_TXN_FULL); + return MDBX_TXN_FULL; + } + + tASSERT(txn, txn->dbs[FREE_DBI].flags == MDBX_INTEGERKEY); + tASSERT(txn, check_table_flags(txn->dbs[MAIN_DBI].flags)); + txn->flags = flags; + txn->nested = nullptr; + txn->wr.loose_pages = nullptr; + txn->wr.loose_count = 0; +#if MDBX_ENABLE_REFUND + txn->wr.loose_refund_wl = 0; +#endif /* MDBX_ENABLE_REFUND */ + MDBX_PNL_SETSIZE(txn->wr.retired_pages, 0); + txn->wr.spilled.list = nullptr; + txn->wr.spilled.least_removed = 0; + txn->wr.gc.time_acc = 0; + txn->wr.gc.last_reclaimed = 0; + if (txn->wr.gc.retxl) + MDBX_PNL_SETSIZE(txn->wr.gc.retxl, 0); + env->txn = txn; + + return MDBX_SUCCESS; +} + int txn_basal_end(MDBX_txn *txn, unsigned mode) { MDBX_env *const env = txn->env; tASSERT(txn, (txn->flags & (MDBX_TXN_FINISHED | txn_may_have_cursors)) == 0 && txn->owner); diff --git a/src/txn.c b/src/txn.c index 9d0600a4..005b4c4b 100644 --- a/src/txn.c +++ b/src/txn.c @@ -70,6 +70,19 @@ int txn_abort(MDBX_txn *txn) { return txn_end(txn, TXN_END_ABORT | TXN_END_SLOT | TXN_END_FREE); } +static bool txn_check_overlapped(lck_t *const lck, const uint32_t pid, const uintptr_t tid) { + const size_t snap_nreaders = atomic_load32(&lck->rdt_length, mo_AcquireRelease); + for (size_t i = 0; i < snap_nreaders; ++i) { + if (atomic_load32(&lck->rdt[i].pid, mo_Relaxed) == pid && + unlikely(atomic_load64(&lck->rdt[i].tid, mo_Relaxed) == tid)) { + const txnid_t txnid = safe64_read(&lck->rdt[i].txnid); + if (txnid >= MIN_TXNID && txnid <= MAX_TXNID) + return true; + } + } + return false; +} + int txn_renew(MDBX_txn *txn, unsigned flags) { MDBX_env *const env = txn->env; int rc; @@ -90,17 +103,9 @@ int txn_renew(MDBX_txn *txn, unsigned flags) { /* not recovery mode */ env->stuck_meta >= 0)) return MDBX_BUSY; lck_t *const lck = env->lck_mmap.lck; - if (lck && (env->flags & MDBX_NOSTICKYTHREADS) == 0 && (globals.runtime_flags & MDBX_DBG_LEGACY_OVERLAP) == 0) { - const size_t snap_nreaders = atomic_load32(&lck->rdt_length, mo_AcquireRelease); - for (size_t i = 0; i < snap_nreaders; ++i) { - if (atomic_load32(&lck->rdt[i].pid, mo_Relaxed) == env->pid && - unlikely(atomic_load64(&lck->rdt[i].tid, mo_Relaxed) == tid)) { - const txnid_t txnid = safe64_read(&lck->rdt[i].txnid); - if (txnid >= MIN_TXNID && txnid <= MAX_TXNID) - return MDBX_TXN_OVERLAPPING; - } - } - } + if (lck && !(env->flags & MDBX_NOSTICKYTHREADS) && !(globals.runtime_flags & MDBX_DBG_LEGACY_OVERLAP) && + txn_check_overlapped(lck, env->pid, tid)) + return MDBX_TXN_OVERLAPPING; /* Not yet touching txn == env->basal_txn, it may be active */ jitter4testing(false); @@ -118,41 +123,9 @@ int txn_renew(MDBX_txn *txn, unsigned flags) { } #endif /* Windows */ - txn->wr.troika = meta_tap(env); - const meta_ptr_t head = meta_recent(env, &txn->wr.troika); - uint64_t timestamp = 0; - while ("workaround for https://libmdbx.dqdkfa.ru/dead-github/issues/269") { - rc = coherency_fetch_head(txn, head, ×tamp); - if (likely(rc == MDBX_SUCCESS)) - break; - if (unlikely(rc != MDBX_RESULT_TRUE)) - goto bailout; - } - eASSERT(env, meta_txnid(head.ptr_v) == txn->txnid); - txn->txnid = safe64_txnid_next(txn->txnid); - if (unlikely(txn->txnid > MAX_TXNID)) { - rc = MDBX_TXN_FULL; - ERROR("txnid overflow, raise %d", rc); + rc = txn_basal_start(txn, flags); + if (unlikely(rc != MDBX_SUCCESS)) goto bailout; - } - - tASSERT(txn, txn->dbs[FREE_DBI].flags == MDBX_INTEGERKEY); - tASSERT(txn, check_table_flags(txn->dbs[MAIN_DBI].flags)); - txn->flags = flags; - txn->nested = nullptr; - txn->wr.loose_pages = nullptr; - txn->wr.loose_count = 0; -#if MDBX_ENABLE_REFUND - txn->wr.loose_refund_wl = 0; -#endif /* MDBX_ENABLE_REFUND */ - MDBX_PNL_SETSIZE(txn->wr.retired_pages, 0); - txn->wr.spilled.list = nullptr; - txn->wr.spilled.least_removed = 0; - txn->wr.gc.time_acc = 0; - txn->wr.gc.last_reclaimed = 0; - if (txn->wr.gc.retxl) - MDBX_PNL_SETSIZE(txn->wr.gc.retxl, 0); - env->txn = txn; } txn->front_txnid = txn->txnid + ((flags & (MDBX_WRITEMAP | MDBX_RDONLY)) == 0);