mdbx: refine/simplify read-latch loop inside mdbx_txn_renew0().

1. Explicitly check and handle a race/collision case with `find_oldest_reader()`.
2. Handle "recovery mode" (me_stuck_meta >= 0) by the same code as for regular latch.
3. Add bailout error message for buggy compiler and/or hardware (paranoid).
This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2022-07-08 22:29:24 +03:00
parent 434ad8edc8
commit 9421bb424d

View File

@ -8054,84 +8054,77 @@ static int mdbx_txn_renew0(MDBX_txn *txn, const unsigned flags) {
} }
/* Seek & fetch the last meta */ /* Seek & fetch the last meta */
if (likely(/* not recovery mode */ env->me_stuck_meta < 0)) { uint64_t timestamp = 0;
uint64_t timestamp = 0; unsigned loop = 0;
while (1) { while (1) {
meta_cache_clear(env); meta_cache_clear(env);
volatile const MDBX_meta *const meta = meta_prefer_last(env); volatile const MDBX_meta *const meta =
mdbx_jitter4testing(false); likely(env->me_stuck_meta < 0)
const txnid_t snap = meta_txnid(env, meta); ? /* regular */ meta_prefer_last(env)
mdbx_jitter4testing(false); : /* recovery mode */ METAPAGE(env, env->me_stuck_meta);
if (likely(r)) { mdbx_jitter4testing(false);
safe64_reset(&r->mr_txnid, false); const txnid_t target_txnid = meta_txnid(env, meta);
atomic_store32(&r->mr_snapshot_pages_used, meta->mm_geo.next, mdbx_jitter4testing(false);
mo_Relaxed);
atomic_store64(&r->mr_snapshot_pages_retired,
unaligned_peek_u64_volatile(4, meta->mm_pages_retired),
mo_Relaxed);
safe64_write(&r->mr_txnid, snap);
mdbx_jitter4testing(false);
mdbx_assert(env, r->mr_pid.weak == mdbx_getpid());
mdbx_assert(
env, r->mr_tid.weak ==
((env->me_flags & MDBX_NOTLS) ? 0 : mdbx_thread_self()));
mdbx_assert(env, r->mr_txnid.weak == snap);
atomic_store32(&env->me_lck->mti_readers_refresh_flag, true,
mo_AcquireRelease);
} else {
/* exclusive mode without lck */
}
mdbx_jitter4testing(true);
/* Snap the state from current meta-head */
txn->mt_txnid = snap;
txn->mt_geo = meta->mm_geo;
STATIC_ASSERT(CORE_DBS == 2);
txn->mt_dbs[0] = meta->mm_dbs[0];
txn->mt_dbs[1] = meta->mm_dbs[1];
txn->mt_canary = meta->mm_canary;
/* LY: Retry on a race, ITS#7970.
* The barrier is not needed here since C11-atomics are used,
* but it is reasonable paranoia to avoid compiler misoptimization
* and makes clarity for code readers. */
mdbx_compiler_barrier();
if (likely(meta == meta_prefer_last(env) &&
snap == meta_txnid(env, meta) &&
snap >= atomic_load64(&env->me_lck->mti_oldest_reader,
mo_AcquireRelease))) {
rc = meta_waittxnid(env, (const MDBX_meta *)meta, &timestamp);
mdbx_jitter4testing(false);
if (likely(rc == MDBX_SUCCESS))
break;
if (likely(rc == MDBX_RESULT_TRUE))
continue;
goto bailout;
}
}
} else {
/* r/o recovery mode */
MDBX_meta *const meta = METAPAGE(env, env->me_stuck_meta);
txn->mt_txnid = constmeta_txnid(env, meta);
txn->mt_geo = meta->mm_geo;
memcpy(txn->mt_dbs, meta->mm_dbs, CORE_DBS * sizeof(MDBX_db));
txn->mt_canary = meta->mm_canary;
if (likely(r)) { if (likely(r)) {
safe64_reset(&r->mr_txnid, false);
atomic_store32(&r->mr_snapshot_pages_used, meta->mm_geo.next, atomic_store32(&r->mr_snapshot_pages_used, meta->mm_geo.next,
mo_Relaxed); mo_Relaxed);
atomic_store64(&r->mr_snapshot_pages_retired, atomic_store64(&r->mr_snapshot_pages_retired,
unaligned_peek_u64(4, meta->mm_pages_retired), unaligned_peek_u64_volatile(4, meta->mm_pages_retired),
mo_Relaxed); mo_Relaxed);
atomic_store64(&r->mr_txnid, txn->mt_txnid, mo_Relaxed); safe64_write(&r->mr_txnid, target_txnid);
mdbx_jitter4testing(false); mdbx_jitter4testing(false);
mdbx_assert(env, r->mr_pid.weak == mdbx_getpid()); mdbx_assert(env, r->mr_pid.weak == mdbx_getpid());
mdbx_assert( mdbx_assert(
env, r->mr_tid.weak == env, r->mr_tid.weak ==
((env->me_flags & MDBX_NOTLS) ? 0 : mdbx_thread_self())); ((env->me_flags & MDBX_NOTLS) ? 0 : mdbx_thread_self()));
mdbx_assert(env, r->mr_txnid.weak == txn->mt_txnid); mdbx_assert(env,
r->mr_txnid.weak == target_txnid ||
(r->mr_txnid.weak >= SAFE64_INVALID_THRESHOLD &&
target_txnid < env->me_lck->mti_oldest_reader.weak));
atomic_store32(&env->me_lck->mti_readers_refresh_flag, true, atomic_store32(&env->me_lck->mti_readers_refresh_flag, true,
mo_Relaxed); mo_AcquireRelease);
} else {
/* exclusive mode without lck */
mdbx_assert(env, !env->me_lck_mmap.lck &&
env->me_lck == (void *)&env->x_lckless_stub);
} }
mdbx_jitter4testing(true);
/* Snap the state from current meta-head */
txn->mt_txnid = target_txnid;
txn->mt_geo = meta->mm_geo;
STATIC_ASSERT(CORE_DBS == 2);
txn->mt_dbs[0] = meta->mm_dbs[0];
txn->mt_dbs[1] = meta->mm_dbs[1];
txn->mt_canary = meta->mm_canary;
/* LY: Retry on a race, ITS#7970.
* The barrier is not needed here since C11-atomics are used,
* but it is reasonable paranoia to avoid compiler misoptimization
* and makes clarity for code readers. */
mdbx_compiler_barrier();
const txnid_t oldest =
atomic_load64(&env->me_lck->mti_oldest_reader, mo_AcquireRelease);
if (unlikely(target_txnid < oldest ||
(meta != meta_prefer_last(env) && env->me_stuck_meta < 0) ||
target_txnid != meta_txnid(env, meta))) {
if (unlikely(++loop > 42)) {
mdbx_error("bailout waiting for valid snapshot (%s)",
"metapages are too volatile");
rc = MDBX_PROBLEM;
goto bailout;
}
timestamp = 0;
continue;
}
rc = meta_waittxnid(env, meta, &timestamp);
mdbx_jitter4testing(false);
if (likely(rc == MDBX_SUCCESS))
break;
if (unlikely(rc != MDBX_RESULT_TRUE))
goto bailout;
} }
if (unlikely(txn->mt_txnid < MIN_TXNID || txn->mt_txnid > MAX_TXNID)) { if (unlikely(txn->mt_txnid < MIN_TXNID || txn->mt_txnid > MAX_TXNID)) {