22// / \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2026
33/* clang-format off */
44
5- #define MDBX_BUILD_SOURCERY d0c652016ea3e3d314181251772ed3ad35b30d8654a3e0e3f7a1dfc73f6406b4_v0_14_1_326_g72e0af44
5+ #define MDBX_BUILD_SOURCERY bceb4cffb452beb8b344b66517dbfcc12ae8e80220a05240377f9d076620e4d6_v0_14_1_342_g630b73ee
66
77#define LIBMDBX_INTERNALS
88#define MDBX_DEPRECATED
@@ -3851,26 +3851,6 @@ MDBX_MAYBE_UNUSED static inline void dxb_sanitize_tail(MDBX_env *env, MDBX_txn *
38513851}
38523852#endif /* ENABLE_MEMCHECK || __SANITIZE_ADDRESS__ */
38533853
3854- /* txn.c */
3855- #define TXN_END_NAMES \
3856- {" committed" , " pure-commit" , " abort" , " reset" , " fail-begin" , " fail-begin-nested" , " ousted" , nullptr }
3857- enum {
3858- /* txn_end operation number, for logging */
3859- TXN_END_COMMITTED /* 0 */ ,
3860- TXN_END_PURE_COMMIT /* 1 */ ,
3861- TXN_END_ABORT /* 2 */ ,
3862- TXN_END_RESET /* 3 */ ,
3863- TXN_END_FAIL_BEGIN /* 4 */ ,
3864- TXN_END_FAIL_BEGIN_NESTED /* 5 */ ,
3865- TXN_END_OUSTED /* 6 */ ,
3866- TXN_END_OPMASK = 0x07 /* mask for txn_end() operation number */ ,
3867-
3868- TXN_END_UPDATE = 0x10 /* update env state (DBIs) */ ,
3869- TXN_END_FREE = 0x20 /* free txn unless it is env.basal_txn */ ,
3870- TXN_END_SLOT = 0x40 /* release any reader slot if NOSTICKYTHREADS */ ,
3871- TXN_END_LOCK = 0x80 /* release writer mutex */
3872- };
3873-
38743854struct commit_timestamp {
38753855 uint64_t start, prep, gc, audit, write, sync, gc_cpu;
38763856};
@@ -3881,14 +3861,13 @@ MDBX_INTERNAL int txn_check_badbits_parked(const MDBX_txn *txn, int bad_bits);
38813861MDBX_INTERNAL void txn_done_cursors (MDBX_txn *txn);
38823862MDBX_INTERNAL int txn_shadow_cursors (const MDBX_txn *parent, const size_t dbi);
38833863
3884- MDBX_INTERNAL MDBX_txn *txn_alloc (const MDBX_txn_flags_t flags, MDBX_env *env);
3864+ MDBX_INTERNAL MDBX_txn *txn_alloc (const unsigned flags, MDBX_env *env);
38853865MDBX_INTERNAL int txn_abort (MDBX_txn *txn);
38863866MDBX_INTERNAL int txn_commit (MDBX_txn *txn, struct commit_timestamp *);
3887- MDBX_INTERNAL int txn_renew (MDBX_txn *txn, unsigned flags);
3888- MDBX_INTERNAL int txn_end (MDBX_txn *txn, unsigned mode);
38893867#if !(defined(_WIN32) || defined(_WIN64))
38903868MDBX_INTERNAL void txn_abort_after_resurrect (MDBX_txn *txn);
38913869#endif /* Windows */
3870+ MDBX_INTERNAL int txn_setup_primal (MDBX_txn *txn);
38923871
38933872MDBX_INTERNAL int txn_nested_create (MDBX_txn *parent, const MDBX_txn_flags_t flags);
38943873MDBX_INTERNAL int txn_nested_abort (MDBX_txn *nested);
@@ -3899,16 +3878,16 @@ MDBX_INTERNAL MDBX_txn *txn_basal_create(const size_t max_dbi);
38993878MDBX_INTERNAL void txn_basal_destroy (MDBX_txn *txn);
39003879MDBX_INTERNAL int txn_basal_start (MDBX_txn *txn, unsigned flags);
39013880MDBX_INTERNAL int txn_basal_commit (MDBX_txn *txn, struct commit_timestamp *ts);
3902- MDBX_INTERNAL int txn_basal_end (MDBX_txn *txn, unsigned mode );
3881+ MDBX_INTERNAL int txn_basal_end (MDBX_txn *txn, bool unlock );
39033882MDBX_INTERNAL int txn_basal_checkpoint (MDBX_txn *txn, MDBX_txn_flags_t weakening_durability,
39043883 struct commit_timestamp *ts);
39053884
39063885MDBX_INTERNAL int txn_ro_park (MDBX_txn *txn, bool autounpark);
39073886MDBX_INTERNAL int txn_ro_unpark (MDBX_txn *txn);
3908- MDBX_INTERNAL int txn_ro_start (MDBX_txn *txn, unsigned flags);
3909- MDBX_INTERNAL int txn_ro_end (MDBX_txn *txn, unsigned mode);
3887+ MDBX_INTERNAL int txn_ro_start (MDBX_txn *txn, bool prepare);
39103888MDBX_INTERNAL int txn_ro_clone (const MDBX_txn *const source, MDBX_txn *const clone);
39113889MDBX_INTERNAL int txn_ro_reset (MDBX_txn *txn);
3890+ MDBX_INTERNAL void txn_ro_free (MDBX_txn *txn);
39123891
39133892/* env.c */
39143893MDBX_INTERNAL int env_open (MDBX_env *env, mdbx_mode_t mode);
@@ -4499,6 +4478,9 @@ enum dbi_state {
44994478};
45004479
45014480enum txn_flags {
4481+ txn_ro_flat = MDBX_TXN_RDONLY,
4482+ txn_ro_nested = UINT32_C (0x0800 ),
4483+ txn_ro_both = txn_ro_flat | txn_ro_nested,
45024484 txn_ro_begin_flags = MDBX_TXN_RDONLY | MDBX_TXN_RDONLY_PREPARE,
45034485 txn_rw_begin_flags = MDBX_TXN_NOMETASYNC | MDBX_TXN_NOSYNC | MDBX_TXN_TRY,
45044486 txn_rw_checkpoint = MDBX_TXN_RDONLY_PREPARE & ~MDBX_TXN_RDONLY,
@@ -4828,14 +4810,14 @@ struct MDBX_env {
48284810 osal_ioring_t ioring;
48294811
48304812#if defined(_WIN32) || defined(_WIN64)
4831- osal_srwlock_t remap_guard ;
4813+ osal_srwlock_t remap_lock ;
48324814 /* Workaround for LockFileEx and WriteFile multithread bug */
48334815 CRITICAL_SECTION lck_event_cs;
48344816 CRITICAL_SECTION dxb_event_cs;
48354817 char *pathname_char; /* cache of multi-byte representation of pathname
48364818 to the DB files */
48374819#else
4838- osal_fastmutex_t remap_guard ;
4820+ osal_fastmutex_t remap_lock ;
48394821#endif
48404822
48414823 /* ------------------------------------------------- stub for lck-less mode */
@@ -5628,7 +5610,7 @@ static __always_inline int check_txn_anythread(const MDBX_txn *txn, int bad_bits
56285610 return MDBX_EPERM;
56295611
56305612 if (unlikely (txn->flags & bad_bits)) {
5631- if ((bad_bits & MDBX_TXN_RDONLY ) && unlikely (txn->flags & MDBX_TXN_RDONLY ))
5613+ if ((bad_bits & txn_ro_both ) && unlikely (txn->flags & txn_ro_both ))
56325614 return MDBX_EACCESS;
56335615 if ((bad_bits & MDBX_TXN_PARKED) == 0 )
56345616 return MDBX_BAD_TXN;
@@ -5646,7 +5628,7 @@ static __always_inline int check_txn(const MDBX_txn *txn, int bad_bits) {
56465628#if MDBX_TXN_CHECKOWNER
56475629 if (err == MDBX_SUCCESS && (txn->flags & (MDBX_NOSTICKYTHREADS | MDBX_TXN_FINISHED)) != MDBX_NOSTICKYTHREADS &&
56485630 !(bad_bits /* abort/reset/txn-break */ == 0 &&
5649- ((txn->flags & (MDBX_TXN_RDONLY | MDBX_TXN_FINISHED)) == (MDBX_TXN_RDONLY | MDBX_TXN_FINISHED))) &&
5631+ ((txn->flags & (txn_ro_flat | MDBX_TXN_FINISHED)) == (txn_ro_flat | MDBX_TXN_FINISHED))) &&
56505632 unlikely (txn->owner != osal_thread_self ()))
56515633 err = txn->owner ? MDBX_THREAD_MISMATCH : MDBX_BAD_TXN;
56525634#endif /* MDBX_TXN_CHECKOWNER */
@@ -5655,14 +5637,14 @@ static __always_inline int check_txn(const MDBX_txn *txn, int bad_bits) {
56555637}
56565638
56575639static inline int check_txn_rw (const MDBX_txn *txn, int bad_bits) {
5658- return check_txn (txn, (bad_bits | MDBX_TXN_RDONLY ) & ~MDBX_TXN_PARKED);
5640+ return check_txn (txn, (bad_bits | txn_ro_both ) & ~MDBX_TXN_PARKED);
56595641}
56605642
56615643MDBX_NOTHROW_CONST_FUNCTION static inline txnid_t txn_basis_snapshot (const MDBX_txn *txn) {
5662- STATIC_ASSERT (((MDBX_TXN_RDONLY >> ((xMDBX_TXNID_STEP == 2 ) ? 16 : 17 )) & xMDBX_TXNID_STEP) == xMDBX_TXNID_STEP);
5644+ STATIC_ASSERT (((txn_ro_flat >> ((xMDBX_TXNID_STEP == 2 ) ? 16 : 17 )) & xMDBX_TXNID_STEP) == xMDBX_TXNID_STEP);
56635645 const txnid_t committed_txnid =
56645646 txn->txnid - xMDBX_TXNID_STEP + ((txn->flags >> ((xMDBX_TXNID_STEP == 2 ) ? 16 : 17 )) & xMDBX_TXNID_STEP);
5665- tASSERT (txn, committed_txnid == ((txn->flags & MDBX_TXN_RDONLY ) ? txn->txnid : txn->txnid - xMDBX_TXNID_STEP));
5647+ tASSERT (txn, committed_txnid == ((txn->flags & txn_ro_flat ) ? txn->txnid : txn->txnid - xMDBX_TXNID_STEP));
56665648 return committed_txnid;
56675649}
56685650
@@ -6018,7 +6000,7 @@ static inline int cursor_check_ro(const MDBX_cursor *mc) { return cursor_check(m
60186000
60196001/* для записи данных. */
60206002static inline int cursor_check_rw (const MDBX_cursor *mc) {
6021- return cursor_check (mc, (MDBX_TXN_BLOCKED - MDBX_TXN_PARKED) | MDBX_TXN_RDONLY );
6003+ return cursor_check (mc, (MDBX_TXN_BLOCKED - MDBX_TXN_PARKED) | txn_ro_both );
60226004}
60236005
60246006MDBX_INTERNAL MDBX_cursor *cursor_eot (MDBX_cursor *cursor, MDBX_txn *txn);
@@ -6142,7 +6124,7 @@ MDBX_INTERNAL dpl_t *dpl_reserve(MDBX_txn *txn, size_t size);
61426124MDBX_INTERNAL __noinline dpl_t *dpl_sort_slowpath (const MDBX_txn *txn);
61436125
61446126static inline dpl_t *dpl_sort (const MDBX_txn *txn) {
6145- tASSERT (txn, (txn->flags & MDBX_TXN_RDONLY ) == 0 );
6127+ tASSERT (txn, (txn->flags & txn_ro_both ) == 0 );
61466128 tASSERT (txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
61476129
61486130 dpl_t *dl = txn->wr .dirtylist ;
@@ -6168,7 +6150,7 @@ MDBX_NOTHROW_PURE_FUNCTION static inline pgno_t dpl_endpgno(const dpl_t *dl, siz
61686150}
61696151
61706152MDBX_NOTHROW_PURE_FUNCTION static inline bool dpl_intersect (const MDBX_txn *txn, pgno_t pgno, size_t npages) {
6171- tASSERT (txn, (txn->flags & MDBX_TXN_RDONLY ) == 0 );
6153+ tASSERT (txn, (txn->flags & txn_ro_both ) == 0 );
61726154 tASSERT (txn, (txn->flags & MDBX_WRITEMAP) == 0 || MDBX_AVOID_MSYNC);
61736155
61746156 dpl_t *dl = txn->wr .dirtylist ;
@@ -6214,7 +6196,7 @@ MDBX_INTERNAL int __must_check_result dpl_append(MDBX_txn *txn, pgno_t pgno, pag
62146196MDBX_MAYBE_UNUSED MDBX_INTERNAL bool dpl_check (MDBX_txn *txn);
62156197
62166198MDBX_NOTHROW_PURE_FUNCTION static inline uint32_t dpl_age (const MDBX_txn *txn, size_t i) {
6217- tASSERT (txn, (txn->flags & (MDBX_TXN_RDONLY | MDBX_WRITEMAP)) == 0 );
6199+ tASSERT (txn, (txn->flags & (txn_ro_both | MDBX_WRITEMAP)) == 0 );
62186200 const dpl_t *dl = txn->wr .dirtylist ;
62196201 assert ((intptr_t )i > 0 && i <= dl->length );
62206202 size_t *const ptr = ptr_disp (dl->items [i].ptr , -(ptrdiff_t )sizeof (size_t ));
@@ -6578,7 +6560,7 @@ static inline bool spill_intersect(const MDBX_txn *txn, pgno_t pgno, size_t npag
65786560}
65796561
65806562static inline int txn_spill (MDBX_txn *const txn, MDBX_cursor *const m0, const size_t need) {
6581- tASSERT (txn, (txn->flags & MDBX_TXN_RDONLY ) == 0 );
6563+ tASSERT (txn, (txn->flags & txn_ro_both ) == 0 );
65826564 tASSERT (txn, !m0 || cursor_is_tracked (m0));
65836565
65846566 const intptr_t wanna_spill_entries = txn->wr .dirtylist ? (need - txn->wr .dirtyroom - txn->wr .loose_count ) : 0 ;
@@ -6704,7 +6686,7 @@ MDBX_INTERNAL int page_retire_ex(MDBX_cursor *mc, const pgno_t pgno, page_t *mp
67046686static inline int page_retire (MDBX_cursor *mc, page_t *mp) { return page_retire_ex (mc, mp->pgno , mp, mp->flags ); }
67056687
67066688static inline void page_wash (MDBX_txn *txn, size_t di, page_t *const mp, const size_t npages) {
6707- tASSERT (txn, (txn->flags & MDBX_TXN_RDONLY ) == 0 );
6689+ tASSERT (txn, (txn->flags & txn_ro_both ) == 0 );
67086690 mp->txnid = INVALID_TXNID;
67096691 mp->flags = P_BAD;
67106692
@@ -8498,7 +8480,7 @@ __cold MDBX_env_flags_t env::operate_parameters::make_flags(bool accede, bool us
84988480 flags |= MDBX_VALIDATION;
84998481
85008482 if (mode != readonly) {
8501- if (options.nested_write_transactions )
8483+ if (options.nested_transactions )
85028484 flags &= ~MDBX_WRITEMAP;
85038485 if (reclaiming.coalesce )
85048486 flags |= MDBX_COALESCE;
@@ -8545,7 +8527,7 @@ env::reclaiming_options::reclaiming_options(MDBX_env_flags_t flags) noexcept
85458527
85468528env::operate_options::operate_options (MDBX_env_flags_t flags) noexcept
85478529 : no_sticky_threads(((flags & (MDBX_NOSTICKYTHREADS | MDBX_EXCLUSIVE)) == MDBX_NOSTICKYTHREADS) ? true : false ),
8548- nested_write_transactions ((flags & (MDBX_WRITEMAP | MDBX_RDONLY)) ? false : true),
8530+ nested_transactions ((flags & (MDBX_WRITEMAP | MDBX_RDONLY)) ? false : true),
85498531 exclusive((flags & MDBX_EXCLUSIVE) ? true : false), disable_readahead((flags & MDBX_NORDAHEAD) ? true : false),
85508532 disable_clear_memory((flags & MDBX_NOMEMINIT) ? true : false) {}
85518533
@@ -8667,7 +8649,7 @@ __cold env_managed::env_managed(const char *pathname, const operate_parameters &
86678649 setup (op.max_maps , op.max_readers );
86688650 error::success_or_throw (::mdbx_env_open (handle_, pathname, op.make_flags (accede), 0 ));
86698651
8670- if (op.options .nested_write_transactions && !get_options ().nested_write_transactions )
8652+ if (op.options .nested_transactions && !get_options ().nested_transactions )
86718653 MDBX_CXX20_UNLIKELY error::throw_exception (MDBX_INCOMPATIBLE);
86728654}
86738655
@@ -8679,7 +8661,7 @@ __cold env_managed::env_managed(const char *pathname, const env_managed::create_
86798661 error::success_or_throw (
86808662 ::mdbx_env_open (handle_, pathname, op.make_flags(accede, cp.use_subdirectory), cp.file_mode_bits));
86818663
8682- if (op.options .nested_write_transactions && !get_options ().nested_write_transactions )
8664+ if (op.options .nested_transactions && !get_options ().nested_transactions )
86838665 MDBX_CXX20_UNLIKELY error::throw_exception (MDBX_INCOMPATIBLE);
86848666}
86858667
@@ -8696,7 +8678,7 @@ __cold env_managed::env_managed(const wchar_t *pathname, const operate_parameter
86968678 setup (op.max_maps , op.max_readers );
86978679 error::success_or_throw (::mdbx_env_openW (handle_, pathname, op.make_flags (accede), 0 ));
86988680
8699- if (op.options .nested_write_transactions && !get_options ().nested_write_transactions )
8681+ if (op.options .nested_transactions && !get_options ().nested_transactions )
87008682 MDBX_CXX20_UNLIKELY error::throw_exception (MDBX_INCOMPATIBLE);
87018683}
87028684
@@ -8708,7 +8690,7 @@ __cold env_managed::env_managed(const wchar_t *pathname, const env_managed::crea
87088690 error::success_or_throw (
87098691 ::mdbx_env_openW (handle_, pathname, op.make_flags(accede, cp.use_subdirectory), cp.file_mode_bits));
87108692
8711- if (op.options .nested_write_transactions && !get_options ().nested_write_transactions )
8693+ if (op.options .nested_transactions && !get_options ().nested_transactions )
87128694 MDBX_CXX20_UNLIKELY error::throw_exception (MDBX_INCOMPATIBLE);
87138695}
87148696
@@ -8731,10 +8713,13 @@ __cold env_managed::env_managed(const MDBX_STD_FILESYSTEM_PATH &pathname, const
87318713
87328714// ------------------------------------------------------------------------------
87338715
8734- txn_managed txn::start_nested () {
8716+ txn_managed txn::start_nested () { return start_nested (false ); }
8717+
8718+ txn_managed txn::start_nested (bool readonly) {
87358719 MDBX_txn *nested;
87368720 error::throw_on_nullptr (handle_, MDBX_BAD_TXN);
8737- error::success_or_throw (::mdbx_txn_begin (mdbx_txn_env (handle_), handle_, MDBX_TXN_READWRITE, &nested));
8721+ error::success_or_throw (
8722+ ::mdbx_txn_begin (mdbx_txn_env(handle_), handle_, readonly ? MDBX_TXN_RDONLY : MDBX_TXN_READWRITE, &nested));
87388723 assert (nested != nullptr );
87398724 return txn_managed (nested);
87408725}
@@ -8768,6 +8753,16 @@ void txn_managed::commit(commit_latency *latency) {
87688753 MDBX_CXX20_UNLIKELY err.throw_exception ();
87698754}
87708755
8756+ bool txn_managed::checkpoint (commit_latency *latency) {
8757+ const error err = static_cast <MDBX_error_t>(::mdbx_txn_checkpoint (handle_, MDBX_TXN_NOWEAKING, latency));
8758+ if (MDBX_UNLIKELY (err.is_failure ())) {
8759+ if (err.code () != MDBX_THREAD_MISMATCH && err.code () != MDBX_EINVAL)
8760+ handle_ = nullptr ;
8761+ MDBX_CXX20_UNLIKELY err.throw_exception ();
8762+ }
8763+ return err.is_result_true ();
8764+ }
8765+
87718766void txn_managed::commit_embark_read () {
87728767 auto env = handle_->env ;
87738768 commit ();
@@ -9037,8 +9032,8 @@ __cold ::std::ostream &operator<<(::std::ostream &out, const env::operate_option
90379032 out << delimiter << " no_sticky_threads" ;
90389033 delimiter = comma;
90399034 }
9040- if (it.nested_write_transactions ) {
9041- out << delimiter << " nested_write_transactions " ;
9035+ if (it.nested_transactions ) {
9036+ out << delimiter << " nested_transactions " ;
90429037 delimiter = comma;
90439038 }
90449039 if (it.exclusive ) {
0 commit comments