diff --git a/mdbx/mdbx.c b/mdbx/mdbx.c index 4247d38..ef89892 100644 --- a/mdbx/mdbx.c +++ b/mdbx/mdbx.c @@ -3,7 +3,7 @@ #define xMDBX_ALLOY 1 /* alloyed build */ -#define MDBX_BUILD_SOURCERY 4ef6bfc2012bedf4af0bcd644ec87ace207f395c5d5e103573649032ec2cb6e8_v0_13_1_0_g5fc7a6b1 +#define MDBX_BUILD_SOURCERY 7a087912ed373fb7f849bc0a5b72a7d3cbdca30796a95e27dc05c7118e47dde9_v0_13_1_56_g10a93f4b #define LIBMDBX_INTERNALS @@ -19,6 +19,10 @@ #if (defined(MDBX_DEBUG) && MDBX_DEBUG > 0) || \ (defined(MDBX_FORCE_ASSERTIONS) && MDBX_FORCE_ASSERTIONS) #undef NDEBUG +#ifndef MDBX_DEBUG +/* Чтобы избежать включения отладки только из-за включения assert-проверок */ +#define MDBX_DEBUG 0 +#endif #endif /*----------------------------------------------------------------------------*/ @@ -1804,11 +1808,7 @@ osal_bswap32(uint32_t v) { /** Enables chunking long list of retired pages during huge transactions commit * to avoid use sequences of pages. */ #ifndef MDBX_ENABLE_BIGFOOT -#if MDBX_WORDBITS >= 64 || defined(DOXYGEN) #define MDBX_ENABLE_BIGFOOT 1 -#else -#define MDBX_ENABLE_BIGFOOT 0 -#endif #elif !(MDBX_ENABLE_BIGFOOT == 0 || MDBX_ENABLE_BIGFOOT == 1) #error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1 #endif /* MDBX_ENABLE_BIGFOOT */ @@ -6224,19 +6224,11 @@ MDBX_INTERNAL void dpl_release_shadows(MDBX_txn *txn); -#ifndef MDBX_ENABLE_GC_EXPERIMENTAL -#define MDBX_ENABLE_GC_EXPERIMENTAL 0 -#elif !(MDBX_ENABLE_GC_EXPERIMENTAL == 0 || MDBX_ENABLE_GC_EXPERIMENTAL == 1) -#error MDBX_ENABLE_GC_EXPERIMENTAL must be defined as 0 or 1 -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ - typedef struct gc_update_context { unsigned loop; pgno_t prev_first_unallocated; bool dense; -#if MDBX_ENABLE_GC_EXPERIMENTAL - intptr_t reserve_adj; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ + size_t reserve_adj; size_t retired_stored; size_t amount, reserved, cleaned_slot, reused_slot, fill_idx; txnid_t cleaned_id, rid; @@ -6251,7 +6243,7 @@ typedef struct gc_update_context { static inline int gc_update_init(MDBX_txn *txn, gcu_t *ctx) { memset(ctx, 0, offsetof(gcu_t, cursor)); - ctx->dense = txn->txnid < MIN_TXNID; + ctx->dense = txn->txnid <= MIN_TXNID; #if MDBX_ENABLE_BIGFOOT ctx->bigfoot = txn->txnid; #endif /* MDBX_ENABLE_BIGFOOT */ @@ -16442,12 +16434,19 @@ __hot int cursor_put_checklen(MDBX_cursor *mc, const MDBX_val *key, if (mc->tree->flags & MDBX_INTEGERDUP) { if (data->iov_len == 8) { if (unlikely(7 & (uintptr_t)data->iov_base)) { - if (unlikely(flags & MDBX_MULTIPLE)) - return MDBX_BAD_VALSIZE; - /* copy instead of return error to avoid break compatibility */ - aligned_data.iov_base = bcopy_8(&aligned_databytes, data->iov_base); - aligned_data.iov_len = data->iov_len; - data = &aligned_data; + if (unlikely(flags & MDBX_MULTIPLE)) { + /* LY: использование alignof(uint64_t) тут не подходил из-за ошибок + * MSVC и некоторых других компиляторов, когда для элементов + * массивов/векторов обеспечивает выравнивание только на 4-х байтовых + * границу и одновременно alignof(uint64_t) == 8. */ + if (MDBX_WORDBITS > 32 || (3 & (uintptr_t)data->iov_base) != 0) + return MDBX_BAD_VALSIZE; + } else { + /* copy instead of return error to avoid break compatibility */ + aligned_data.iov_base = bcopy_8(&aligned_databytes, data->iov_base); + aligned_data.iov_len = data->iov_len; + data = &aligned_data; + } } } else if (data->iov_len == 4) { if (unlikely(3 & (uintptr_t)data->iov_base)) { @@ -17049,9 +17048,16 @@ __hot int cursor_ops(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, rc = cursor_seek(mc, key, data, MDBX_SET).err; if (unlikely(rc != MDBX_SUCCESS)) return rc; + } else { + if (unlikely(is_eof(mc) || !inner_filled(mc))) + return MDBX_ENODATA; + cASSERT(mc, is_filled(mc)); + if (key) { + const page_t *mp = mc->pg[mc->top]; + const node_t *node = page_node(mp, mc->ki[mc->top]); + *key = get_key(node); + } } - if (unlikely(is_eof(mc) || !inner_filled(mc))) - return MDBX_ENODATA; goto fetch_multiple; case MDBX_NEXT_MULTIPLE: @@ -17064,7 +17070,7 @@ __hot int cursor_ops(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, return rc; else { fetch_multiple: - cASSERT(mc, is_filled(mc) && !inner_filled(mc)); + cASSERT(mc, is_filled(mc) && inner_filled(mc)); MDBX_cursor *mx = &mc->subcur->cursor; data->iov_len = page_numkeys(mx->pg[mx->top]) * mx->tree->dupfix_size; data->iov_base = page_data(mx->pg[mx->top]); @@ -17796,14 +17802,11 @@ static int dbi_open_locked(MDBX_txn *txn, unsigned user_flags, MDBX_dbi *dbi, } /* Done here so we cannot fail after creating a new DB */ - void *clone = nullptr; - if (name.iov_len) { - clone = osal_malloc(dbi_namelen(name)); - if (unlikely(!clone)) - return MDBX_ENOMEM; - name.iov_base = memcpy(clone, name.iov_base, name.iov_len); - } else - name.iov_base = ""; + defer_free_item_t *const clone = osal_malloc(dbi_namelen(name)); + if (unlikely(!clone)) + return MDBX_ENOMEM; + memcpy(clone, name.iov_base, name.iov_len); + name.iov_base = clone; uint8_t dbi_state = DBI_LINDO | DBI_VALID | DBI_FRESH; if (unlikely(rc)) { @@ -18183,8 +18186,53 @@ int mdbx_dbi_close(MDBX_env *env, MDBX_dbi dbi) { return MDBX_BAD_DBI; rc = osal_fastmutex_acquire(&env->dbi_lock); - if (likely(rc == MDBX_SUCCESS)) + if (likely(rc == MDBX_SUCCESS && dbi < env->n_dbi)) { + retry: + if (env->basal_txn && (env->dbs_flags[dbi] & DB_VALID) && + (env->basal_txn->flags & MDBX_TXN_FINISHED) == 0) { + /* LY: Опасный код, так как env->txn может быть изменено в другом потоке. + * К сожалению тут нет надежного решения и может быть падение при неверном + * использовании API (вызове mdbx_dbi_close конкурентно с завершением + * пишущей транзакции). + * + * Для минимизации вероятности падения сначала проверяем dbi-флаги + * в basal_txn, а уже после в env->txn. Таким образом, падение может быть + * только при коллизии с завершением вложенной транзакции. + * + * Альтернативно можно попробовать выполнять обновление/put записи в + * mainDb соответствующей таблице закрываемого хендла. Семантически это + * верный путь, но проблема в текущем API, в котором исторически dbi-хендл + * живет и закрывается вне транзакции. Причем проблема не только в том, + * что нет указателя на текущую пишущую транзакцию, а в том что + * пользователь точно не ожидает что закрытие хендла приведет к + * скрытой/непрозрачной активности внутри транзакции потенциально + * выполняемой в другом потоке. Другими словами, проблема может быть + * только при неверном использовании API и если пользователь это + * допускает, то точно не будет ожидать скрытых действий внутри + * транзакции, и поэтому этот путь потенциально более опасен. */ + const MDBX_txn *const hazard = env->txn; + osal_compiler_barrier(); + if ((dbi_state(env->basal_txn, dbi) & + (DBI_LINDO | DBI_DIRTY | DBI_CREAT)) > DBI_LINDO) { + bailout_dirty_dbi: + osal_fastmutex_release(&env->dbi_lock); + return MDBX_DANGLING_DBI; + } + osal_memory_barrier(); + if (unlikely(hazard != env->txn)) + goto retry; + if (hazard != env->basal_txn && hazard && + (hazard->flags & MDBX_TXN_FINISHED) == 0 && + hazard->signature == txn_signature && + (dbi_state(hazard, dbi) & (DBI_LINDO | DBI_DIRTY | DBI_CREAT)) > + DBI_LINDO) + goto bailout_dirty_dbi; + osal_compiler_barrier(); + if (unlikely(hazard != env->txn)) + goto retry; + } rc = defer_and_release(env, dbi_close_locked(env, dbi)); + } return rc; } @@ -19037,10 +19085,10 @@ __cold int dxb_resize(MDBX_env *const env, const pgno_t used_pgno, const void *const prev_map = env->dxb_mmap.base; #endif /* MDBX_ENABLE_MADVISE || ENABLE_MEMCHECK */ - VERBOSE("resize/%d datafile/mapping: " + VERBOSE("resize(env-flags 0x%x, mode %d) datafile/mapping: " "present %" PRIuPTR " -> %" PRIuPTR ", " "limit %" PRIuPTR " -> %" PRIuPTR, - mode, prev_size, size_bytes, prev_limit, limit_bytes); + env->flags, mode, prev_size, size_bytes, prev_limit, limit_bytes); eASSERT(env, limit_bytes >= size_bytes); eASSERT(env, bytes2pgno(env, size_bytes) >= size_pgno); @@ -23535,6 +23583,7 @@ static rid_t get_rid_for_reclaimed(MDBX_txn *txn, gcu_t *ctx, (MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) - ctx->reused_slot) * txn->env->maxgc_large1page) { if (unlikely(ctx->rid <= MIN_TXNID)) { + ctx->dense = true; if (unlikely(MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) <= ctx->reused_slot)) { NOTICE("** restart: reserve depleted (reused_gc_slot %zu >= " @@ -23561,10 +23610,10 @@ static rid_t get_rid_for_reclaimed(MDBX_txn *txn, gcu_t *ctx, goto return_error; } const txnid_t gc_first = unaligned_peek_u64(4, key.iov_base); - if (unlikely(gc_first <= MIN_TXNID)) { - DEBUG("%s: no free GC's id(s) less than %" PRIaTXN - " (going dense-mode)", - dbg_prefix(ctx), ctx->rid); + if (unlikely(gc_first <= INITIAL_TXNID)) { + NOTICE("%s: no free GC's id(s) less than %" PRIaTXN + " (going dense-mode)", + dbg_prefix(ctx), ctx->rid); ctx->dense = true; goto return_restart; } @@ -23671,19 +23720,11 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { txn->cursors[FREE_DBI] = &ctx->cursor; int rc; - // tASSERT(txn, MDBX_PNL_GETSIZE(txn->tw.retired_pages) || - // ctx->cleaned_slot < - // (txn->tw.gc.reclaimed ? - // MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) : 0) - // || ctx->cleaned_id < txn->tw.gc.last_reclaimed); - /* txn->tw.relist[] can grow and shrink during this call. * txn->tw.gc.last_reclaimed and txn->tw.retired_pages[] can only grow. * But page numbers cannot disappear from txn->tw.retired_pages[]. */ -#if MDBX_ENABLE_GC_EXPERIMENTAL retry_clean_adj: ctx->reserve_adj = 0; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ retry: ctx->loop += ctx->prev_first_unallocated == txn->geo.first_unallocated; TRACE(">> restart, loop %u", ctx->loop); @@ -23710,7 +23751,8 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { ctx->reserved = 0; ctx->cleaned_slot = 0; ctx->reused_slot = 0; - ctx->amount = ctx->fill_idx = ~0u; + ctx->amount = 0; + ctx->fill_idx = ~0u; ctx->cleaned_id = 0; ctx->rid = txn->tw.gc.last_reclaimed; while (true) { @@ -23827,9 +23869,7 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { env->maxgc_large1page / 2)) { TRACE("%s: reclaimed-list changed %zu -> %zu, retry", dbg_prefix(ctx), ctx->amount, MDBX_PNL_GETSIZE(txn->tw.relist)); -#if MDBX_ENABLE_GC_EXPERIMENTAL ctx->reserve_adj += ctx->reserved - MDBX_PNL_GETSIZE(txn->tw.relist); -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ goto retry; } ctx->amount = MDBX_PNL_GETSIZE(txn->tw.relist); @@ -23853,7 +23893,6 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { if (unlikely(rc != MDBX_SUCCESS)) goto bailout; } -#if MDBX_ENABLE_GC_EXPERIMENTAL const size_t left = ctx->amount - ctx->reserved - ctx->reserve_adj; TRACE("%s: amount %zu, reserved %zd, reserve_adj %zu, left %zd, " "lifo-reclaimed-slots %zu, " @@ -23861,15 +23900,6 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { dbg_prefix(ctx), ctx->amount, ctx->reserved, ctx->reserve_adj, left, txn->tw.gc.reclaimed ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) : 0, ctx->reused_slot); -#else - const size_t left = ctx->amount - ctx->reserved; - TRACE("%s: amount %zu, reserved %zd, left %zd, " - "lifo-reclaimed-slots %zu, " - "reused-gc-slots %zu", - dbg_prefix(ctx), ctx->amount, ctx->reserved, left, - txn->tw.gc.reclaimed ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) : 0, - ctx->reused_slot); -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ if (0 >= (intptr_t)left) break; @@ -23992,9 +24022,7 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { TRACE("%s", " >> filling"); /* Fill in the reserved records */ -#if MDBX_ENABLE_GC_EXPERIMENTAL size_t excess_slots = 0; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ ctx->fill_idx = txn->tw.gc.reclaimed ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) - ctx->reused_slot @@ -24005,15 +24033,17 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { tASSERT(txn, dpl_check(txn)); if (ctx->amount) { MDBX_val key, data; - key.iov_len = data.iov_len = 0; /* avoid MSVC warning */ + key.iov_len = data.iov_len = 0; key.iov_base = data.iov_base = nullptr; size_t left = ctx->amount, excess = 0; if (txn->tw.gc.reclaimed == nullptr) { tASSERT(txn, is_lifo(txn) == 0); rc = outer_first(&ctx->cursor, &key, &data); - if (unlikely(rc != MDBX_SUCCESS)) - goto bailout; + if (unlikely(rc != MDBX_SUCCESS)) { + if (rc != MDBX_NOTFOUND) + goto bailout; + } } else { tASSERT(txn, is_lifo(txn) != 0); } @@ -24024,36 +24054,29 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { MDBX_PNL_GETSIZE(txn->tw.relist)); if (txn->tw.gc.reclaimed == nullptr) { tASSERT(txn, is_lifo(txn) == 0); - fill_gc_id = unaligned_peek_u64(4, key.iov_base); + fill_gc_id = + key.iov_base ? unaligned_peek_u64(4, key.iov_base) : MIN_TXNID; if (ctx->fill_idx == 0 || fill_gc_id > txn->tw.gc.last_reclaimed) { -#if MDBX_ENABLE_GC_EXPERIMENTAL if (!left) break; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ NOTICE("** restart: reserve depleted (fill_idx %zu, fill_id %" PRIaTXN " > last_reclaimed %" PRIaTXN ", left %zu", ctx->fill_idx, fill_gc_id, txn->tw.gc.last_reclaimed, left); -#if MDBX_ENABLE_GC_EXPERIMENTAL ctx->reserve_adj = (ctx->reserve_adj > left) ? ctx->reserve_adj - left : 0; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ goto retry; } ctx->fill_idx -= 1; } else { tASSERT(txn, is_lifo(txn) != 0); if (ctx->fill_idx >= MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed)) { -#if MDBX_ENABLE_GC_EXPERIMENTAL if (!left) break; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ NOTICE("** restart: reserve depleted (fill_idx %zu >= " "gc.reclaimed %zu, left %zu", ctx->fill_idx, MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed), left); -#if MDBX_ENABLE_GC_EXPERIMENTAL ctx->reserve_adj = (ctx->reserve_adj > left) ? ctx->reserve_adj - left : 0; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ goto retry; } ctx->fill_idx += 1; @@ -24082,12 +24105,10 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { excess += delta; TRACE("%s: chunk %zu > left %zu, @%" PRIaTXN, dbg_prefix(ctx), chunk, left, fill_gc_id); -#if MDBX_ENABLE_GC_EXPERIMENTAL if (!left) { excess_slots += 1; goto next; } -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ if ((ctx->loop < 5 && delta > (ctx->loop / 2)) || delta > env->maxgc_large1page) data.iov_len = (left + 1) * sizeof(pgno_t); @@ -24103,10 +24124,8 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { NOTICE("** restart: reclaimed-list changed (%zu -> %zu, loose +%zu)", ctx->amount, MDBX_PNL_GETSIZE(txn->tw.relist), txn->tw.loose_count); -#if MDBX_ENABLE_GC_EXPERIMENTAL if (ctx->loop < 5 || (ctx->loop > 10 && (ctx->loop & 1))) goto retry_clean_adj; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ goto retry; } @@ -24142,23 +24161,16 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { goto bailout; } -#if MDBX_ENABLE_GC_EXPERIMENTAL next: -#else - if (left == 0) - break; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ if (txn->tw.gc.reclaimed == nullptr) { tASSERT(txn, is_lifo(txn) == 0); rc = outer_next(&ctx->cursor, &key, &data, MDBX_NEXT); if (unlikely(rc != MDBX_SUCCESS)) { -#if MDBX_ENABLE_GC_EXPERIMENTAL if (rc == MDBX_NOTFOUND && !left) { rc = MDBX_SUCCESS; break; } -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ goto bailout; } } else { @@ -24167,14 +24179,12 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { } if (excess) { -#if MDBX_ENABLE_GC_EXPERIMENTAL size_t n = excess, adj = excess; while (n >= env->maxgc_large1page) adj -= n /= env->maxgc_large1page; ctx->reserve_adj += adj; TRACE("%s: extra %zu reserved space, adj +%zu (%zu)", dbg_prefix(ctx), excess, adj, ctx->reserve_adj); -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ } } @@ -24186,27 +24196,15 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { goto retry; } -#if MDBX_ENABLE_GC_EXPERIMENTAL if (unlikely(excess_slots)) { const bool will_retry = ctx->loop < 5 || excess_slots > 1; NOTICE("** %s: reserve excess (excess-slots %zu, filled-slot %zu, adj %zu, " - "loop %zu)", + "loop %u)", will_retry ? "restart" : "ignore", excess_slots, ctx->fill_idx, ctx->reserve_adj, ctx->loop); if (will_retry) goto retry; } -#else - if (unlikely(ctx->fill_idx != (txn->tw.gc.reclaimed - ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) - : 0))) { - const bool will_retry = ctx->loop < 9; - NOTICE("** %s: reserve excess (filled-idx %zu, loop %u)", - will_retry ? "restart" : "ignore", ctx->fill_idx, ctx->loop); - if (will_retry) - goto retry; - } -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ tASSERT(txn, txn->tw.gc.reclaimed == nullptr || ctx->cleaned_slot == MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed)); @@ -28888,13 +28886,14 @@ __extern_C void __assert2(const char *file, int line, const char *function, __assert2(file, line, function, assertion) #elif defined(__UCLIBC__) -__extern_C void __assert(const char *, const char *, unsigned, const char *) +MDBX_NORETURN __extern_C void __assert(const char *, const char *, unsigned, + const char *) #ifdef __THROW __THROW #else __nothrow #endif /* __THROW */ - MDBX_NORETURN; + ; #define __assert_fail(assertion, file, line, function) \ __assert(assertion, file, line, function) @@ -28902,14 +28901,15 @@ __extern_C void __assert(const char *, const char *, unsigned, const char *) /* workaround for avoid musl libc wrong prototype */ ( \ defined(__GLIBC__) || defined(__GNU_LIBRARY__)) /* Prototype should match libc runtime. ISO POSIX (2003) & LSB 1.x-3.x */ -__extern_C void __assert_fail(const char *assertion, const char *file, - unsigned line, const char *function) +MDBX_NORETURN __extern_C void __assert_fail(const char *assertion, + const char *file, unsigned line, + const char *function) #ifdef __THROW __THROW #else __nothrow #endif /* __THROW */ - MDBX_NORETURN; + ; #elif defined(__APPLE__) || defined(__MACH__) __extern_C void __assert_rtn(const char *function, const char *file, int line, @@ -28927,8 +28927,9 @@ __extern_C void __assert_rtn(const char *function, const char *file, int line, #define __assert_fail(assertion, file, line, function) \ __assert_rtn(function, file, line, assertion) #elif defined(__sun) || defined(__SVR4) || defined(__svr4__) -__extern_C void __assert_c99(const char *assection, const char *file, int line, - const char *function) MDBX_NORETURN; +MDBX_NORETURN __extern_C void __assert_c99(const char *assection, + const char *file, int line, + const char *function); #define __assert_fail(assertion, file, line, function) \ __assert_c99(assertion, file, line, function) #elif defined(__OpenBSD__) @@ -31503,7 +31504,9 @@ __cold MDBX_INTERNAL void osal_jitter(bool tiny) { for (;;) { #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ defined(__x86_64__) - const unsigned salt = 277u * (unsigned)__rdtsc(); + unsigned salt = 5296013u * (unsigned)__rdtsc(); + salt ^= salt >> 11; + salt *= 25810541u; #elif (defined(_WIN32) || defined(_WIN64)) && MDBX_WITHOUT_MSVC_CRT static ULONG state; const unsigned salt = (unsigned)RtlRandomEx(&state); @@ -31511,13 +31514,26 @@ __cold MDBX_INTERNAL void osal_jitter(bool tiny) { const unsigned salt = rand(); #endif - const unsigned coin = salt % (tiny ? 29u : 43u); + const int coin = salt % (tiny ? 29u : 43u); if (coin < 43 / 3) break; #if defined(_WIN32) || defined(_WIN64) - SwitchToThread(); - if (coin > 43 * 2 / 3) - Sleep(1); + if (coin < 43 * 2 / 3) + SwitchToThread(); + else { + static HANDLE timer; + if (!timer) + timer = CreateWaitableTimer(NULL, TRUE, NULL); + + LARGE_INTEGER ft; + ft.QuadPart = + coin * (int64_t)-10; // Convert to 100 nanosecond interval, + // negative value indicates relative time. + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + // CloseHandle(timer); + break; + } #else sched_yield(); if (coin > 43 * 2 / 3) @@ -32251,7 +32267,9 @@ __cold int mdbx_get_sysraminfo(intptr_t *page_size, intptr_t *total_pages, #include #endif /* FreeBSD */ -#if __GLIBC_PREREQ(2, 25) || defined(__FreeBSD__) || defined(__NetBSD__) || \ +#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED +#include +#elif __GLIBC_PREREQ(2, 25) || defined(__FreeBSD__) || defined(__NetBSD__) || \ defined(__BSD__) || defined(__bsdi__) || defined(__DragonFly__) || \ defined(__APPLE__) || __has_include() #include @@ -40499,9 +40517,9 @@ __dll_export 0, 13, 1, - 0, - {"2024-08-30T00:01:07+03:00", "4ad05c5f867a963162def46b68eff5f7130b81ca", "5fc7a6b1077794789b97bb2a56f5a4eb541a0bc0", - "v0.13.1-0-g5fc7a6b1"}, + 56, + {"2024-11-07T11:40:16+03:00", "b2d74d5eb64fcb73b35ed64508c8fcaeb3f513e1", "10a93f4b9f485e8f71592f4ec9016a69faa209c8", + "v0.13.1-56-g10a93f4b"}, sourcery}; __dll_export diff --git a/mdbx/mdbx.h b/mdbx/mdbx.h index 9972e6b..2ae43a6 100644 --- a/mdbx/mdbx.h +++ b/mdbx/mdbx.h @@ -189,16 +189,43 @@ typedef mode_t mdbx_mode_t; #define __has_attribute(x) (0) #endif /* __has_attribute */ +#ifndef __has_c_attribute +#define __has_c_attribute(x) (0) +#endif /* __has_c_attribute */ + #ifndef __has_cpp_attribute #define __has_cpp_attribute(x) 0 #endif /* __has_cpp_attribute */ +#ifndef __has_CXX_attribute +#if defined(__cplusplus) && \ + (!defined(_MSC_VER) || defined(__clang__) || _MSC_VER >= 1942) +#define __has_CXX_attribute(x) __has_cpp_attribute(x) +#else +#define __has_CXX_attribute(x) 0 +#endif +#endif /* __has_CXX_attribute */ + +#ifndef __has_C23_or_CXX_attribute +#if defined(__cplusplus) +#define __has_C23_or_CXX_attribute(x) __has_CXX_attribute(x) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ > 202311L +#define __has_C23_or_CXX_attribute(x) __has_c_attribute(x) +#else +#define __has_C23_or_CXX_attribute(x) 0 +#endif +#endif /* __has_C23_or_CXX_attribute */ + #ifndef __has_feature #define __has_feature(x) (0) +#define __has_exceptions_disabled (0) +#else +#define __has_exceptions_disabled \ + (__has_feature(cxx_noexcept) && !__has_feature(cxx_exceptions)) #endif /* __has_feature */ #ifndef __has_extension -#define __has_extension(x) (0) +#define __has_extension(x) __has_feature(x) #endif /* __has_extension */ #ifndef __has_builtin @@ -213,15 +240,14 @@ typedef mode_t mdbx_mode_t; * These functions should be declared with the attribute pure. */ #if defined(DOXYGEN) #define MDBX_PURE_FUNCTION [[gnu::pure]] +#elif __has_C23_or_CXX_attribute(gnu::pure) && \ + (!defined(__apple_build_version__) || !defined(__clang_major__) || \ + __clang_major__ > 17) +#define MDBX_PURE_FUNCTION [[gnu::pure]] #elif (defined(__GNUC__) || __has_attribute(__pure__)) && \ - (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ - || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) + (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ || \ + !defined(__cplusplus) || __has_exceptions_disabled) #define MDBX_PURE_FUNCTION __attribute__((__pure__)) -#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 -#define MDBX_PURE_FUNCTION -#elif defined(__cplusplus) && __has_cpp_attribute(gnu::pure) && \ - (!defined(__clang__) || !__has_feature(cxx_exceptions)) -#define MDBX_PURE_FUNCTION [[gnu::pure]] #else #define MDBX_PURE_FUNCTION #endif /* MDBX_PURE_FUNCTION */ @@ -231,22 +257,16 @@ typedef mode_t mdbx_mode_t; * that is compatible to CLANG and proposed [[pure]]. */ #if defined(DOXYGEN) #define MDBX_NOTHROW_PURE_FUNCTION [[gnu::pure, gnu::nothrow]] -#elif defined(__GNUC__) || \ - (__has_attribute(__pure__) && __has_attribute(__nothrow__)) -#define MDBX_NOTHROW_PURE_FUNCTION __attribute__((__pure__, __nothrow__)) -#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 -#if __has_cpp_attribute(pure) -#define MDBX_NOTHROW_PURE_FUNCTION [[pure]] -#else -#define MDBX_NOTHROW_PURE_FUNCTION -#endif -#elif defined(__cplusplus) && __has_cpp_attribute(gnu::pure) -#if __has_cpp_attribute(gnu::nothrow) +#elif __has_C23_or_CXX_attribute(gnu::pure) +#if __has_C23_or_CXX_attribute(gnu::nothrow) #define MDBX_NOTHROW_PURE_FUNCTION [[gnu::pure, gnu::nothrow]] #else #define MDBX_NOTHROW_PURE_FUNCTION [[gnu::pure]] #endif -#elif defined(__cplusplus) && __has_cpp_attribute(pure) +#elif defined(__GNUC__) || \ + (__has_attribute(__pure__) && __has_attribute(__nothrow__)) +#define MDBX_NOTHROW_PURE_FUNCTION __attribute__((__pure__, __nothrow__)) +#elif __has_CXX_attribute(pure) #define MDBX_NOTHROW_PURE_FUNCTION [[pure]] #else #define MDBX_NOTHROW_PURE_FUNCTION @@ -264,15 +284,14 @@ typedef mode_t mdbx_mode_t; * It does not make sense for a const function to return void. */ #if defined(DOXYGEN) #define MDBX_CONST_FUNCTION [[gnu::const]] -#elif (defined(__GNUC__) || __has_attribute(__pure__)) && \ - (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ - || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) -#define MDBX_CONST_FUNCTION __attribute__((__const__)) -#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 -#define MDBX_CONST_FUNCTION MDBX_PURE_FUNCTION -#elif defined(__cplusplus) && __has_cpp_attribute(gnu::const) && \ - (!defined(__clang__) || !__has_feature(cxx_exceptions)) +#elif __has_C23_or_CXX_attribute(gnu::const) && \ + (!defined(__apple_build_version__) || !defined(__clang_major__) || \ + __clang_major__ > 17) #define MDBX_CONST_FUNCTION [[gnu::const]] +#elif (defined(__GNUC__) || __has_attribute(__const__)) && \ + (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ || \ + !defined(__cplusplus) || __has_exceptions_disabled) +#define MDBX_CONST_FUNCTION __attribute__((__const__)) #else #define MDBX_CONST_FUNCTION MDBX_PURE_FUNCTION #endif /* MDBX_CONST_FUNCTION */ @@ -282,18 +301,16 @@ typedef mode_t mdbx_mode_t; * that is compatible to CLANG and future [[const]]. */ #if defined(DOXYGEN) #define MDBX_NOTHROW_CONST_FUNCTION [[gnu::const, gnu::nothrow]] +#elif __has_C23_or_CXX_attribute(gnu::const) +#if __has_C23_or_CXX_attribute(gnu::nothrow) +#define MDBX_NOTHROW_CONST_FUNCTION [[gnu::const, gnu::nothrow]] +#else +#define MDBX_NOTHROW_CONST_FUNCTION [[gnu::const]] +#endif #elif defined(__GNUC__) || \ (__has_attribute(__const__) && __has_attribute(__nothrow__)) #define MDBX_NOTHROW_CONST_FUNCTION __attribute__((__const__, __nothrow__)) -#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 -#define MDBX_NOTHROW_CONST_FUNCTION MDBX_NOTHROW_PURE_FUNCTION -#elif defined(__cplusplus) && __has_cpp_attribute(gnu::const) -#if __has_cpp_attribute(gnu::nothrow) -#define MDBX_NOTHROW_PURE_FUNCTION [[gnu::const, gnu::nothrow]] -#else -#define MDBX_NOTHROW_PURE_FUNCTION [[gnu::const]] -#endif -#elif defined(__cplusplus) && __has_cpp_attribute(const) +#elif __has_CXX_attribute(const) #define MDBX_NOTHROW_CONST_FUNCTION [[const]] #else #define MDBX_NOTHROW_CONST_FUNCTION MDBX_NOTHROW_PURE_FUNCTION @@ -612,7 +629,7 @@ extern "C" { #define MDBX_VERSION_MINOR 13 #ifndef LIBMDBX_API -#if defined(LIBMDBX_EXPORTS) +#if defined(LIBMDBX_EXPORTS) || defined(DOXYGEN) #define LIBMDBX_API __dll_export #elif defined(LIBMDBX_IMPORTS) #define LIBMDBX_API __dll_import @@ -622,7 +639,7 @@ extern "C" { #endif /* LIBMDBX_API */ #ifdef __cplusplus -#if defined(__clang__) || __has_attribute(type_visibility) +#if defined(__clang__) || __has_attribute(type_visibility) || defined(DOXYGEN) #define LIBMDBX_API_TYPE LIBMDBX_API __attribute__((type_visibility("default"))) #else #define LIBMDBX_API_TYPE LIBMDBX_API @@ -1996,7 +2013,7 @@ typedef enum MDBX_error { MDBX_DUPLICATED_CLK = -30413, /** Some cursors and/or other resources should be closed before table or - * corresponding DBI-handle could be (re)used */ + * corresponding DBI-handle could be (re)used and/or closed. */ MDBX_DANGLING_DBI = -30412, /** The parked read transaction was outed for the sake of @@ -2542,6 +2559,11 @@ LIBMDBX_API int mdbx_env_open(MDBX_env *env, const char *pathname, * \see mdbx_env_open() */ LIBMDBX_API int mdbx_env_openW(MDBX_env *env, const wchar_t *pathname, MDBX_env_flags_t flags, mdbx_mode_t mode); +#define mdbx_env_openT(env, pathname, flags, mode) \ + mdbx_env_openW(env, pathname, flags, mode) +#else +#define mdbx_env_openT(env, pathname, flags, mode) \ + mdbx_env_open(env, pathname, flags, mode) #endif /* Windows */ /** \brief Deletion modes for \ref mdbx_env_delete(). @@ -2592,6 +2614,9 @@ LIBMDBX_API int mdbx_env_delete(const char *pathname, * \see mdbx_env_delete() */ LIBMDBX_API int mdbx_env_deleteW(const wchar_t *pathname, MDBX_env_delete_mode_t mode); +#define mdbx_env_deleteT(pathname, mode) mdbx_env_deleteW(pathname, mode) +#else +#define mdbx_env_deleteT(pathname, mode) mdbx_env_delete(pathname, mode) #endif /* Windows */ /** \brief Copy an MDBX environment to the specified path, with options. @@ -2713,6 +2738,7 @@ LIBMDBX_API int mdbx_txn_copy2pathname(MDBX_txn *txn, const char *dest, * \see mdbx_env_copy() */ LIBMDBX_API int mdbx_env_copyW(MDBX_env *env, const wchar_t *dest, MDBX_copy_flags_t flags); +#define mdbx_env_copyT(env, dest, flags) mdbx_env_copyW(env, dest, flags) /** \copydoc mdbx_txn_copy2pathname() * \ingroup c_extra @@ -2720,6 +2746,12 @@ LIBMDBX_API int mdbx_env_copyW(MDBX_env *env, const wchar_t *dest, * \see mdbx_txn_copy2pathname() */ LIBMDBX_API int mdbx_txn_copy2pathnameW(MDBX_txn *txn, const wchar_t *dest, MDBX_copy_flags_t flags); +#define mdbx_txn_copy2pathnameT(txn, dest, flags) \ + mdbx_txn_copy2pathnameW(txn, dest, path) +#else +#define mdbx_env_copyT(env, dest, flags) mdbx_env_copy(env, dest, flags) +#define mdbx_txn_copy2pathnameT(txn, dest, flags) \ + mdbx_txn_copy2pathname(txn, dest, path) #endif /* Windows */ /** \brief Copy an environment to the specified file descriptor, with @@ -3386,6 +3418,9 @@ LIBMDBX_API int mdbx_env_get_path(const MDBX_env *env, const char **dest); * \note Available only on Windows. * \see mdbx_env_get_path() */ LIBMDBX_API int mdbx_env_get_pathW(const MDBX_env *env, const wchar_t **dest); +#define mdbx_env_get_pathT(env, dest) mdbx_env_get_pathW(env, dest) +#else +#define mdbx_env_get_pathT(env, dest) mdbx_env_get_path(env, dest) #endif /* Windows */ /** \brief Return the file descriptor for the given environment. @@ -3848,7 +3883,7 @@ mdbx_env_get_maxvalsize_ex(const MDBX_env *env, MDBX_db_flags_t flags); /** \deprecated Please use \ref mdbx_env_get_maxkeysize_ex() * and/or \ref mdbx_env_get_maxvalsize_ex() * \ingroup c_statinfo */ -MDBX_DEPRECATED MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int +MDBX_NOTHROW_PURE_FUNCTION MDBX_DEPRECATED LIBMDBX_API int mdbx_env_get_maxkeysize(const MDBX_env *env); /** \brief Returns maximal size of key-value pair to fit in a single page @@ -4887,9 +4922,12 @@ LIBMDBX_INLINE_API(int, mdbx_dbi_flags, * \ref mdbx_env_set_maxdbs(), unless that value would be large. * * \note Use with care. - * This call is synchronized via mutex with \ref mdbx_dbi_close(), but NOT with - * other transactions running by other threads. The "next" version of libmdbx - * (\ref MithrilDB) will solve this issue. + * This call is synchronized via mutex with \ref mdbx_dbi_open(), but NOT with + * any transaction(s) running by other thread(s). + * So the `mdbx_dbi_close()` MUST NOT be called in-parallel/concurrently + * with any transactions using the closing dbi-handle, nor during other thread + * commit/abort a write transacton(s). The "next" version of libmdbx (\ref + * MithrilDB) will solve this issue. * * Handles should only be closed if no other threads are going to reference * the table handle or one of its cursors any further. Do not close a handle @@ -6406,6 +6444,11 @@ LIBMDBX_API int mdbx_env_open_for_recoveryW(MDBX_env *env, const wchar_t *pathname, unsigned target_meta, bool writeable); +#define mdbx_env_open_for_recoveryT(env, pathname, target_mets, writeable) \ + mdbx_env_open_for_recoveryW(env, pathname, target_mets, writeable) +#else +#define mdbx_env_open_for_recoveryT(env, pathname, target_mets, writeable) \ + mdbx_env_open_for_recovery(env, pathname, target_mets, writeable) #endif /* Windows */ /** \brief Turn database to the specified meta-page. @@ -6455,6 +6498,11 @@ LIBMDBX_API int mdbx_preopen_snapinfo(const char *pathname, MDBX_envinfo *info, * \see mdbx_preopen_snapinfo() */ LIBMDBX_API int mdbx_preopen_snapinfoW(const wchar_t *pathname, MDBX_envinfo *info, size_t bytes); +#define mdbx_preopen_snapinfoT(pathname, info, bytes) \ + mdbx_preopen_snapinfoW(pathname, info, bytes) +#else +#define mdbx_preopen_snapinfoT(pathname, info, bytes) \ + mdbx_preopen_snapinfo(pathname, info, bytes) #endif /* Windows */ /** \brief Флаги/опции для проверки целостности базы данных. diff --git a/mdbxdist/CMakeLists.txt b/mdbxdist/CMakeLists.txt index 1a378bf..59dcada 100644 --- a/mdbxdist/CMakeLists.txt +++ b/mdbxdist/CMakeLists.txt @@ -450,7 +450,6 @@ if(MDBX_MANAGE_BUILD_FLAGS) setup_compile_flags() endif() -list(FIND CMAKE_C_COMPILE_FEATURES c_std_11 HAS_C11) list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_11 HAS_CXX11) list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_14 HAS_CXX14) list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_17 HAS_CXX17) @@ -481,6 +480,9 @@ if(NOT DEFINED MDBX_CXX_STANDARD) set(MDBX_CXX_STANDARD 98) endif() endif() + +list(FIND CMAKE_C_COMPILE_FEATURES c_std_11 HAS_C11) +list(FIND CMAKE_C_COMPILE_FEATURES c_std_23 HAS_C23) if(NOT DEFINED MDBX_C_STANDARD) # MSVC >= 19.28 (Microsoft Visual Studio 16.8) is mad! # It unable process Windows SDK headers in the C11 mode! @@ -488,6 +490,8 @@ if(NOT DEFINED MDBX_C_STANDARD) set(MDBX_C_STANDARD 99) set(C_FALLBACK_11 OFF) set(C_FALLBACK_GNU11 OFF) + elseif(NOT HAS_C23 LESS 0) + set(MDBX_C_STANDARD 23) elseif(HAS_C11 LESS 0 AND NOT C_FALLBACK_GNU11 AND NOT C_FALLBACK_11) set(MDBX_C_STANDARD 99) else() @@ -495,7 +499,7 @@ if(NOT DEFINED MDBX_C_STANDARD) endif() endif() -if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND EXISTS "${MDBX_SOURCE_DIR}/ntdll.def") +if(WIN32 AND EXISTS "${MDBX_SOURCE_DIR}/ntdll.def") if(MSVC) if(NOT MSVC_LIB_EXE) # Find lib.exe @@ -592,7 +596,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR IOS) add_mdbx_option(MDBX_OSX_SPEED_INSTEADOF_DURABILITY "Disable use fcntl(F_FULLFSYNC) in favor of speed" OFF) mark_as_advanced(MDBX_OSX_SPEED_INSTEADOF_DURABILITY) endif() -if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") +if(WIN32) if(MDBX_NTDLL_EXTRA_IMPLIB) add_mdbx_option(MDBX_WITHOUT_MSVC_CRT "Avoid dependence from MSVC CRT and use ntdll.dll instead" OFF) endif() @@ -656,6 +660,10 @@ else() set(MDBX_ENABLE_TESTS FALSE) endif() +if(CI) + add_definitions(-DMDBX_CI="${CI}") +endif() + ################################################################################ ################################################################################ @@ -796,7 +804,7 @@ macro(target_setup_options TARGET) set_target_properties(${TARGET} PROPERTIES INTERPROCEDURAL_OPTIMIZATION $) endif() - if(NOT C_FALLBACK_GNU11 AND NOT C_FALLBACK_11) + if(NOT MDBX_C_STANDARD EQUAL 11 OR (NOT C_FALLBACK_GNU11 AND NOT C_FALLBACK_11)) set_target_properties(${TARGET} PROPERTIES C_STANDARD ${MDBX_C_STANDARD} C_STANDARD_REQUIRED ON) endif() @@ -825,7 +833,7 @@ macro(libmdbx_setup_libs TARGET MODE) else() target_link_libraries(${TARGET} ${MODE} Threads::Threads) endif() - if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + if(WIN32) target_link_libraries(${TARGET} ${MODE} ntdll user32 kernel32 advapi32 ole32) if(MDBX_NTDLL_EXTRA_IMPLIB AND MDBX_WITHOUT_MSVC_CRT) target_link_libraries(${TARGET} ${MODE} ntdll_extra) @@ -906,14 +914,27 @@ if(MDBX_BUILD_SHARED_LIBRARY AND MDBX_LINK_TOOLS_NONSTATIC) set(CMAKE_INSTALL_RPATH "\$ORIGIN/../lib") endif() endif() + + if(WIN32) + # Windows don't have RPATH feature, + # therefore we should prepare PATH or copy DLL(s) + set(TOOL_MDBX_DLLCRUTCH "Crutch for ${CMAKE_SYSTEM_NAME}") + if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_VERSION VERSION_LESS 3.0) + # will use LOCATION property to compose DLLPATH + cmake_policy(SET CMP0026 OLD) + endif() + else() + set(TOOL_MDBX_DLLCRUTCH FALSE) + endif() else() set(TOOL_MDBX_LIB mdbx-static) + set(TOOL_MDBX_DLLCRUTCH FALSE) endif() # build mdbx-tools if(MDBX_BUILD_TOOLS) set(WINGETOPT_SRC "") - if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + if(WIN32) set(WINGETOPT_SRC ${MDBX_SOURCE_DIR}/tools/wingetopt.c ${MDBX_SOURCE_DIR}/tools/wingetopt.h) endif() diff --git a/mdbxdist/ChangeLog.md b/mdbxdist/ChangeLog.md index bb2b908..639a106 100644 --- a/mdbxdist/ChangeLog.md +++ b/mdbxdist/ChangeLog.md @@ -4,6 +4,69 @@ ChangeLog English version [by liar Google](https://gitflic-ru.translate.goog/project/erthink/libmdbx/blob?file=ChangeLog.md&_x_tr_sl=ru&_x_tr_tl=en) and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md). + +## v0.13.2 в процессе + +Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов. + +Исправления: + + - Функция `mdbx_close_dbi()` доработана для возврата ошибки `MDBX_DANGLING_DBI` + при попытке закрыть dbi-дескриптор таблицы, созданной и/или измененной в + ещё выполняющейся транзакции. Такое преждевременное закрытие дескриптора + является неверным использованием API и нарушением контракта/предусловий + сформулированных в описании `mdbx_close_dbi()`. Однако, вместо возврата + ошибки выполнялось некорректное закрытие дескриптора, что могло + приводить к созданию таблицы с пустым именем, утечки страниц БД и/или + нарушению структуры b-tree (неверной ссылкой на корень таблицы). + + - Исправлено открытие таблицы с пустым/нулевым именем, в том числе устранена + возможность `SIGSEGV` при закрытии её дескриптора. + + - Добавлены упущенные inline-реализации `mdbx::cursor::upper_bound()` и `mdbx::cursor::upper_bound_multivalue()`. + - Продолжена корректировка описания С++ API для использования термина "таблица" вместо "sub-database". + - Исправлено проверяемое условие внутри `assert()` в пути обработки `MDBX_GET/NEXT/PREV_MULTIPLE`. + - На 32-битных платформах разрешено использовть 4-байтное выравнивание при получении 64-битных значений посредством `MDBX_MULTIPLE`. + - Добавлен костыль для устранения проблем из-за некорректной обработки `[[gnu::pure]]` в Apple Clang. + +Новое: + + - Ускорено обновление GC при возврате/помещении списков страниц в + сложных сценариях. Был доработан и активирован ранее отключенный + экспериментальный режим корректирующей обратной связи. Этим + принципиально улучшилась сходимость (сократилось количество повторных + попыток), а также устранен дефект приводящий к "зацикливанию" при + фиксации транзакций (с возвратом ошибки `MDBX_PROBLEM`) в редких + специфических условиях. + Подробности см. в описании коммита [`6c56ed97bbd8ca46abac61886a113ba31e5f1291`](https://gitflic.ru/project/erthink/libmdbx/commit/6c56ed97bbd8ca46abac61886a113ba31e5f1291). + + - Добавлен метод `mdbx::cursor::get_multiple_samelength()` и переименован `mdbx::txn::put_multiple_samelength()`. + - Для единообразия C++ API при выполнении операции `MDBX_GET_MULTIPLE` теперь также возвращается значение самого ключа. + - Для размерных констант `mdbx::env::geometry` базовый тип изменен с беззнакового `size_t` на знаковый `intptr_t`. + - Включен стандарт `C23` в CMake-скриптах сборки. + - Добавлены T-макросы для парных `char`/`wchar_t` функций. + +Мелочи: + + - Теперь `MDBX_ENABLE_BIGFOOT` включена по-умолчанию вне зависимости от разрядности платформы. + - Дополнение README и исправление опечаток/орфографии. + - Использование `WIN32` вместо `${CMAKE_SYSTEM_NAME}`. + - Подавление параноидальных предупреждений MSVC в extra-тестах. + - Дополнение отладочного логирования внутри `dxb_resize()`. + - Добавление в сценарии CMake/CTest копирования dll под Windows для работы исключений в тестах на C++. + - Добавление С++ теста `extra/open`. + - Доработка `osal_jitter()` для уменьшения задержек в тестах под Windows. + - Исправление максимальной длины значений в тесте `extra/crunched-delete`. + - Добавление логирования С++ исключений в `extra/dupfix_multiple`. + - Корректировка API-макросов для Doxygen. + - Уточнение описания `mdbx_dbi_close()` для случая хендлов измененных таблиц. + - Добавление теста `extra/early_close_dbi`. + - Доработка скрипта стохастического теста и его переименование в `stochastic.sh`. + + +-------------------------------------------------------------------------------- + + ## v0.13.1 "РДС-1" от 2024-08-29 Новая версия со сменой лицензии, существенным расширением API, @@ -170,6 +233,48 @@ and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic ******************************************************************************** +## v0.12.12 "Доллежаль" от 2024-10-27 + +Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов, +в память о советском ученом-энергетике Николае Антоновиче Доллежаль в день 125-летия со дня его рождения. + +Это последний выпуск куста стабильных версий 0.12.x, спустя более двух +лет после выпуска 0.12.1. Последующие выпуски 0.12.x будут формироваться +только в случае существенных проблем/ошибок, вероятность чего близка к +нулю. Для всех проектов находящихся в стадии активной разраборки +рекомендуется использовать ветку `master`. + +``` +git diff' stat: x commits, y files changed, z insertions(+), zz deletions(-) +Signed-off-by: Леонид Юрьев (Leonid Yuriev) +``` + +Значимые исправления: + + - Исправление упущенного `TXN_END_EOTDONE` при сбое старта читающей транзакции. + Упомянутый флажок отсутствовал в пути разрушения транзакции при ошибке + её запуска. Из-за чего делалась попытка разрушить курсоры, что приводило + к падению **отладочных сборок**, так как в них соответствующий массив + намеренно заполнен некорректными указателями. + + - Устранение возможности `SIGSEGV` внутри `coherency_check()` после + изменения геометрии другим процессом с увеличением верхнего размера БД + и увеличением БД больше предыдущего лимита. + + - Доработка `mdbx_close_dbi()` для возврата ошибки при попытке закрыть + dbi-дескриптор таблицы, созданной и/или измененной в ещё выполняющейся + транзакции. Такое преждевременное закрытие дескриптора является неверным + использованием API и нарушением контракта/предусловий сформулированных + в описании `mdbx_close_dbi()`. Однако, вместо возврата ошибки + выполнялось некорректное закрытие дескриптора, что могло приводить к + созданию таблицы с пустым именем, утечки страниц БД и/или нарушению + структуры b-tree (неверной ссылкой на корень таблицы). + Добавлен соответствующий тест `extra/early_close_dbi`. + + +-------------------------------------------------------------------------------- + + ## v0.12.11 "Лиза и Соня" от 2024-07-23 Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов, @@ -183,7 +288,6 @@ and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic и наведение ATACAMS невозможно без использования орбитальной группировки военных спутников США. - ``` git diff' stat: 29 commits, 14 files changed, 379 insertions(+), 151 deletions(-) Signed-off-by: Леонид Юрьев (Leonid Yuriev) @@ -662,7 +766,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) все они были несущественные, либо ложные. - Устранено ложное предупреждение GCC при сборке для SH4. - Добавлена поддержка ASAN (Address Sanitizer) при сборке посредством MSVC. - - Расширен набор перебираемых режимов в скрипте `test/long_stochastic.sh`, + - Расширен набор перебираемых режимов в скрипте `test/stochastic.sh`, добавлена опция `--extra`. - В C++ API добавлена поддержка расширенных опций времени выполнения `mdbx::extra_runtime_option`, аналогично `enum MDBX_option_t` из C API. @@ -972,7 +1076,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) - Уменьшение в 42 раза значения по-умолчанию для `me_options.dp_limit` в отладочных сборках. - Добавление платформы `gcc-riscv64-linux-gnu` в список для цели `cross-gcc`. - - Небольшие правки скрипта `long_stochastic.sh` для работы в Windows. + - Небольшие правки скрипта `stochastic.sh` для работы в Windows. - Удаление ненужного вызова `LockFileEx()` внутри `mdbx_env_copy()`. - Добавлено описание использования файловых дескрипторов в различных режимах. - Добавлено использование `_CrtDbgReport()` в отладочных сборках. diff --git a/mdbxdist/README.md b/mdbxdist/README.md index d82599b..1fbf8e6 100644 --- a/mdbxdist/README.md +++ b/mdbxdist/README.md @@ -220,7 +220,8 @@ Thus syncing data to disk might be a bottleneck for write intensive workload. but read transactions prevents recycling an old retired/freed pages, since it read ones. Thus altering of data during a parallel long-lived read operation will increase the process work set, may exhaust entire free database space, the database can grow quickly, and result in performance degradation. -Try to avoid long running read transactions. +Try to avoid long running read transactions, otherwise use [transaction parking](https://libmdbx.dqdkfa.ru/group__c__transactions.html#ga2c2c97730ff35cadcedfbd891ac9b12f) +and/or [Handle-Slow-Readers callback](https://libmdbx.dqdkfa.ru/group__c__err.html#ga2cb11b56414c282fe06dd942ae6cade6). 5. _libmdbx_ is extraordinarily fast and provides minimal overhead for data access, so you should reconsider using brute force techniques and double check your code. @@ -278,45 +279,43 @@ the user's point of view. 5. The same database format for 32- and 64-bit builds. > _libmdbx_ database format depends only on the [endianness](https://en.wikipedia.org/wiki/Endianness) but not on the [bitness](https://en.wiktionary.org/wiki/bitness). -6. LIFO policy for Garbage Collection recycling. This can significantly increase write performance due write-back disk cache up to several times in a best case scenario. +6. The "Big Foot" feature than solves speific performance issues with huge transactions and extra-large page-number-lists. + +7. LIFO policy for Garbage Collection recycling. This can significantly increase write performance due write-back disk cache up to several times in a best case scenario. > LIFO means that for reuse will be taken the latest becomes unused pages. > Therefore the loop of database pages circulation becomes as short as possible. > In other words, the set of pages, that are (over)written in memory and on disk during a series of write transactions, will be as small as possible. > Thus creates ideal conditions for the battery-backed or flash-backed disk cache efficiency. -7. Fast estimation of range query result volume, i.e. how many items can +8. Parking of read transactions with ousting and auto-restart, [Handle-Slow-Readers callback](https://libmdbx.dqdkfa.ru/group__c__err.html#ga2cb11b56414c282fe06dd942ae6cade6) to resolve an issues due to long-lived read transactions. + +9. Fast estimation of range query result volume, i.e. how many items can be found between a `KEY1` and a `KEY2`. This is a prerequisite for build and/or optimize query execution plans. > _libmdbx_ performs a rough estimate based on common B-tree pages of the paths from root to corresponding keys. -8. Database integrity check API both with standalone `mdbx_chk` utility. - -9. Support for opening databases in the exclusive mode, including on a network share. +10. Database integrity check API both with standalone `mdbx_chk` utility. -10. Zero-length for keys and values. - -11. Ability to determine whether the particular data is on a dirty page -or not, that allows to avoid copy-out before updates. +11. Support for opening databases in the exclusive mode, including on a network share. 12. Extended information of whole-database, tables/sub-databases, transactions, readers enumeration. > _libmdbx_ provides a lot of information, including dirty and leftover pages > for a write transaction, reading lag and holdover space for read transactions. -13. Extended update and delete operations. - > _libmdbx_ allows one _at once_ with getting previous value - > and addressing the particular item from multi-value with the same key. +13. Support of Zero-length for keys and values. 14. Useful runtime options for tuning engine to application's requirements and use cases specific. 15. Automated steady sync-to-disk upon several thresholds and/or timeout via cheap polling. -16. Sequence generation and three persistent 64-bit markers. - -17. Handle-Slow-Readers callback to resolve a database full/overflow issues due to long-lived read transaction(s). +16. Ability to determine whether the particular data is on a dirty page +or not, that allows to avoid copy-out before updates. -18. Ability to determine whether the cursor is pointed to a key-value -pair, to the first, to the last, or not set to anything. +17. Extended update and delete operations. + > _libmdbx_ allows one _at once_ with getting previous value + > and addressing the particular item from multi-value with the same key. +18. Sequence generation and three persistent 64-bit vector-clock like markers. ## Other fixes and specifics @@ -473,7 +472,7 @@ Therefore, only basic information is provided: - The `Makefile` provide several self-described targets for testing: `smoke`, `test`, `check`, `memcheck`, `test-valgrind`, `test-asan`, `test-leak`, `test-ubsan`, `cross-gcc`, `cross-qemu`, `gcc-analyzer`, `smoke-fault`, `smoke-singleprocess`, `test-singleprocess`, 'long-test'. Please run `make --help` if doubt. - - In addition to the `mdbx_test` utility, there is the script [`long_stochastic.sh`](https://gitflic.ru/project/erthink/libmdbx/blob/master/test/long_stochastic.sh), + - In addition to the `mdbx_test` utility, there is the script [`stochastic.sh`](https://gitflic.ru/project/erthink/libmdbx/blob/master/test/stochastic.sh), which calls `mdbx_test` by going through set of modes and options, with gradually increasing the number of operations and the size of transactions. This script is used for mostly of all automatic testing, including `Makefile` targets and Continuous Integration. - Brief information of available command-line options is available by `--help`. @@ -584,7 +583,7 @@ during configure by CMake. An example of running a basic test script can be found in the [CI-script](appveyor.yml) for [AppVeyor](https://www.appveyor.com/). To -run the [long stochastic test scenario](test/long_stochastic.sh), +run the [long stochastic test scenario](test/stochastic.sh), [bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) is required, and such testing is recommended with placing the test data on the [RAM-disk](https://en.wikipedia.org/wiki/RAM_drive). @@ -604,7 +603,7 @@ directory with source code, and run `make check` to execute the base tests. If something goes wrong, it is recommended to install [Homebrew](https://brew.sh/) and try again. -To run the [long stochastic test scenario](test/long_stochastic.sh), you +To run the [long stochastic test scenario](test/stochastic.sh), you will need to install the current (not outdated) version of [Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)). To do this, we recommend that you install [Homebrew](https://brew.sh/) and then execute diff --git a/mdbxdist/VERSION.txt b/mdbxdist/VERSION.txt index 883dcff..3f0c26f 100644 --- a/mdbxdist/VERSION.txt +++ b/mdbxdist/VERSION.txt @@ -1 +1 @@ -0.13.1.0 +0.13.1.56 diff --git a/mdbxdist/cmake/compiler.cmake b/mdbxdist/cmake/compiler.cmake index bd50b9d..e05df1a 100644 --- a/mdbxdist/cmake/compiler.cmake +++ b/mdbxdist/cmake/compiler.cmake @@ -368,13 +368,15 @@ else() set(CMAKE_REQUIRED_FLAGS "-fopenmp -Werror") if(CMAKE_CXX_COMPILER_LOADED) check_cxx_source_compiles("int main(void) { - #pragma omp parallel - return 0; + #pragma omp for + for(int i = 0, j = 0; i != 42; i = 1 + i * 12345) j += i % 43; + return j; }" HAVE_OPENMP) else() check_c_source_compiles("int main(void) { - #pragma omp parallel - return 0; + #pragma omp for + for(int i = 0, j = 0; i != 42; i = 1 + i * 12345) j += i % 43; + return j; }" HAVE_OPENMP) endif() set(CMAKE_REQUIRED_FLAGS "") @@ -384,9 +386,13 @@ endif() if(CMAKE_CXX_COMPILER_LOADED) list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_11 HAS_CXX11) if(HAS_CXX11 LESS 0) - check_cxx_compiler_flag("-std=gnu++11" CXX_FALLBACK_GNU11) - if(NOT CXX_FALLBACK_GNU11) - check_cxx_compiler_flag("-std=c++11" CXX_FALLBACK_11) + if (MSVC) + check_cxx_compiler_flag("/std:c++11" CXX_FALLBACK_11) + else() + check_cxx_compiler_flag("-std=gnu++11" CXX_FALLBACK_GNU11) + if(NOT CXX_FALLBACK_GNU11) + check_cxx_compiler_flag("-std=c++11" CXX_FALLBACK_11) + endif() endif() endif() endif() diff --git a/mdbxdist/mdbx.c b/mdbxdist/mdbx.c index 4247d38..ef89892 100644 --- a/mdbxdist/mdbx.c +++ b/mdbxdist/mdbx.c @@ -3,7 +3,7 @@ #define xMDBX_ALLOY 1 /* alloyed build */ -#define MDBX_BUILD_SOURCERY 4ef6bfc2012bedf4af0bcd644ec87ace207f395c5d5e103573649032ec2cb6e8_v0_13_1_0_g5fc7a6b1 +#define MDBX_BUILD_SOURCERY 7a087912ed373fb7f849bc0a5b72a7d3cbdca30796a95e27dc05c7118e47dde9_v0_13_1_56_g10a93f4b #define LIBMDBX_INTERNALS @@ -19,6 +19,10 @@ #if (defined(MDBX_DEBUG) && MDBX_DEBUG > 0) || \ (defined(MDBX_FORCE_ASSERTIONS) && MDBX_FORCE_ASSERTIONS) #undef NDEBUG +#ifndef MDBX_DEBUG +/* Чтобы избежать включения отладки только из-за включения assert-проверок */ +#define MDBX_DEBUG 0 +#endif #endif /*----------------------------------------------------------------------------*/ @@ -1804,11 +1808,7 @@ osal_bswap32(uint32_t v) { /** Enables chunking long list of retired pages during huge transactions commit * to avoid use sequences of pages. */ #ifndef MDBX_ENABLE_BIGFOOT -#if MDBX_WORDBITS >= 64 || defined(DOXYGEN) #define MDBX_ENABLE_BIGFOOT 1 -#else -#define MDBX_ENABLE_BIGFOOT 0 -#endif #elif !(MDBX_ENABLE_BIGFOOT == 0 || MDBX_ENABLE_BIGFOOT == 1) #error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1 #endif /* MDBX_ENABLE_BIGFOOT */ @@ -6224,19 +6224,11 @@ MDBX_INTERNAL void dpl_release_shadows(MDBX_txn *txn); -#ifndef MDBX_ENABLE_GC_EXPERIMENTAL -#define MDBX_ENABLE_GC_EXPERIMENTAL 0 -#elif !(MDBX_ENABLE_GC_EXPERIMENTAL == 0 || MDBX_ENABLE_GC_EXPERIMENTAL == 1) -#error MDBX_ENABLE_GC_EXPERIMENTAL must be defined as 0 or 1 -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ - typedef struct gc_update_context { unsigned loop; pgno_t prev_first_unallocated; bool dense; -#if MDBX_ENABLE_GC_EXPERIMENTAL - intptr_t reserve_adj; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ + size_t reserve_adj; size_t retired_stored; size_t amount, reserved, cleaned_slot, reused_slot, fill_idx; txnid_t cleaned_id, rid; @@ -6251,7 +6243,7 @@ typedef struct gc_update_context { static inline int gc_update_init(MDBX_txn *txn, gcu_t *ctx) { memset(ctx, 0, offsetof(gcu_t, cursor)); - ctx->dense = txn->txnid < MIN_TXNID; + ctx->dense = txn->txnid <= MIN_TXNID; #if MDBX_ENABLE_BIGFOOT ctx->bigfoot = txn->txnid; #endif /* MDBX_ENABLE_BIGFOOT */ @@ -16442,12 +16434,19 @@ __hot int cursor_put_checklen(MDBX_cursor *mc, const MDBX_val *key, if (mc->tree->flags & MDBX_INTEGERDUP) { if (data->iov_len == 8) { if (unlikely(7 & (uintptr_t)data->iov_base)) { - if (unlikely(flags & MDBX_MULTIPLE)) - return MDBX_BAD_VALSIZE; - /* copy instead of return error to avoid break compatibility */ - aligned_data.iov_base = bcopy_8(&aligned_databytes, data->iov_base); - aligned_data.iov_len = data->iov_len; - data = &aligned_data; + if (unlikely(flags & MDBX_MULTIPLE)) { + /* LY: использование alignof(uint64_t) тут не подходил из-за ошибок + * MSVC и некоторых других компиляторов, когда для элементов + * массивов/векторов обеспечивает выравнивание только на 4-х байтовых + * границу и одновременно alignof(uint64_t) == 8. */ + if (MDBX_WORDBITS > 32 || (3 & (uintptr_t)data->iov_base) != 0) + return MDBX_BAD_VALSIZE; + } else { + /* copy instead of return error to avoid break compatibility */ + aligned_data.iov_base = bcopy_8(&aligned_databytes, data->iov_base); + aligned_data.iov_len = data->iov_len; + data = &aligned_data; + } } } else if (data->iov_len == 4) { if (unlikely(3 & (uintptr_t)data->iov_base)) { @@ -17049,9 +17048,16 @@ __hot int cursor_ops(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, rc = cursor_seek(mc, key, data, MDBX_SET).err; if (unlikely(rc != MDBX_SUCCESS)) return rc; + } else { + if (unlikely(is_eof(mc) || !inner_filled(mc))) + return MDBX_ENODATA; + cASSERT(mc, is_filled(mc)); + if (key) { + const page_t *mp = mc->pg[mc->top]; + const node_t *node = page_node(mp, mc->ki[mc->top]); + *key = get_key(node); + } } - if (unlikely(is_eof(mc) || !inner_filled(mc))) - return MDBX_ENODATA; goto fetch_multiple; case MDBX_NEXT_MULTIPLE: @@ -17064,7 +17070,7 @@ __hot int cursor_ops(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, return rc; else { fetch_multiple: - cASSERT(mc, is_filled(mc) && !inner_filled(mc)); + cASSERT(mc, is_filled(mc) && inner_filled(mc)); MDBX_cursor *mx = &mc->subcur->cursor; data->iov_len = page_numkeys(mx->pg[mx->top]) * mx->tree->dupfix_size; data->iov_base = page_data(mx->pg[mx->top]); @@ -17796,14 +17802,11 @@ static int dbi_open_locked(MDBX_txn *txn, unsigned user_flags, MDBX_dbi *dbi, } /* Done here so we cannot fail after creating a new DB */ - void *clone = nullptr; - if (name.iov_len) { - clone = osal_malloc(dbi_namelen(name)); - if (unlikely(!clone)) - return MDBX_ENOMEM; - name.iov_base = memcpy(clone, name.iov_base, name.iov_len); - } else - name.iov_base = ""; + defer_free_item_t *const clone = osal_malloc(dbi_namelen(name)); + if (unlikely(!clone)) + return MDBX_ENOMEM; + memcpy(clone, name.iov_base, name.iov_len); + name.iov_base = clone; uint8_t dbi_state = DBI_LINDO | DBI_VALID | DBI_FRESH; if (unlikely(rc)) { @@ -18183,8 +18186,53 @@ int mdbx_dbi_close(MDBX_env *env, MDBX_dbi dbi) { return MDBX_BAD_DBI; rc = osal_fastmutex_acquire(&env->dbi_lock); - if (likely(rc == MDBX_SUCCESS)) + if (likely(rc == MDBX_SUCCESS && dbi < env->n_dbi)) { + retry: + if (env->basal_txn && (env->dbs_flags[dbi] & DB_VALID) && + (env->basal_txn->flags & MDBX_TXN_FINISHED) == 0) { + /* LY: Опасный код, так как env->txn может быть изменено в другом потоке. + * К сожалению тут нет надежного решения и может быть падение при неверном + * использовании API (вызове mdbx_dbi_close конкурентно с завершением + * пишущей транзакции). + * + * Для минимизации вероятности падения сначала проверяем dbi-флаги + * в basal_txn, а уже после в env->txn. Таким образом, падение может быть + * только при коллизии с завершением вложенной транзакции. + * + * Альтернативно можно попробовать выполнять обновление/put записи в + * mainDb соответствующей таблице закрываемого хендла. Семантически это + * верный путь, но проблема в текущем API, в котором исторически dbi-хендл + * живет и закрывается вне транзакции. Причем проблема не только в том, + * что нет указателя на текущую пишущую транзакцию, а в том что + * пользователь точно не ожидает что закрытие хендла приведет к + * скрытой/непрозрачной активности внутри транзакции потенциально + * выполняемой в другом потоке. Другими словами, проблема может быть + * только при неверном использовании API и если пользователь это + * допускает, то точно не будет ожидать скрытых действий внутри + * транзакции, и поэтому этот путь потенциально более опасен. */ + const MDBX_txn *const hazard = env->txn; + osal_compiler_barrier(); + if ((dbi_state(env->basal_txn, dbi) & + (DBI_LINDO | DBI_DIRTY | DBI_CREAT)) > DBI_LINDO) { + bailout_dirty_dbi: + osal_fastmutex_release(&env->dbi_lock); + return MDBX_DANGLING_DBI; + } + osal_memory_barrier(); + if (unlikely(hazard != env->txn)) + goto retry; + if (hazard != env->basal_txn && hazard && + (hazard->flags & MDBX_TXN_FINISHED) == 0 && + hazard->signature == txn_signature && + (dbi_state(hazard, dbi) & (DBI_LINDO | DBI_DIRTY | DBI_CREAT)) > + DBI_LINDO) + goto bailout_dirty_dbi; + osal_compiler_barrier(); + if (unlikely(hazard != env->txn)) + goto retry; + } rc = defer_and_release(env, dbi_close_locked(env, dbi)); + } return rc; } @@ -19037,10 +19085,10 @@ __cold int dxb_resize(MDBX_env *const env, const pgno_t used_pgno, const void *const prev_map = env->dxb_mmap.base; #endif /* MDBX_ENABLE_MADVISE || ENABLE_MEMCHECK */ - VERBOSE("resize/%d datafile/mapping: " + VERBOSE("resize(env-flags 0x%x, mode %d) datafile/mapping: " "present %" PRIuPTR " -> %" PRIuPTR ", " "limit %" PRIuPTR " -> %" PRIuPTR, - mode, prev_size, size_bytes, prev_limit, limit_bytes); + env->flags, mode, prev_size, size_bytes, prev_limit, limit_bytes); eASSERT(env, limit_bytes >= size_bytes); eASSERT(env, bytes2pgno(env, size_bytes) >= size_pgno); @@ -23535,6 +23583,7 @@ static rid_t get_rid_for_reclaimed(MDBX_txn *txn, gcu_t *ctx, (MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) - ctx->reused_slot) * txn->env->maxgc_large1page) { if (unlikely(ctx->rid <= MIN_TXNID)) { + ctx->dense = true; if (unlikely(MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) <= ctx->reused_slot)) { NOTICE("** restart: reserve depleted (reused_gc_slot %zu >= " @@ -23561,10 +23610,10 @@ static rid_t get_rid_for_reclaimed(MDBX_txn *txn, gcu_t *ctx, goto return_error; } const txnid_t gc_first = unaligned_peek_u64(4, key.iov_base); - if (unlikely(gc_first <= MIN_TXNID)) { - DEBUG("%s: no free GC's id(s) less than %" PRIaTXN - " (going dense-mode)", - dbg_prefix(ctx), ctx->rid); + if (unlikely(gc_first <= INITIAL_TXNID)) { + NOTICE("%s: no free GC's id(s) less than %" PRIaTXN + " (going dense-mode)", + dbg_prefix(ctx), ctx->rid); ctx->dense = true; goto return_restart; } @@ -23671,19 +23720,11 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { txn->cursors[FREE_DBI] = &ctx->cursor; int rc; - // tASSERT(txn, MDBX_PNL_GETSIZE(txn->tw.retired_pages) || - // ctx->cleaned_slot < - // (txn->tw.gc.reclaimed ? - // MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) : 0) - // || ctx->cleaned_id < txn->tw.gc.last_reclaimed); - /* txn->tw.relist[] can grow and shrink during this call. * txn->tw.gc.last_reclaimed and txn->tw.retired_pages[] can only grow. * But page numbers cannot disappear from txn->tw.retired_pages[]. */ -#if MDBX_ENABLE_GC_EXPERIMENTAL retry_clean_adj: ctx->reserve_adj = 0; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ retry: ctx->loop += ctx->prev_first_unallocated == txn->geo.first_unallocated; TRACE(">> restart, loop %u", ctx->loop); @@ -23710,7 +23751,8 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { ctx->reserved = 0; ctx->cleaned_slot = 0; ctx->reused_slot = 0; - ctx->amount = ctx->fill_idx = ~0u; + ctx->amount = 0; + ctx->fill_idx = ~0u; ctx->cleaned_id = 0; ctx->rid = txn->tw.gc.last_reclaimed; while (true) { @@ -23827,9 +23869,7 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { env->maxgc_large1page / 2)) { TRACE("%s: reclaimed-list changed %zu -> %zu, retry", dbg_prefix(ctx), ctx->amount, MDBX_PNL_GETSIZE(txn->tw.relist)); -#if MDBX_ENABLE_GC_EXPERIMENTAL ctx->reserve_adj += ctx->reserved - MDBX_PNL_GETSIZE(txn->tw.relist); -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ goto retry; } ctx->amount = MDBX_PNL_GETSIZE(txn->tw.relist); @@ -23853,7 +23893,6 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { if (unlikely(rc != MDBX_SUCCESS)) goto bailout; } -#if MDBX_ENABLE_GC_EXPERIMENTAL const size_t left = ctx->amount - ctx->reserved - ctx->reserve_adj; TRACE("%s: amount %zu, reserved %zd, reserve_adj %zu, left %zd, " "lifo-reclaimed-slots %zu, " @@ -23861,15 +23900,6 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { dbg_prefix(ctx), ctx->amount, ctx->reserved, ctx->reserve_adj, left, txn->tw.gc.reclaimed ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) : 0, ctx->reused_slot); -#else - const size_t left = ctx->amount - ctx->reserved; - TRACE("%s: amount %zu, reserved %zd, left %zd, " - "lifo-reclaimed-slots %zu, " - "reused-gc-slots %zu", - dbg_prefix(ctx), ctx->amount, ctx->reserved, left, - txn->tw.gc.reclaimed ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) : 0, - ctx->reused_slot); -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ if (0 >= (intptr_t)left) break; @@ -23992,9 +24022,7 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { TRACE("%s", " >> filling"); /* Fill in the reserved records */ -#if MDBX_ENABLE_GC_EXPERIMENTAL size_t excess_slots = 0; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ ctx->fill_idx = txn->tw.gc.reclaimed ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) - ctx->reused_slot @@ -24005,15 +24033,17 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { tASSERT(txn, dpl_check(txn)); if (ctx->amount) { MDBX_val key, data; - key.iov_len = data.iov_len = 0; /* avoid MSVC warning */ + key.iov_len = data.iov_len = 0; key.iov_base = data.iov_base = nullptr; size_t left = ctx->amount, excess = 0; if (txn->tw.gc.reclaimed == nullptr) { tASSERT(txn, is_lifo(txn) == 0); rc = outer_first(&ctx->cursor, &key, &data); - if (unlikely(rc != MDBX_SUCCESS)) - goto bailout; + if (unlikely(rc != MDBX_SUCCESS)) { + if (rc != MDBX_NOTFOUND) + goto bailout; + } } else { tASSERT(txn, is_lifo(txn) != 0); } @@ -24024,36 +24054,29 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { MDBX_PNL_GETSIZE(txn->tw.relist)); if (txn->tw.gc.reclaimed == nullptr) { tASSERT(txn, is_lifo(txn) == 0); - fill_gc_id = unaligned_peek_u64(4, key.iov_base); + fill_gc_id = + key.iov_base ? unaligned_peek_u64(4, key.iov_base) : MIN_TXNID; if (ctx->fill_idx == 0 || fill_gc_id > txn->tw.gc.last_reclaimed) { -#if MDBX_ENABLE_GC_EXPERIMENTAL if (!left) break; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ NOTICE("** restart: reserve depleted (fill_idx %zu, fill_id %" PRIaTXN " > last_reclaimed %" PRIaTXN ", left %zu", ctx->fill_idx, fill_gc_id, txn->tw.gc.last_reclaimed, left); -#if MDBX_ENABLE_GC_EXPERIMENTAL ctx->reserve_adj = (ctx->reserve_adj > left) ? ctx->reserve_adj - left : 0; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ goto retry; } ctx->fill_idx -= 1; } else { tASSERT(txn, is_lifo(txn) != 0); if (ctx->fill_idx >= MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed)) { -#if MDBX_ENABLE_GC_EXPERIMENTAL if (!left) break; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ NOTICE("** restart: reserve depleted (fill_idx %zu >= " "gc.reclaimed %zu, left %zu", ctx->fill_idx, MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed), left); -#if MDBX_ENABLE_GC_EXPERIMENTAL ctx->reserve_adj = (ctx->reserve_adj > left) ? ctx->reserve_adj - left : 0; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ goto retry; } ctx->fill_idx += 1; @@ -24082,12 +24105,10 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { excess += delta; TRACE("%s: chunk %zu > left %zu, @%" PRIaTXN, dbg_prefix(ctx), chunk, left, fill_gc_id); -#if MDBX_ENABLE_GC_EXPERIMENTAL if (!left) { excess_slots += 1; goto next; } -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ if ((ctx->loop < 5 && delta > (ctx->loop / 2)) || delta > env->maxgc_large1page) data.iov_len = (left + 1) * sizeof(pgno_t); @@ -24103,10 +24124,8 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { NOTICE("** restart: reclaimed-list changed (%zu -> %zu, loose +%zu)", ctx->amount, MDBX_PNL_GETSIZE(txn->tw.relist), txn->tw.loose_count); -#if MDBX_ENABLE_GC_EXPERIMENTAL if (ctx->loop < 5 || (ctx->loop > 10 && (ctx->loop & 1))) goto retry_clean_adj; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ goto retry; } @@ -24142,23 +24161,16 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { goto bailout; } -#if MDBX_ENABLE_GC_EXPERIMENTAL next: -#else - if (left == 0) - break; -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ if (txn->tw.gc.reclaimed == nullptr) { tASSERT(txn, is_lifo(txn) == 0); rc = outer_next(&ctx->cursor, &key, &data, MDBX_NEXT); if (unlikely(rc != MDBX_SUCCESS)) { -#if MDBX_ENABLE_GC_EXPERIMENTAL if (rc == MDBX_NOTFOUND && !left) { rc = MDBX_SUCCESS; break; } -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ goto bailout; } } else { @@ -24167,14 +24179,12 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { } if (excess) { -#if MDBX_ENABLE_GC_EXPERIMENTAL size_t n = excess, adj = excess; while (n >= env->maxgc_large1page) adj -= n /= env->maxgc_large1page; ctx->reserve_adj += adj; TRACE("%s: extra %zu reserved space, adj +%zu (%zu)", dbg_prefix(ctx), excess, adj, ctx->reserve_adj); -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ } } @@ -24186,27 +24196,15 @@ int gc_update(MDBX_txn *txn, gcu_t *ctx) { goto retry; } -#if MDBX_ENABLE_GC_EXPERIMENTAL if (unlikely(excess_slots)) { const bool will_retry = ctx->loop < 5 || excess_slots > 1; NOTICE("** %s: reserve excess (excess-slots %zu, filled-slot %zu, adj %zu, " - "loop %zu)", + "loop %u)", will_retry ? "restart" : "ignore", excess_slots, ctx->fill_idx, ctx->reserve_adj, ctx->loop); if (will_retry) goto retry; } -#else - if (unlikely(ctx->fill_idx != (txn->tw.gc.reclaimed - ? MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed) - : 0))) { - const bool will_retry = ctx->loop < 9; - NOTICE("** %s: reserve excess (filled-idx %zu, loop %u)", - will_retry ? "restart" : "ignore", ctx->fill_idx, ctx->loop); - if (will_retry) - goto retry; - } -#endif /* MDBX_ENABLE_GC_EXPERIMENTAL */ tASSERT(txn, txn->tw.gc.reclaimed == nullptr || ctx->cleaned_slot == MDBX_PNL_GETSIZE(txn->tw.gc.reclaimed)); @@ -28888,13 +28886,14 @@ __extern_C void __assert2(const char *file, int line, const char *function, __assert2(file, line, function, assertion) #elif defined(__UCLIBC__) -__extern_C void __assert(const char *, const char *, unsigned, const char *) +MDBX_NORETURN __extern_C void __assert(const char *, const char *, unsigned, + const char *) #ifdef __THROW __THROW #else __nothrow #endif /* __THROW */ - MDBX_NORETURN; + ; #define __assert_fail(assertion, file, line, function) \ __assert(assertion, file, line, function) @@ -28902,14 +28901,15 @@ __extern_C void __assert(const char *, const char *, unsigned, const char *) /* workaround for avoid musl libc wrong prototype */ ( \ defined(__GLIBC__) || defined(__GNU_LIBRARY__)) /* Prototype should match libc runtime. ISO POSIX (2003) & LSB 1.x-3.x */ -__extern_C void __assert_fail(const char *assertion, const char *file, - unsigned line, const char *function) +MDBX_NORETURN __extern_C void __assert_fail(const char *assertion, + const char *file, unsigned line, + const char *function) #ifdef __THROW __THROW #else __nothrow #endif /* __THROW */ - MDBX_NORETURN; + ; #elif defined(__APPLE__) || defined(__MACH__) __extern_C void __assert_rtn(const char *function, const char *file, int line, @@ -28927,8 +28927,9 @@ __extern_C void __assert_rtn(const char *function, const char *file, int line, #define __assert_fail(assertion, file, line, function) \ __assert_rtn(function, file, line, assertion) #elif defined(__sun) || defined(__SVR4) || defined(__svr4__) -__extern_C void __assert_c99(const char *assection, const char *file, int line, - const char *function) MDBX_NORETURN; +MDBX_NORETURN __extern_C void __assert_c99(const char *assection, + const char *file, int line, + const char *function); #define __assert_fail(assertion, file, line, function) \ __assert_c99(assertion, file, line, function) #elif defined(__OpenBSD__) @@ -31503,7 +31504,9 @@ __cold MDBX_INTERNAL void osal_jitter(bool tiny) { for (;;) { #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ defined(__x86_64__) - const unsigned salt = 277u * (unsigned)__rdtsc(); + unsigned salt = 5296013u * (unsigned)__rdtsc(); + salt ^= salt >> 11; + salt *= 25810541u; #elif (defined(_WIN32) || defined(_WIN64)) && MDBX_WITHOUT_MSVC_CRT static ULONG state; const unsigned salt = (unsigned)RtlRandomEx(&state); @@ -31511,13 +31514,26 @@ __cold MDBX_INTERNAL void osal_jitter(bool tiny) { const unsigned salt = rand(); #endif - const unsigned coin = salt % (tiny ? 29u : 43u); + const int coin = salt % (tiny ? 29u : 43u); if (coin < 43 / 3) break; #if defined(_WIN32) || defined(_WIN64) - SwitchToThread(); - if (coin > 43 * 2 / 3) - Sleep(1); + if (coin < 43 * 2 / 3) + SwitchToThread(); + else { + static HANDLE timer; + if (!timer) + timer = CreateWaitableTimer(NULL, TRUE, NULL); + + LARGE_INTEGER ft; + ft.QuadPart = + coin * (int64_t)-10; // Convert to 100 nanosecond interval, + // negative value indicates relative time. + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + // CloseHandle(timer); + break; + } #else sched_yield(); if (coin > 43 * 2 / 3) @@ -32251,7 +32267,9 @@ __cold int mdbx_get_sysraminfo(intptr_t *page_size, intptr_t *total_pages, #include #endif /* FreeBSD */ -#if __GLIBC_PREREQ(2, 25) || defined(__FreeBSD__) || defined(__NetBSD__) || \ +#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED +#include +#elif __GLIBC_PREREQ(2, 25) || defined(__FreeBSD__) || defined(__NetBSD__) || \ defined(__BSD__) || defined(__bsdi__) || defined(__DragonFly__) || \ defined(__APPLE__) || __has_include() #include @@ -40499,9 +40517,9 @@ __dll_export 0, 13, 1, - 0, - {"2024-08-30T00:01:07+03:00", "4ad05c5f867a963162def46b68eff5f7130b81ca", "5fc7a6b1077794789b97bb2a56f5a4eb541a0bc0", - "v0.13.1-0-g5fc7a6b1"}, + 56, + {"2024-11-07T11:40:16+03:00", "b2d74d5eb64fcb73b35ed64508c8fcaeb3f513e1", "10a93f4b9f485e8f71592f4ec9016a69faa209c8", + "v0.13.1-56-g10a93f4b"}, sourcery}; __dll_export diff --git a/mdbxdist/mdbx.c++ b/mdbxdist/mdbx.c++ index 1fee9ed..61aac5e 100644 --- a/mdbxdist/mdbx.c++ +++ b/mdbxdist/mdbx.c++ @@ -2,7 +2,7 @@ /// \author Леонид Юрьев aka Leonid Yuriev \date 2015-2024 -#define MDBX_BUILD_SOURCERY 4ef6bfc2012bedf4af0bcd644ec87ace207f395c5d5e103573649032ec2cb6e8_v0_13_1_0_g5fc7a6b1 +#define MDBX_BUILD_SOURCERY 7a087912ed373fb7f849bc0a5b72a7d3cbdca30796a95e27dc05c7118e47dde9_v0_13_1_56_g10a93f4b #define LIBMDBX_INTERNALS @@ -18,6 +18,10 @@ #if (defined(MDBX_DEBUG) && MDBX_DEBUG > 0) || \ (defined(MDBX_FORCE_ASSERTIONS) && MDBX_FORCE_ASSERTIONS) #undef NDEBUG +#ifndef MDBX_DEBUG +/* Чтобы избежать включения отладки только из-за включения assert-проверок */ +#define MDBX_DEBUG 0 +#endif #endif /*----------------------------------------------------------------------------*/ @@ -1803,11 +1807,7 @@ osal_bswap32(uint32_t v) { /** Enables chunking long list of retired pages during huge transactions commit * to avoid use sequences of pages. */ #ifndef MDBX_ENABLE_BIGFOOT -#if MDBX_WORDBITS >= 64 || defined(DOXYGEN) #define MDBX_ENABLE_BIGFOOT 1 -#else -#define MDBX_ENABLE_BIGFOOT 0 -#endif #elif !(MDBX_ENABLE_BIGFOOT == 0 || MDBX_ENABLE_BIGFOOT == 1) #error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1 #endif /* MDBX_ENABLE_BIGFOOT */ diff --git a/mdbxdist/mdbx.h b/mdbxdist/mdbx.h index 9972e6b..2ae43a6 100644 --- a/mdbxdist/mdbx.h +++ b/mdbxdist/mdbx.h @@ -189,16 +189,43 @@ typedef mode_t mdbx_mode_t; #define __has_attribute(x) (0) #endif /* __has_attribute */ +#ifndef __has_c_attribute +#define __has_c_attribute(x) (0) +#endif /* __has_c_attribute */ + #ifndef __has_cpp_attribute #define __has_cpp_attribute(x) 0 #endif /* __has_cpp_attribute */ +#ifndef __has_CXX_attribute +#if defined(__cplusplus) && \ + (!defined(_MSC_VER) || defined(__clang__) || _MSC_VER >= 1942) +#define __has_CXX_attribute(x) __has_cpp_attribute(x) +#else +#define __has_CXX_attribute(x) 0 +#endif +#endif /* __has_CXX_attribute */ + +#ifndef __has_C23_or_CXX_attribute +#if defined(__cplusplus) +#define __has_C23_or_CXX_attribute(x) __has_CXX_attribute(x) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ > 202311L +#define __has_C23_or_CXX_attribute(x) __has_c_attribute(x) +#else +#define __has_C23_or_CXX_attribute(x) 0 +#endif +#endif /* __has_C23_or_CXX_attribute */ + #ifndef __has_feature #define __has_feature(x) (0) +#define __has_exceptions_disabled (0) +#else +#define __has_exceptions_disabled \ + (__has_feature(cxx_noexcept) && !__has_feature(cxx_exceptions)) #endif /* __has_feature */ #ifndef __has_extension -#define __has_extension(x) (0) +#define __has_extension(x) __has_feature(x) #endif /* __has_extension */ #ifndef __has_builtin @@ -213,15 +240,14 @@ typedef mode_t mdbx_mode_t; * These functions should be declared with the attribute pure. */ #if defined(DOXYGEN) #define MDBX_PURE_FUNCTION [[gnu::pure]] +#elif __has_C23_or_CXX_attribute(gnu::pure) && \ + (!defined(__apple_build_version__) || !defined(__clang_major__) || \ + __clang_major__ > 17) +#define MDBX_PURE_FUNCTION [[gnu::pure]] #elif (defined(__GNUC__) || __has_attribute(__pure__)) && \ - (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ - || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) + (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ || \ + !defined(__cplusplus) || __has_exceptions_disabled) #define MDBX_PURE_FUNCTION __attribute__((__pure__)) -#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 -#define MDBX_PURE_FUNCTION -#elif defined(__cplusplus) && __has_cpp_attribute(gnu::pure) && \ - (!defined(__clang__) || !__has_feature(cxx_exceptions)) -#define MDBX_PURE_FUNCTION [[gnu::pure]] #else #define MDBX_PURE_FUNCTION #endif /* MDBX_PURE_FUNCTION */ @@ -231,22 +257,16 @@ typedef mode_t mdbx_mode_t; * that is compatible to CLANG and proposed [[pure]]. */ #if defined(DOXYGEN) #define MDBX_NOTHROW_PURE_FUNCTION [[gnu::pure, gnu::nothrow]] -#elif defined(__GNUC__) || \ - (__has_attribute(__pure__) && __has_attribute(__nothrow__)) -#define MDBX_NOTHROW_PURE_FUNCTION __attribute__((__pure__, __nothrow__)) -#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 -#if __has_cpp_attribute(pure) -#define MDBX_NOTHROW_PURE_FUNCTION [[pure]] -#else -#define MDBX_NOTHROW_PURE_FUNCTION -#endif -#elif defined(__cplusplus) && __has_cpp_attribute(gnu::pure) -#if __has_cpp_attribute(gnu::nothrow) +#elif __has_C23_or_CXX_attribute(gnu::pure) +#if __has_C23_or_CXX_attribute(gnu::nothrow) #define MDBX_NOTHROW_PURE_FUNCTION [[gnu::pure, gnu::nothrow]] #else #define MDBX_NOTHROW_PURE_FUNCTION [[gnu::pure]] #endif -#elif defined(__cplusplus) && __has_cpp_attribute(pure) +#elif defined(__GNUC__) || \ + (__has_attribute(__pure__) && __has_attribute(__nothrow__)) +#define MDBX_NOTHROW_PURE_FUNCTION __attribute__((__pure__, __nothrow__)) +#elif __has_CXX_attribute(pure) #define MDBX_NOTHROW_PURE_FUNCTION [[pure]] #else #define MDBX_NOTHROW_PURE_FUNCTION @@ -264,15 +284,14 @@ typedef mode_t mdbx_mode_t; * It does not make sense for a const function to return void. */ #if defined(DOXYGEN) #define MDBX_CONST_FUNCTION [[gnu::const]] -#elif (defined(__GNUC__) || __has_attribute(__pure__)) && \ - (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ - || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) -#define MDBX_CONST_FUNCTION __attribute__((__const__)) -#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 -#define MDBX_CONST_FUNCTION MDBX_PURE_FUNCTION -#elif defined(__cplusplus) && __has_cpp_attribute(gnu::const) && \ - (!defined(__clang__) || !__has_feature(cxx_exceptions)) +#elif __has_C23_or_CXX_attribute(gnu::const) && \ + (!defined(__apple_build_version__) || !defined(__clang_major__) || \ + __clang_major__ > 17) #define MDBX_CONST_FUNCTION [[gnu::const]] +#elif (defined(__GNUC__) || __has_attribute(__const__)) && \ + (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ || \ + !defined(__cplusplus) || __has_exceptions_disabled) +#define MDBX_CONST_FUNCTION __attribute__((__const__)) #else #define MDBX_CONST_FUNCTION MDBX_PURE_FUNCTION #endif /* MDBX_CONST_FUNCTION */ @@ -282,18 +301,16 @@ typedef mode_t mdbx_mode_t; * that is compatible to CLANG and future [[const]]. */ #if defined(DOXYGEN) #define MDBX_NOTHROW_CONST_FUNCTION [[gnu::const, gnu::nothrow]] +#elif __has_C23_or_CXX_attribute(gnu::const) +#if __has_C23_or_CXX_attribute(gnu::nothrow) +#define MDBX_NOTHROW_CONST_FUNCTION [[gnu::const, gnu::nothrow]] +#else +#define MDBX_NOTHROW_CONST_FUNCTION [[gnu::const]] +#endif #elif defined(__GNUC__) || \ (__has_attribute(__const__) && __has_attribute(__nothrow__)) #define MDBX_NOTHROW_CONST_FUNCTION __attribute__((__const__, __nothrow__)) -#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 -#define MDBX_NOTHROW_CONST_FUNCTION MDBX_NOTHROW_PURE_FUNCTION -#elif defined(__cplusplus) && __has_cpp_attribute(gnu::const) -#if __has_cpp_attribute(gnu::nothrow) -#define MDBX_NOTHROW_PURE_FUNCTION [[gnu::const, gnu::nothrow]] -#else -#define MDBX_NOTHROW_PURE_FUNCTION [[gnu::const]] -#endif -#elif defined(__cplusplus) && __has_cpp_attribute(const) +#elif __has_CXX_attribute(const) #define MDBX_NOTHROW_CONST_FUNCTION [[const]] #else #define MDBX_NOTHROW_CONST_FUNCTION MDBX_NOTHROW_PURE_FUNCTION @@ -612,7 +629,7 @@ extern "C" { #define MDBX_VERSION_MINOR 13 #ifndef LIBMDBX_API -#if defined(LIBMDBX_EXPORTS) +#if defined(LIBMDBX_EXPORTS) || defined(DOXYGEN) #define LIBMDBX_API __dll_export #elif defined(LIBMDBX_IMPORTS) #define LIBMDBX_API __dll_import @@ -622,7 +639,7 @@ extern "C" { #endif /* LIBMDBX_API */ #ifdef __cplusplus -#if defined(__clang__) || __has_attribute(type_visibility) +#if defined(__clang__) || __has_attribute(type_visibility) || defined(DOXYGEN) #define LIBMDBX_API_TYPE LIBMDBX_API __attribute__((type_visibility("default"))) #else #define LIBMDBX_API_TYPE LIBMDBX_API @@ -1996,7 +2013,7 @@ typedef enum MDBX_error { MDBX_DUPLICATED_CLK = -30413, /** Some cursors and/or other resources should be closed before table or - * corresponding DBI-handle could be (re)used */ + * corresponding DBI-handle could be (re)used and/or closed. */ MDBX_DANGLING_DBI = -30412, /** The parked read transaction was outed for the sake of @@ -2542,6 +2559,11 @@ LIBMDBX_API int mdbx_env_open(MDBX_env *env, const char *pathname, * \see mdbx_env_open() */ LIBMDBX_API int mdbx_env_openW(MDBX_env *env, const wchar_t *pathname, MDBX_env_flags_t flags, mdbx_mode_t mode); +#define mdbx_env_openT(env, pathname, flags, mode) \ + mdbx_env_openW(env, pathname, flags, mode) +#else +#define mdbx_env_openT(env, pathname, flags, mode) \ + mdbx_env_open(env, pathname, flags, mode) #endif /* Windows */ /** \brief Deletion modes for \ref mdbx_env_delete(). @@ -2592,6 +2614,9 @@ LIBMDBX_API int mdbx_env_delete(const char *pathname, * \see mdbx_env_delete() */ LIBMDBX_API int mdbx_env_deleteW(const wchar_t *pathname, MDBX_env_delete_mode_t mode); +#define mdbx_env_deleteT(pathname, mode) mdbx_env_deleteW(pathname, mode) +#else +#define mdbx_env_deleteT(pathname, mode) mdbx_env_delete(pathname, mode) #endif /* Windows */ /** \brief Copy an MDBX environment to the specified path, with options. @@ -2713,6 +2738,7 @@ LIBMDBX_API int mdbx_txn_copy2pathname(MDBX_txn *txn, const char *dest, * \see mdbx_env_copy() */ LIBMDBX_API int mdbx_env_copyW(MDBX_env *env, const wchar_t *dest, MDBX_copy_flags_t flags); +#define mdbx_env_copyT(env, dest, flags) mdbx_env_copyW(env, dest, flags) /** \copydoc mdbx_txn_copy2pathname() * \ingroup c_extra @@ -2720,6 +2746,12 @@ LIBMDBX_API int mdbx_env_copyW(MDBX_env *env, const wchar_t *dest, * \see mdbx_txn_copy2pathname() */ LIBMDBX_API int mdbx_txn_copy2pathnameW(MDBX_txn *txn, const wchar_t *dest, MDBX_copy_flags_t flags); +#define mdbx_txn_copy2pathnameT(txn, dest, flags) \ + mdbx_txn_copy2pathnameW(txn, dest, path) +#else +#define mdbx_env_copyT(env, dest, flags) mdbx_env_copy(env, dest, flags) +#define mdbx_txn_copy2pathnameT(txn, dest, flags) \ + mdbx_txn_copy2pathname(txn, dest, path) #endif /* Windows */ /** \brief Copy an environment to the specified file descriptor, with @@ -3386,6 +3418,9 @@ LIBMDBX_API int mdbx_env_get_path(const MDBX_env *env, const char **dest); * \note Available only on Windows. * \see mdbx_env_get_path() */ LIBMDBX_API int mdbx_env_get_pathW(const MDBX_env *env, const wchar_t **dest); +#define mdbx_env_get_pathT(env, dest) mdbx_env_get_pathW(env, dest) +#else +#define mdbx_env_get_pathT(env, dest) mdbx_env_get_path(env, dest) #endif /* Windows */ /** \brief Return the file descriptor for the given environment. @@ -3848,7 +3883,7 @@ mdbx_env_get_maxvalsize_ex(const MDBX_env *env, MDBX_db_flags_t flags); /** \deprecated Please use \ref mdbx_env_get_maxkeysize_ex() * and/or \ref mdbx_env_get_maxvalsize_ex() * \ingroup c_statinfo */ -MDBX_DEPRECATED MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int +MDBX_NOTHROW_PURE_FUNCTION MDBX_DEPRECATED LIBMDBX_API int mdbx_env_get_maxkeysize(const MDBX_env *env); /** \brief Returns maximal size of key-value pair to fit in a single page @@ -4887,9 +4922,12 @@ LIBMDBX_INLINE_API(int, mdbx_dbi_flags, * \ref mdbx_env_set_maxdbs(), unless that value would be large. * * \note Use with care. - * This call is synchronized via mutex with \ref mdbx_dbi_close(), but NOT with - * other transactions running by other threads. The "next" version of libmdbx - * (\ref MithrilDB) will solve this issue. + * This call is synchronized via mutex with \ref mdbx_dbi_open(), but NOT with + * any transaction(s) running by other thread(s). + * So the `mdbx_dbi_close()` MUST NOT be called in-parallel/concurrently + * with any transactions using the closing dbi-handle, nor during other thread + * commit/abort a write transacton(s). The "next" version of libmdbx (\ref + * MithrilDB) will solve this issue. * * Handles should only be closed if no other threads are going to reference * the table handle or one of its cursors any further. Do not close a handle @@ -6406,6 +6444,11 @@ LIBMDBX_API int mdbx_env_open_for_recoveryW(MDBX_env *env, const wchar_t *pathname, unsigned target_meta, bool writeable); +#define mdbx_env_open_for_recoveryT(env, pathname, target_mets, writeable) \ + mdbx_env_open_for_recoveryW(env, pathname, target_mets, writeable) +#else +#define mdbx_env_open_for_recoveryT(env, pathname, target_mets, writeable) \ + mdbx_env_open_for_recovery(env, pathname, target_mets, writeable) #endif /* Windows */ /** \brief Turn database to the specified meta-page. @@ -6455,6 +6498,11 @@ LIBMDBX_API int mdbx_preopen_snapinfo(const char *pathname, MDBX_envinfo *info, * \see mdbx_preopen_snapinfo() */ LIBMDBX_API int mdbx_preopen_snapinfoW(const wchar_t *pathname, MDBX_envinfo *info, size_t bytes); +#define mdbx_preopen_snapinfoT(pathname, info, bytes) \ + mdbx_preopen_snapinfoW(pathname, info, bytes) +#else +#define mdbx_preopen_snapinfoT(pathname, info, bytes) \ + mdbx_preopen_snapinfo(pathname, info, bytes) #endif /* Windows */ /** \brief Флаги/опции для проверки целостности базы данных. diff --git a/mdbxdist/mdbx.h++ b/mdbxdist/mdbx.h++ index 9c4101f..bd139ad 100644 --- a/mdbxdist/mdbx.h++ +++ b/mdbxdist/mdbx.h++ @@ -924,7 +924,7 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { /// \param [in] ignore_spaces If `true` function will skips spaces surrounding /// (before, between and after) a encoded bytes. However, spaces should not /// break a pair of characters encoding a single byte. - inline MDBX_NOTHROW_PURE_FUNCTION bool + MDBX_NOTHROW_PURE_FUNCTION inline bool is_hex(bool ignore_spaces = false) const noexcept; /// \brief Checks whether the content of the slice is a @@ -932,7 +932,7 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { /// \param [in] ignore_spaces If `true` function will skips spaces surrounding /// (before, between and after) a encoded bytes. However, spaces should not /// break a code group of characters. - inline MDBX_NOTHROW_PURE_FUNCTION bool + MDBX_NOTHROW_PURE_FUNCTION inline bool is_base58(bool ignore_spaces = false) const noexcept; /// \brief Checks whether the content of the slice is a @@ -940,7 +940,7 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val { /// \param [in] ignore_spaces If `true` function will skips spaces surrounding /// (before, between and after) a encoded bytes. However, spaces should not /// break a code group of characters. - inline MDBX_NOTHROW_PURE_FUNCTION bool + MDBX_NOTHROW_PURE_FUNCTION inline bool is_base64(bool ignore_spaces = false) const noexcept; inline void swap(slice &other) noexcept; @@ -1736,17 +1736,17 @@ private: return capacity_bytes < sizeof(bin); } - enum : byte { lastbyte_inplace_signature = byte(~0u) }; + enum : byte { lastbyte_inplace_signature = byte(~byte(0)) }; enum : size_t { inplace_signature_limit = size_t(lastbyte_inplace_signature) << (sizeof(size_t /* allocated::capacity_bytes_ */) - 1) * CHAR_BIT }; - constexpr byte lastbyte() const noexcept { + constexpr byte inplace_lastbyte() const noexcept { return inplace_[sizeof(bin) - 1]; } - MDBX_CXX17_CONSTEXPR byte &lastbyte() noexcept { + MDBX_CXX17_CONSTEXPR byte &inplace_lastbyte() noexcept { return inplace_[sizeof(bin) - 1]; } @@ -1758,7 +1758,7 @@ private: (std::numeric_limits::max() >> CHAR_BIT) == inplace_signature_limit, "WTF?"); - return lastbyte() == lastbyte_inplace_signature; + return inplace_lastbyte() == lastbyte_inplace_signature; } constexpr bool is_allocated() const noexcept { return !is_inplace(); } @@ -1772,7 +1772,7 @@ private: if (::std::is_trivial::value) /* workaround for "uninitialized" warning from some compilers */ memset(&allocated_.ptr_, 0, sizeof(allocated_.ptr_)); - lastbyte() = lastbyte_inplace_signature; + inplace_lastbyte() = lastbyte_inplace_signature; MDBX_CONSTEXPR_ASSERT(is_inplace() && address() == inplace_ && is_suitable_for_inplace(capacity())); return address(); @@ -3486,8 +3486,8 @@ MDBX_CXX01_CONSTEXPR_ENUM bool is_msgpack(value_mode mode) noexcept { return mode == value_mode::msgpack; } -/// \brief A handle for an individual database (key-value spaces) in the -/// environment. +/// \brief A handle for an individual table (aka key-value space, maps or +/// sub-database) in the environment. /// \see txn::open_map() \see txn::create_map() /// \see txn::clear_map() \see txn::drop_map() /// \see txn::get_handle_info() \see txn::get_map_stat() @@ -3537,8 +3537,9 @@ enum put_mode { /// instances, but does not destroys the represented underlying object from the /// own class destructor. /// -/// An environment supports multiple key-value tables (aka key-value -/// maps, spaces or sub-databases), all residing in the same shared-memory map. +/// An environment supports multiple key-value tables (aka key-value maps, +/// spaces or sub-databases), all residing in the same shared-memory mapped +/// file. class LIBMDBX_API_TYPE env { friend class txn; @@ -3569,22 +3570,26 @@ public: /// create_parameters &, const operate_parameters &, bool accede) struct LIBMDBX_API_TYPE geometry { - enum : int64_t { + enum : intptr_t { default_value = -1, ///< Means "keep current or use default" minimal_value = 0, ///< Means "minimal acceptable" maximal_value = INTPTR_MAX, ///< Means "maximal acceptable" - kB = 1000, ///< \f$10^{3}\f$ bytes - MB = kB * 1000, ///< \f$10^{6}\f$ bytes - GB = MB * 1000, ///< \f$10^{9}\f$ bytes - TB = GB * 1000, ///< \f$10^{12}\f$ bytes - PB = TB * 1000, ///< \f$10^{15}\f$ bytes - EB = PB * 1000, ///< \f$10^{18}\f$ bytes - KiB = 1024, ///< \f$2^{10}\f$ bytes - MiB = KiB << 10, ///< \f$2^{20}\f$ bytes - GiB = MiB << 10, ///< \f$2^{30}\f$ bytes - TiB = GiB << 10, ///< \f$2^{40}\f$ bytes - PiB = TiB << 10, ///< \f$2^{50}\f$ bytes - EiB = PiB << 10, ///< \f$2^{60}\f$ bytes + kB = 1000, ///< \f$10^{3}\f$ bytes (0x03E8) + MB = kB * 1000, ///< \f$10^{6}\f$ bytes (0x000F_4240) + GB = MB * 1000, ///< \f$10^{9}\f$ bytes (0x3B9A_CA00) +#if INTPTR_MAX > 0x7fffFFFFl + TB = GB * 1000, ///< \f$10^{12}\f$ bytes (0x0000_00E8_D4A5_1000) + PB = TB * 1000, ///< \f$10^{15}\f$ bytes (0x0003_8D7E_A4C6_8000) + EB = PB * 1000, ///< \f$10^{18}\f$ bytes (0x0DE0_B6B3_A764_0000) +#endif /* 64-bit intptr_t */ + KiB = 1024, ///< \f$2^{10}\f$ bytes (0x0400) + MiB = KiB << 10, ///< \f$2^{20}\f$ bytes (0x0010_0000) + GiB = MiB << 10, ///< \f$2^{30}\f$ bytes (0x4000_0000) +#if INTPTR_MAX > 0x7fffFFFFl + TiB = GiB << 10, ///< \f$2^{40}\f$ bytes (0x0000_0100_0000_0000) + PiB = TiB << 10, ///< \f$2^{50}\f$ bytes (0x0004_0000_0000_0000) + EiB = PiB << 10, ///< \f$2^{60}\f$ bytes (0x1000_0000_0000_0000) +#endif /* 64-bit intptr_t */ }; /// \brief Tagged type for output to std::ostream @@ -3699,7 +3704,7 @@ public: /// \brief Operate parameters. struct LIBMDBX_API_TYPE operate_parameters { - /// \brief The maximum number of named databases for the environment. + /// \brief The maximum number of named tables/maps for the environment. /// Zero means default value. unsigned max_maps{0}; /// \brief The maximum number of threads/reader slots for the environment. @@ -3774,24 +3779,24 @@ public: /// \brief Returns the maximal database size in bytes for specified page /// size. static inline size_t dbsize_max(intptr_t pagesize); - /// \brief Returns the minimal key size in bytes for specified database + /// \brief Returns the minimal key size in bytes for specified table /// flags. static inline size_t key_min(MDBX_db_flags_t flags) noexcept; /// \brief Returns the minimal key size in bytes for specified keys mode. static inline size_t key_min(key_mode mode) noexcept; /// \brief Returns the maximal key size in bytes for specified page size and - /// database flags. + /// table flags. static inline size_t key_max(intptr_t pagesize, MDBX_db_flags_t flags); /// \brief Returns the maximal key size in bytes for specified page size and /// keys mode. static inline size_t key_max(intptr_t pagesize, key_mode mode); /// \brief Returns the maximal key size in bytes for given environment and - /// database flags. + /// table flags. static inline size_t key_max(const env &, MDBX_db_flags_t flags); /// \brief Returns the maximal key size in bytes for given environment and /// keys mode. static inline size_t key_max(const env &, key_mode mode); - /// \brief Returns the minimal values size in bytes for specified database + /// \brief Returns the minimal values size in bytes for specified table /// flags. static inline size_t value_min(MDBX_db_flags_t flags) noexcept; /// \brief Returns the minimal values size in bytes for specified values @@ -3799,41 +3804,41 @@ public: static inline size_t value_min(value_mode) noexcept; /// \brief Returns the maximal value size in bytes for specified page size - /// and database flags. + /// and table flags. static inline size_t value_max(intptr_t pagesize, MDBX_db_flags_t flags); /// \brief Returns the maximal value size in bytes for specified page size /// and values mode. static inline size_t value_max(intptr_t pagesize, value_mode); /// \brief Returns the maximal value size in bytes for given environment and - /// database flags. + /// table flags. static inline size_t value_max(const env &, MDBX_db_flags_t flags); /// \brief Returns the maximal value size in bytes for specified page size /// and values mode. static inline size_t value_max(const env &, value_mode); /// \brief Returns maximal size of key-value pair to fit in a single page - /// for specified size and database flags. + /// for specified size and table flags. static inline size_t pairsize4page_max(intptr_t pagesize, MDBX_db_flags_t flags); /// \brief Returns maximal size of key-value pair to fit in a single page /// for specified page size and values mode. static inline size_t pairsize4page_max(intptr_t pagesize, value_mode); /// \brief Returns maximal size of key-value pair to fit in a single page - /// for given environment and database flags. + /// for given environment and table flags. static inline size_t pairsize4page_max(const env &, MDBX_db_flags_t flags); /// \brief Returns maximal size of key-value pair to fit in a single page /// for specified page size and values mode. static inline size_t pairsize4page_max(const env &, value_mode); /// \brief Returns maximal data size in bytes to fit in a leaf-page or - /// single large/overflow-page for specified size and database flags. + /// single large/overflow-page for specified size and table flags. static inline size_t valsize4page_max(intptr_t pagesize, MDBX_db_flags_t flags); /// \brief Returns maximal data size in bytes to fit in a leaf-page or /// single large/overflow-page for specified page size and values mode. static inline size_t valsize4page_max(intptr_t pagesize, value_mode); /// \brief Returns maximal data size in bytes to fit in a leaf-page or - /// single large/overflow-page for given environment and database flags. + /// single large/overflow-page for given environment and table flags. static inline size_t valsize4page_max(const env &, MDBX_db_flags_t flags); /// \brief Returns maximal data size in bytes to fit in a leaf-page or /// single large/overflow-page for specified page size and values mode. @@ -3960,7 +3965,7 @@ public: /// \see extra_runtime_option::max_readers inline unsigned max_readers() const; - /// \brief Returns the maximum number of named databases for the environment. + /// \brief Returns the maximum number of named tables for the environment. /// \see extra_runtime_option::max_maps inline unsigned max_maps() const; @@ -4104,7 +4109,7 @@ public: /// \brief Close a key-value map (aka table) handle. Normally /// unnecessary. /// - /// Closing a database handle is not necessary, but lets \ref txn::open_map() + /// Closing a table handle is not necessary, but lets \ref txn::open_map() /// reuse the handle value. Usually it's better to set a bigger /// \ref env::operate_parameters::max_maps, unless that value would be /// large. @@ -4115,8 +4120,8 @@ public: /// of libmdbx (\ref MithrilDB) will solve this issue. /// /// Handles should only be closed if no other threads are going to reference - /// the database handle or one of its cursors any further. Do not close a - /// handle if an existing transaction has modified its database. Doing so can + /// the table handle or one of its cursors any further. Do not close a + /// handle if an existing transaction has modified its table. Doing so can /// cause misbehavior from database corruption to errors like /// \ref MDBX_BAD_DBI (since the DB name is gone). inline void close_map(const map_handle &); @@ -4205,8 +4210,8 @@ public: /// object from the own class destructor, but disallows copying and assignment /// for instances. /// -/// An environment supports multiple key-value databases (aka key-value spaces -/// or tables), all residing in the same shared-memory map. +/// An environment supports multiple key-value tables (aka key-value spaces +/// or maps), all residing in the same shared-memory mapped file. class LIBMDBX_API_TYPE env_managed : public env { using inherited = env; /// delegated constructor for RAII @@ -4262,7 +4267,7 @@ public: /// \brief Explicitly closes the environment and release the memory map. /// - /// Only a single thread may call this function. All transactions, databases, + /// Only a single thread may call this function. All transactions, tables, /// and cursors must already be closed before calling this function. Attempts /// to use any such handles after calling this function will cause a /// `SIGSEGV`. The environment handle will be freed and must not be used again @@ -4522,7 +4527,7 @@ public: /// \brief Returns statistics for a table. inline map_stat get_map_stat(map_handle map) const; /// \brief Returns depth (bitmask) information of nested dupsort (multi-value) - /// B+trees for given database. + /// B+trees for given table. inline uint32_t get_tree_deepmask(map_handle map) const; /// \brief Returns information about key-value map (aka table) handle. inline map_handle::info get_handle_info(map_handle map) const; @@ -4571,11 +4576,11 @@ public: /// multimap (aka table). inline slice get(map_handle map, slice key, size_t &values_count, const slice &value_at_absence) const; - /// \brief Get value for equal or great key from a database. + /// \brief Get value for equal or great key from a table. /// \return Bundle of key-value pair and boolean flag, /// which will be `true` if the exact key was found and `false` otherwise. inline pair_result get_equal_or_great(map_handle map, const slice &key) const; - /// \brief Get value for equal or great key from a database. + /// \brief Get value for equal or great key from a table. /// \return Bundle of key-value pair and boolean flag, /// which will be `true` if the exact key was found and `false` otherwise. inline pair_result get_equal_or_great(map_handle map, const slice &key, @@ -4668,25 +4673,26 @@ public: return append(map, kv.key, kv.value, multivalue_order_preserved); } - size_t put_multiple(map_handle map, const slice &key, - const size_t value_length, const void *values_array, - size_t values_count, put_mode mode, - bool allow_partial = false); + size_t put_multiple_samelength(map_handle map, const slice &key, + const size_t value_length, + const void *values_array, size_t values_count, + put_mode mode, bool allow_partial = false); template - size_t put_multiple(map_handle map, const slice &key, - const VALUE *values_array, size_t values_count, - put_mode mode, bool allow_partial = false) { + size_t put_multiple_samelength(map_handle map, const slice &key, + const VALUE *values_array, size_t values_count, + put_mode mode, bool allow_partial = false) { static_assert(::std::is_standard_layout::value && !::std::is_pointer::value && !::std::is_array::value, "Must be a standard layout type!"); - return put_multiple(map, key, sizeof(VALUE), values_array, values_count, - mode, allow_partial); + return put_multiple_samelength(map, key, sizeof(VALUE), values_array, + values_count, mode, allow_partial); } template - void put_multiple(map_handle map, const slice &key, - const ::std::vector &vector, put_mode mode) { - put_multiple(map, key, vector.data(), vector.size(), mode); + void put_multiple_samelength(map_handle map, const slice &key, + const ::std::vector &vector, + put_mode mode) { + put_multiple_samelength(map, key, vector.data(), vector.size(), mode); } inline ptrdiff_t estimate(map_handle map, const pair &from, @@ -4866,6 +4872,10 @@ public: pair_exact = pair_equal, pair_greater_or_equal = MDBX_TO_PAIR_GREATER_OR_EQUAL, pair_greater_than = MDBX_TO_PAIR_GREATER_THAN, + + batch_samelength = MDBX_GET_MULTIPLE, + batch_samelength_next = MDBX_NEXT_MULTIPLE, + batch_samelength_previous = MDBX_PREV_MULTIPLE }; struct move_result : public pair_result { @@ -5131,6 +5141,23 @@ public: const slice &value, bool throw_notfound = false); + inline move_result get_multiple_samelength(const slice &key, + bool throw_notfound = true) { + return move(batch_samelength, key, throw_notfound); + } + + inline move_result get_multiple_samelength(bool throw_notfound = false) { + return move(batch_samelength, throw_notfound); + } + + inline move_result next_multiple_samelength(bool throw_notfound = false) { + return move(batch_samelength_next, throw_notfound); + } + + inline move_result previous_multiple_samelength(bool throw_notfound = false) { + return move(batch_samelength_previous, throw_notfound); + } + inline bool eof() const; inline bool on_first() const; inline bool on_last() const; @@ -5853,17 +5880,17 @@ slice::base64_decode(bool ignore_spaces, const ALLOCATOR &allocator) const { .as_buffer(allocator); } -inline MDBX_NOTHROW_PURE_FUNCTION bool +MDBX_NOTHROW_PURE_FUNCTION inline bool slice::is_hex(bool ignore_spaces) const noexcept { return !from_hex(*this, ignore_spaces).is_erroneous(); } -inline MDBX_NOTHROW_PURE_FUNCTION bool +MDBX_NOTHROW_PURE_FUNCTION inline bool slice::is_base58(bool ignore_spaces) const noexcept { return !from_base58(*this, ignore_spaces).is_erroneous(); } -inline MDBX_NOTHROW_PURE_FUNCTION bool +MDBX_NOTHROW_PURE_FUNCTION inline bool slice::is_base64(bool ignore_spaces) const noexcept { return !from_base64(*this, ignore_spaces).is_erroneous(); } @@ -6912,10 +6939,11 @@ inline void txn::append(map_handle map, const slice &key, const slice &value, : MDBX_APPEND)); } -inline size_t txn::put_multiple(map_handle map, const slice &key, - const size_t value_length, - const void *values_array, size_t values_count, - put_mode mode, bool allow_partial) { +inline size_t txn::put_multiple_samelength(map_handle map, const slice &key, + const size_t value_length, + const void *values_array, + size_t values_count, put_mode mode, + bool allow_partial) { MDBX_val args[2] = {{const_cast(values_array), value_length}, {nullptr, values_count}}; const int err = ::mdbx_put(handle_, map.dbi, const_cast(&key), args, @@ -7099,6 +7127,11 @@ inline cursor::move_result cursor::lower_bound(const slice &key, return move(key_lowerbound, key, throw_notfound); } +inline cursor::move_result cursor::upper_bound(const slice &key, + bool throw_notfound) { + return move(key_greater_than, key, throw_notfound); +} + inline cursor::move_result cursor::find_multivalue(const slice &key, const slice &value, bool throw_notfound) { @@ -7111,6 +7144,12 @@ inline cursor::move_result cursor::lower_bound_multivalue(const slice &key, return move(multi_exactkey_lowerboundvalue, key, value, throw_notfound); } +inline cursor::move_result cursor::upper_bound_multivalue(const slice &key, + const slice &value, + bool throw_notfound) { + return move(multi_exactkey_value_greater, key, value, throw_notfound); +} + inline bool cursor::seek(const slice &key) { return move(seek_key, const_cast(&key), nullptr, false); } diff --git a/mdbxdist/mdbx_chk.c b/mdbxdist/mdbx_chk.c index b8f8780..af07e35 100644 --- a/mdbxdist/mdbx_chk.c +++ b/mdbxdist/mdbx_chk.c @@ -16,7 +16,7 @@ /// \author Леонид Юрьев aka Leonid Yuriev \date 2015-2024 -#define MDBX_BUILD_SOURCERY 4ef6bfc2012bedf4af0bcd644ec87ace207f395c5d5e103573649032ec2cb6e8_v0_13_1_0_g5fc7a6b1 +#define MDBX_BUILD_SOURCERY 7a087912ed373fb7f849bc0a5b72a7d3cbdca30796a95e27dc05c7118e47dde9_v0_13_1_56_g10a93f4b #define LIBMDBX_INTERNALS @@ -32,6 +32,10 @@ #if (defined(MDBX_DEBUG) && MDBX_DEBUG > 0) || \ (defined(MDBX_FORCE_ASSERTIONS) && MDBX_FORCE_ASSERTIONS) #undef NDEBUG +#ifndef MDBX_DEBUG +/* Чтобы избежать включения отладки только из-за включения assert-проверок */ +#define MDBX_DEBUG 0 +#endif #endif /*----------------------------------------------------------------------------*/ @@ -1817,11 +1821,7 @@ osal_bswap32(uint32_t v) { /** Enables chunking long list of retired pages during huge transactions commit * to avoid use sequences of pages. */ #ifndef MDBX_ENABLE_BIGFOOT -#if MDBX_WORDBITS >= 64 || defined(DOXYGEN) #define MDBX_ENABLE_BIGFOOT 1 -#else -#define MDBX_ENABLE_BIGFOOT 0 -#endif #elif !(MDBX_ENABLE_BIGFOOT == 0 || MDBX_ENABLE_BIGFOOT == 1) #error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1 #endif /* MDBX_ENABLE_BIGFOOT */ diff --git a/mdbxdist/mdbx_copy.c b/mdbxdist/mdbx_copy.c index e04bec9..01a14e4 100644 --- a/mdbxdist/mdbx_copy.c +++ b/mdbxdist/mdbx_copy.c @@ -18,7 +18,7 @@ /// \author Леонид Юрьев aka Leonid Yuriev \date 2015-2024 -#define MDBX_BUILD_SOURCERY 4ef6bfc2012bedf4af0bcd644ec87ace207f395c5d5e103573649032ec2cb6e8_v0_13_1_0_g5fc7a6b1 +#define MDBX_BUILD_SOURCERY 7a087912ed373fb7f849bc0a5b72a7d3cbdca30796a95e27dc05c7118e47dde9_v0_13_1_56_g10a93f4b #define LIBMDBX_INTERNALS @@ -34,6 +34,10 @@ #if (defined(MDBX_DEBUG) && MDBX_DEBUG > 0) || \ (defined(MDBX_FORCE_ASSERTIONS) && MDBX_FORCE_ASSERTIONS) #undef NDEBUG +#ifndef MDBX_DEBUG +/* Чтобы избежать включения отладки только из-за включения assert-проверок */ +#define MDBX_DEBUG 0 +#endif #endif /*----------------------------------------------------------------------------*/ @@ -1819,11 +1823,7 @@ osal_bswap32(uint32_t v) { /** Enables chunking long list of retired pages during huge transactions commit * to avoid use sequences of pages. */ #ifndef MDBX_ENABLE_BIGFOOT -#if MDBX_WORDBITS >= 64 || defined(DOXYGEN) #define MDBX_ENABLE_BIGFOOT 1 -#else -#define MDBX_ENABLE_BIGFOOT 0 -#endif #elif !(MDBX_ENABLE_BIGFOOT == 0 || MDBX_ENABLE_BIGFOOT == 1) #error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1 #endif /* MDBX_ENABLE_BIGFOOT */ diff --git a/mdbxdist/mdbx_drop.c b/mdbxdist/mdbx_drop.c index b25daae..391e521 100644 --- a/mdbxdist/mdbx_drop.c +++ b/mdbxdist/mdbx_drop.c @@ -18,7 +18,7 @@ /// \author Леонид Юрьев aka Leonid Yuriev \date 2015-2024 -#define MDBX_BUILD_SOURCERY 4ef6bfc2012bedf4af0bcd644ec87ace207f395c5d5e103573649032ec2cb6e8_v0_13_1_0_g5fc7a6b1 +#define MDBX_BUILD_SOURCERY 7a087912ed373fb7f849bc0a5b72a7d3cbdca30796a95e27dc05c7118e47dde9_v0_13_1_56_g10a93f4b #define LIBMDBX_INTERNALS @@ -34,6 +34,10 @@ #if (defined(MDBX_DEBUG) && MDBX_DEBUG > 0) || \ (defined(MDBX_FORCE_ASSERTIONS) && MDBX_FORCE_ASSERTIONS) #undef NDEBUG +#ifndef MDBX_DEBUG +/* Чтобы избежать включения отладки только из-за включения assert-проверок */ +#define MDBX_DEBUG 0 +#endif #endif /*----------------------------------------------------------------------------*/ @@ -1819,11 +1823,7 @@ osal_bswap32(uint32_t v) { /** Enables chunking long list of retired pages during huge transactions commit * to avoid use sequences of pages. */ #ifndef MDBX_ENABLE_BIGFOOT -#if MDBX_WORDBITS >= 64 || defined(DOXYGEN) #define MDBX_ENABLE_BIGFOOT 1 -#else -#define MDBX_ENABLE_BIGFOOT 0 -#endif #elif !(MDBX_ENABLE_BIGFOOT == 0 || MDBX_ENABLE_BIGFOOT == 1) #error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1 #endif /* MDBX_ENABLE_BIGFOOT */ diff --git a/mdbxdist/mdbx_dump.c b/mdbxdist/mdbx_dump.c index 17a92ff..dd39d66 100644 --- a/mdbxdist/mdbx_dump.c +++ b/mdbxdist/mdbx_dump.c @@ -18,7 +18,7 @@ /// \author Леонид Юрьев aka Leonid Yuriev \date 2015-2024 -#define MDBX_BUILD_SOURCERY 4ef6bfc2012bedf4af0bcd644ec87ace207f395c5d5e103573649032ec2cb6e8_v0_13_1_0_g5fc7a6b1 +#define MDBX_BUILD_SOURCERY 7a087912ed373fb7f849bc0a5b72a7d3cbdca30796a95e27dc05c7118e47dde9_v0_13_1_56_g10a93f4b #define LIBMDBX_INTERNALS @@ -34,6 +34,10 @@ #if (defined(MDBX_DEBUG) && MDBX_DEBUG > 0) || \ (defined(MDBX_FORCE_ASSERTIONS) && MDBX_FORCE_ASSERTIONS) #undef NDEBUG +#ifndef MDBX_DEBUG +/* Чтобы избежать включения отладки только из-за включения assert-проверок */ +#define MDBX_DEBUG 0 +#endif #endif /*----------------------------------------------------------------------------*/ @@ -1819,11 +1823,7 @@ osal_bswap32(uint32_t v) { /** Enables chunking long list of retired pages during huge transactions commit * to avoid use sequences of pages. */ #ifndef MDBX_ENABLE_BIGFOOT -#if MDBX_WORDBITS >= 64 || defined(DOXYGEN) #define MDBX_ENABLE_BIGFOOT 1 -#else -#define MDBX_ENABLE_BIGFOOT 0 -#endif #elif !(MDBX_ENABLE_BIGFOOT == 0 || MDBX_ENABLE_BIGFOOT == 1) #error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1 #endif /* MDBX_ENABLE_BIGFOOT */ diff --git a/mdbxdist/mdbx_load.c b/mdbxdist/mdbx_load.c index d5ee412..a855d57 100644 --- a/mdbxdist/mdbx_load.c +++ b/mdbxdist/mdbx_load.c @@ -18,7 +18,7 @@ /// \author Леонид Юрьев aka Leonid Yuriev \date 2015-2024 -#define MDBX_BUILD_SOURCERY 4ef6bfc2012bedf4af0bcd644ec87ace207f395c5d5e103573649032ec2cb6e8_v0_13_1_0_g5fc7a6b1 +#define MDBX_BUILD_SOURCERY 7a087912ed373fb7f849bc0a5b72a7d3cbdca30796a95e27dc05c7118e47dde9_v0_13_1_56_g10a93f4b #define LIBMDBX_INTERNALS @@ -34,6 +34,10 @@ #if (defined(MDBX_DEBUG) && MDBX_DEBUG > 0) || \ (defined(MDBX_FORCE_ASSERTIONS) && MDBX_FORCE_ASSERTIONS) #undef NDEBUG +#ifndef MDBX_DEBUG +/* Чтобы избежать включения отладки только из-за включения assert-проверок */ +#define MDBX_DEBUG 0 +#endif #endif /*----------------------------------------------------------------------------*/ @@ -1819,11 +1823,7 @@ osal_bswap32(uint32_t v) { /** Enables chunking long list of retired pages during huge transactions commit * to avoid use sequences of pages. */ #ifndef MDBX_ENABLE_BIGFOOT -#if MDBX_WORDBITS >= 64 || defined(DOXYGEN) #define MDBX_ENABLE_BIGFOOT 1 -#else -#define MDBX_ENABLE_BIGFOOT 0 -#endif #elif !(MDBX_ENABLE_BIGFOOT == 0 || MDBX_ENABLE_BIGFOOT == 1) #error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1 #endif /* MDBX_ENABLE_BIGFOOT */ diff --git a/mdbxdist/mdbx_stat.c b/mdbxdist/mdbx_stat.c index 1e36dcb..8048dcc 100644 --- a/mdbxdist/mdbx_stat.c +++ b/mdbxdist/mdbx_stat.c @@ -18,7 +18,7 @@ /// \author Леонид Юрьев aka Leonid Yuriev \date 2015-2024 -#define MDBX_BUILD_SOURCERY 4ef6bfc2012bedf4af0bcd644ec87ace207f395c5d5e103573649032ec2cb6e8_v0_13_1_0_g5fc7a6b1 +#define MDBX_BUILD_SOURCERY 7a087912ed373fb7f849bc0a5b72a7d3cbdca30796a95e27dc05c7118e47dde9_v0_13_1_56_g10a93f4b #define LIBMDBX_INTERNALS @@ -34,6 +34,10 @@ #if (defined(MDBX_DEBUG) && MDBX_DEBUG > 0) || \ (defined(MDBX_FORCE_ASSERTIONS) && MDBX_FORCE_ASSERTIONS) #undef NDEBUG +#ifndef MDBX_DEBUG +/* Чтобы избежать включения отладки только из-за включения assert-проверок */ +#define MDBX_DEBUG 0 +#endif #endif /*----------------------------------------------------------------------------*/ @@ -1819,11 +1823,7 @@ osal_bswap32(uint32_t v) { /** Enables chunking long list of retired pages during huge transactions commit * to avoid use sequences of pages. */ #ifndef MDBX_ENABLE_BIGFOOT -#if MDBX_WORDBITS >= 64 || defined(DOXYGEN) #define MDBX_ENABLE_BIGFOOT 1 -#else -#define MDBX_ENABLE_BIGFOOT 0 -#endif #elif !(MDBX_ENABLE_BIGFOOT == 0 || MDBX_ENABLE_BIGFOOT == 1) #error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1 #endif /* MDBX_ENABLE_BIGFOOT */