libmdbx/src/internals.h

604 lines
23 KiB
C
Raw Normal View History

/// \copyright SPDX-License-Identifier: Apache-2.0
/// \note Please refer to the COPYRIGHT file for explanations license change,
/// credits and acknowledgments.
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2024
#pragma once
2022-06-02 18:59:58 +03:00
/*----------------------------------------------------------------------------*/
#include "essentials.h"
typedef struct dp dp_t;
typedef struct dpl dpl_t;
typedef struct kvx kvx_t;
typedef struct meta_ptr meta_ptr_t;
typedef struct inner_cursor subcur_t;
typedef struct cursor_couple cursor_couple_t;
typedef struct defer_free_item defer_free_item_t;
typedef struct troika {
uint8_t fsm, recent, prefer_steady, tail_and_flags;
#if MDBX_WORDBITS > 32 /* Workaround for false-positives from Valgrind */
uint32_t unused_pad;
#endif
#define TROIKA_HAVE_STEADY(troika) ((troika)->fsm & 7u)
#define TROIKA_STRICT_VALID(troika) ((troika)->tail_and_flags & 64u)
#define TROIKA_VALID(troika) ((troika)->tail_and_flags & 128u)
#define TROIKA_TAIL(troika) ((troika)->tail_and_flags & 3u)
txnid_t txnid[NUM_METAS];
} troika_t;
typedef struct page_get_result {
page_t *page;
int err;
} pgr_t;
typedef struct node_search_result {
node_t *node;
bool exact;
} nsr_t;
typedef struct bind_reader_slot_result {
int err;
reader_slot_t *rslot;
} bsr_t;
#include "atomics-ops.h"
#include "proto.h"
#include "txl.h"
#include "unaligned.h"
#if defined(_WIN32) || defined(_WIN64)
#include "windows-import.h"
#endif /* Windows */
enum signatures {
env_signature = INT32_C(0x1A899641),
txn_signature = INT32_C(0x13D53A31),
cur_signature_live = INT32_C(0x7E05D5B1),
cur_signature_ready4dispose = INT32_C(0x2817A047),
cur_signature_wait4eot = INT32_C(0x10E297A7)
};
/*----------------------------------------------------------------------------*/
/* An dirty-page list item is an pgno/pointer pair. */
struct dp {
page_t *ptr;
pgno_t pgno, npages;
};
enum dpl_rules {
dpl_gap_edging = 2,
dpl_gap_mergesort = 16,
dpl_reserve_gap = dpl_gap_mergesort + dpl_gap_edging,
dpl_insertion_threshold = 42
};
/* An DPL (dirty-page list) is a lazy-sorted array of MDBX_DPs. */
struct dpl {
size_t sorted;
size_t length;
/* number of pages, but not an entries. */
size_t pages_including_loose;
/* allocated size excluding the dpl_reserve_gap */
size_t detent;
/* dynamic size with holes at zero and after the last */
dp_t items[dpl_reserve_gap];
};
/*----------------------------------------------------------------------------*/
/* Internal structures */
/* Comparing/ordering and length constraints */
typedef struct clc {
MDBX_cmp_func *cmp; /* comparator */
size_t lmin, lmax; /* min/max length constraints */
} clc_t;
/* Вспомогательная информация о table.
*
* Совокупность потребностей:
* 1. Для транзакций и основного курсора нужны все поля.
* 2. Для вложенного dupsort-курсора нужен компаратор значений, который изнутри
* курсора будет выглядеть как компаратор ключей. Плюс заглушка компаратора
* значений, которая не должна использоваться в штатных ситуациях, но
* требуется хотя-бы для отслеживания таких обращений.
* 3. Использование компараторов для курсора и вложенного dupsort-курсора
* должно выглядеть одинаково.
* 4. Желательно минимизировать объём данных размещаемых внутри вложенного
* dupsort-курсора.
* 5. Желательно чтобы объем всей структуры был степенью двойки.
*
* Решение:
* - не храним в dupsort-курсоре ничего лишнего, а только tree;
* - в курсоры помещаем только указатель на clc_t, который будет указывать
* на соответствующее clc-поле в общей kvx-таблице привязанной к env;
* - компаратор размещаем в начале clc_t, в kvx_t сначала размещаем clc
* для ключей, потом для значений, а имя БД в конце kvx_t.
* - тогда в курсоре clc[0] будет содержать информацию для ключей,
* а clc[1] для значений, причем компаратор значений для dupsort-курсора
* будет попадать на MDBX_val с именем, что приведет к SIGSEGV при попытке
* использования такого компаратора.
* - размер kvx_t становится равным 8 словам.
*
* Трюки и прочая экономия на списках:
* - не храним dbi внутри курсора, вместо этого вычисляем его как разницу между
* dbi_state курсора и началом таблицы dbi_state в транзакции. Смысл тут в
* экономии кол-ва полей при инициализации курсора. Затрат это не создает,
* так как dbi требуется для последующего доступа к массивам в транзакции,
* т.е. при вычислении dbi разыменовывается тот-же указатель на txn
* и читается та же кэш-линия с указателями. */
typedef struct clc2 {
clc_t k; /* для ключей */
clc_t v; /* для значений */
} clc2_t;
struct kvx {
clc2_t clc;
MDBX_val name; /* имя table */
};
/* Non-shared DBI state flags inside transaction */
enum dbi_state {
DBI_DIRTY = 0x01 /* DB was written in this txn */,
DBI_STALE = 0x02 /* Named-DB record is older than txnID */,
DBI_FRESH = 0x04 /* Named-DB handle opened in this txn */,
DBI_CREAT = 0x08 /* Named-DB handle created in this txn */,
DBI_VALID = 0x10 /* Handle is valid, see also DB_VALID */,
DBI_OLDEN = 0x40 /* Handle was closed/reopened outside txn */,
DBI_LINDO = 0x80 /* Lazy initialization done for DBI-slot */,
};
enum txn_flags {
txn_ro_begin_flags = MDBX_TXN_RDONLY | MDBX_TXN_RDONLY_PREPARE,
txn_rw_begin_flags = MDBX_TXN_NOMETASYNC | MDBX_TXN_NOSYNC | MDBX_TXN_TRY,
txn_shrink_allowed = UINT32_C(0x40000000),
txn_parked = MDBX_TXN_PARKED,
txn_gc_drained = 0x40 /* GC was depleted up to oldest reader */,
txn_state_flags = MDBX_TXN_FINISHED | MDBX_TXN_ERROR | MDBX_TXN_DIRTY |
MDBX_TXN_SPILLS | MDBX_TXN_HAS_CHILD | MDBX_TXN_INVALID |
txn_gc_drained
};
/* A database transaction.
* Every operation requires a transaction handle. */
2017-05-23 21:36:09 +03:00
struct MDBX_txn {
int32_t signature;
uint32_t flags; /* Transaction Flags */
size_t n_dbi;
size_t owner; /* thread ID that owns this transaction */
MDBX_txn *parent; /* parent of a nested txn */
MDBX_txn *nested; /* nested txn under this txn,
set together with MDBX_TXN_HAS_CHILD */
geo_t geo;
/* The ID of this transaction. IDs are integers incrementing from
* INITIAL_TXNID. Only committed write transactions increment the ID. If a
* transaction aborts, the ID may be re-used by the next writer. */
txnid_t txnid, front_txnid;
MDBX_env *env; /* the DB environment */
tree_t *dbs; /* Array of tree_t records for each known DB */
mdbx: переработка инициализации, проверки и импорта dbi-хендлов в транзакциях. Ранее инициализация в транзакциях структур данных, связанных с dbi-хендлами и subDb, выполнялась непосредственно при запуске транзакций. Что в сценариях с большим кол-вом dbi-дексприторов (например libfpta) порождало заметные накладные расходы, которые расли линейно от общего кол-ва открытых subDb, а не от реально используемых в транзакции. При использовании одной-двух сотен хендлов, при старте каждой транзакции могли копироваться и/или обнуляться десятки килобайт. Теперь этот недостаток устранен. Изменена схема инициализации, валидации и импорта хендлов открытых после старта транзакции: 1) Инициализация теперь выполняется отложенна, а при старте транзации обнуляется только массив с однобайтовыми статустами dbi-хендлов. При этом доступнва опция сборки `MDBX_ENABLE_DBI_SPARSE`, при активации которой используется битовая карты, что снижает объем инициализации при старте транзакции в 8 раз (CHAR_BIT). 2) Переработана валидация dbi-хендлов на входах API, с уменьшением кол-ва проверок и ветвлений до теоретического минимума. 3) Переработ импорт dbi-хендов открытых после старта транзакци, теперь при этом не захватывается мьютекс.
2023-11-05 22:10:29 +03:00
#if MDBX_ENABLE_DBI_SPARSE
unsigned *__restrict dbi_sparse;
mdbx: переработка инициализации, проверки и импорта dbi-хендлов в транзакциях. Ранее инициализация в транзакциях структур данных, связанных с dbi-хендлами и subDb, выполнялась непосредственно при запуске транзакций. Что в сценариях с большим кол-вом dbi-дексприторов (например libfpta) порождало заметные накладные расходы, которые расли линейно от общего кол-ва открытых subDb, а не от реально используемых в транзакции. При использовании одной-двух сотен хендлов, при старте каждой транзакции могли копироваться и/или обнуляться десятки килобайт. Теперь этот недостаток устранен. Изменена схема инициализации, валидации и импорта хендлов открытых после старта транзакции: 1) Инициализация теперь выполняется отложенна, а при старте транзации обнуляется только массив с однобайтовыми статустами dbi-хендлов. При этом доступнва опция сборки `MDBX_ENABLE_DBI_SPARSE`, при активации которой используется битовая карты, что снижает объем инициализации при старте транзакции в 8 раз (CHAR_BIT). 2) Переработана валидация dbi-хендлов на входах API, с уменьшением кол-ва проверок и ветвлений до теоретического минимума. 3) Переработ импорт dbi-хендов открытых после старта транзакци, теперь при этом не захватывается мьютекс.
2023-11-05 22:10:29 +03:00
#endif /* MDBX_ENABLE_DBI_SPARSE */
/* Array of non-shared txn's flags of DBI.
* Модификатор __restrict тут полезен и безопасен в текущем понимании,
* так как пересечение возможно только с dbi_state курсоров,
* и происходит по-чтению до последующего изменения/записи. */
uint8_t *__restrict dbi_state;
/* Array of sequence numbers for each DB handle. */
uint32_t *__restrict dbi_seqs;
/* Массив с головами односвязных списков отслеживания курсоров. */
MDBX_cursor **cursors;
/* "Канареечные" маркеры/счетчики */
MDBX_canary canary;
/* User-settable context */
void *userctx;
union {
struct {
/* For read txns: This thread/txn's reader table slot, or nullptr. */
reader_slot_t *reader;
} to;
struct {
troika_t troika;
/* In write txns, array of cursors for each DB */
pnl_t __restrict relist; /* Reclaimed GC pages */
struct {
/* The list of reclaimed txns from GC */
txl_t __restrict reclaimed;
txnid_t last_reclaimed; /* ID of last used record */
uint64_t time_acc;
} gc;
#if MDBX_ENABLE_REFUND
pgno_t loose_refund_wl /* FIXME: describe */;
#endif /* MDBX_ENABLE_REFUND */
/* a sequence to spilling dirty page with LRU policy */
unsigned dirtylru;
/* dirtylist room: Dirty array size - dirty pages visible to this txn.
* Includes ancestor txns' dirty pages not hidden by other txns'
* dirty/spilled pages. Thus commit(nested txn) has room to merge
* dirtylist into parent after freeing hidden parent pages. */
size_t dirtyroom;
/* For write txns: Modified pages. Sorted when not MDBX_WRITEMAP. */
dpl_t *__restrict dirtylist;
/* The list of pages that became unused during this transaction. */
pnl_t __restrict retired_pages;
/* The list of loose pages that became unused and may be reused
* in this transaction, linked through `page_next()`. */
page_t *__restrict loose_pages;
/* Number of loose pages (tw.loose_pages) */
size_t loose_count;
union {
struct {
size_t least_removed;
/* The sorted list of dirty pages we temporarily wrote to disk
* because the dirty list was full. page numbers in here are
* shifted left by 1, deleted slots have the LSB set. */
pnl_t __restrict list;
} spilled;
size_t writemap_dirty_npages;
size_t writemap_spilled_npages;
};
} tw;
};
};
#define CURSOR_STACK_SIZE (16 + MDBX_WORDBITS / 4)
struct MDBX_cursor {
int32_t signature;
union {
/* Тут некоторые трюки/заморочки с тем чтобы во всех основных сценариях
* проверять состояние курсора одной простой операцией сравнения,
* и при этом ни на каплю не усложнять код итерации стека курсора.
*
* Поэтому решение такое:
* - поля flags и top сделаны знаковыми, а их отрицательные значения
* используются для обозначения не-установленного/не-инициализированного
* состояния курсора;
* - для инвалидации/сброса курсора достаточно записать отрицательное
* значение в объединенное поле top_and_flags;
* - все проверки состояния сводятся к сравнению одного из полей
* flags/snum/snum_and_flags, которые в зависимости от сценария,
* трактуются либо как знаковые, либо как безнаковые. */
__anonymous_struct_extension__ struct {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
int8_t flags;
/* индекс вершины стека, меньше нуля для не-инициализированного курсора */
int8_t top;
#else
int8_t top;
int8_t flags;
#endif
};
int16_t top_and_flags;
};
/* флаги проверки, в том числе биты для проверки типа листовых страниц. */
uint8_t checking;
/* Указывает на txn->dbi_state[] для DBI этого курсора.
* Модификатор __restrict тут полезен и безопасен в текущем понимании,
* так как пересечение возможно только с dbi_state транзакции,
* и происходит по-чтению до последующего изменения/записи. */
uint8_t *__restrict dbi_state;
/* Связь списка отслеживания курсоров в транзакции */
MDBX_txn *txn;
/* Указывает на tree->dbs[] для DBI этого курсора. */
tree_t *tree;
/* Указывает на env->kvs[] для DBI этого курсора. */
clc2_t *clc;
subcur_t *__restrict subcur;
page_t *pg[CURSOR_STACK_SIZE]; /* stack of pushed pages */
indx_t ki[CURSOR_STACK_SIZE]; /* stack of page indices */
MDBX_cursor *next;
/* Состояние на момент старта вложенной транзакции */
MDBX_cursor *backup;
};
struct inner_cursor {
MDBX_cursor cursor;
tree_t nested_tree;
};
struct cursor_couple {
MDBX_cursor outer;
void *userctx; /* User-settable context */
subcur_t inner;
};
struct defer_free_item {
struct defer_free_item *next;
uint64_t timestamp;
};
enum env_flags {
/* Failed to update the meta page. Probably an I/O error. */
ENV_FATAL_ERROR = INT32_MIN /* 0x80000000 */,
/* Some fields are initialized. */
ENV_ACTIVE = UINT32_C(0x20000000),
/* me_txkey is set */
ENV_TXKEY = UINT32_C(0x10000000),
/* Legacy MDBX_MAPASYNC (prior v0.9) */
DEPRECATED_MAPASYNC = UINT32_C(0x100000),
/* Legacy MDBX_COALESCE (prior v0.12) */
DEPRECATED_COALESCE = UINT32_C(0x2000000),
ENV_INTERNAL_FLAGS = ENV_FATAL_ERROR | ENV_ACTIVE | ENV_TXKEY,
/* Only a subset of the mdbx_env flags can be changed
* at runtime. Changing other flags requires closing the
* environment and re-opening it with the new flags. */
ENV_CHANGEABLE_FLAGS = MDBX_SAFE_NOSYNC | MDBX_NOMETASYNC |
DEPRECATED_MAPASYNC | MDBX_NOMEMINIT |
DEPRECATED_COALESCE | MDBX_PAGEPERTURB | MDBX_ACCEDE |
MDBX_VALIDATION,
ENV_CHANGELESS_FLAGS = MDBX_NOSUBDIR | MDBX_RDONLY | MDBX_WRITEMAP |
MDBX_NOSTICKYTHREADS | MDBX_NORDAHEAD |
MDBX_LIFORECLAIM | MDBX_EXCLUSIVE,
ENV_USABLE_FLAGS = ENV_CHANGEABLE_FLAGS | ENV_CHANGELESS_FLAGS
};
/* The database environment. */
struct MDBX_env {
/* ----------------------------------------------------- mostly static part */
mdbx_atomic_uint32_t signature;
uint32_t flags;
unsigned ps; /* DB page size, initialized from me_os_psize */
osal_mmap_t dxb_mmap; /* The main data file */
#define lazy_fd dxb_mmap.fd
mdbx_filehandle_t dsync_fd, fd4meta;
#if defined(_WIN32) || defined(_WIN64)
HANDLE dxb_lock_event;
#endif /* Windows */
osal_mmap_t lck_mmap; /* The lock file */
lck_t *lck;
uint16_t leaf_nodemax; /* max size of a leaf-node */
uint16_t branch_nodemax; /* max size of a branch-node */
uint16_t subpage_limit;
uint16_t subpage_room_threshold;
uint16_t subpage_reserve_prereq;
uint16_t subpage_reserve_limit;
atomic_pgno_t mlocked_pgno;
uint8_t ps2ln; /* log2 of DB page size */
int8_t stuck_meta; /* recovery-only: target meta page or less that zero */
uint16_t merge_threshold, merge_threshold_gc; /* pages emptier than this are
candidates for merging */
unsigned max_readers; /* size of the reader table */
MDBX_dbi max_dbi; /* size of the DB table */
uint32_t pid; /* process ID of this env */
osal_thread_key_t me_txkey; /* thread-key for readers */
struct { /* path to the DB files */
pathchar_t *lck, *dxb, *specified;
void *buffer;
} pathname;
void *page_auxbuf; /* scratch area for DUPSORT put() */
MDBX_txn *basal_txn; /* preallocated write transaction */
kvx_t *kvs; /* array of auxiliary key-value properties */
uint8_t *__restrict dbs_flags; /* array of flags from tree_t.flags */
mdbx_atomic_uint32_t *dbi_seqs; /* array of dbi sequence numbers */
unsigned maxgc_large1page; /* Number of pgno_t fit in a single large page */
unsigned maxgc_per_branch;
uint32_t registered_reader_pid; /* have liveness lock in reader table */
void *userctx; /* User-settable context */
MDBX_hsr_func *hsr_callback; /* Callback for kicking laggard readers */
size_t madv_threshold;
struct {
unsigned dp_reserve_limit;
unsigned rp_augment_limit;
unsigned dp_limit;
unsigned dp_initial;
uint64_t gc_time_limit;
uint8_t dp_loose_limit;
uint8_t spill_max_denominator;
uint8_t spill_min_denominator;
uint8_t spill_parent4child_denominator;
unsigned merge_threshold_16dot16_percent;
#if !(defined(_WIN32) || defined(_WIN64))
unsigned writethrough_threshold;
#endif /* Windows */
bool prefault_write;
mdbx: доработка `rebalance()` ради уменьшения WAF. После предыдущей серии доработок весной 2021 года, функция `rebalance()` обеспечивала слияние мало заполненной страницы с менее заполненной соседней, одновременно пытаясь не вовлекать соседних страниц, если те еще не были скопированы/клонированы/изменены в текущей транзакции. В целом, реализованная тактика представляется успешной. Однако, при обновлении GC она иногда приводила к исчерпанию подготовленного резерва извлеченных из GC страниц. Это не является проблемой, если не считать вероятность срабатывания `assert(txn->mt_flags & MDBX_TXN_DRAINED_GC)` в отладочных сборках. Тем не менее, из этой ситуации можно сделать вывод, что поведение `rebalance()`, как минимум, может быть обогащено опцией уменьшения WAF ценой меньшей сбалансированности дерева. Технически при этом слияние выполняется преимущественно с грязной страницей, если на ней достаточно места и соседняя страница с другой стороны еще чистая. Соответствующая опция в `enum MDBX_option_t` будет добавлена чуть позже.
2024-02-29 09:19:54 +03:00
bool prefer_waf_insteadof_balance; /* Strive to minimize WAF instead of
balancing pages fullment */
struct {
uint16_t limit;
uint16_t room_threshold;
uint16_t reserve_prereq;
uint16_t reserve_limit;
} subpage;
union {
unsigned all;
/* tracks options with non-auto values but tuned by user */
struct {
unsigned dp_limit : 1;
unsigned rp_augment_limit : 1;
unsigned prefault_write : 1;
} non_auto;
} flags;
} options;
/* struct geo_in_bytes used for accepting db-geo params from user for the new
* database creation, i.e. when mdbx_env_set_geometry() was called before
* mdbx_env_open(). */
struct {
size_t lower; /* minimal size of datafile */
size_t upper; /* maximal size of datafile */
size_t now; /* current size of datafile */
size_t grow; /* step to grow datafile */
size_t shrink; /* threshold to shrink datafile */
} geo_in_bytes;
#if MDBX_LOCKING == MDBX_LOCKING_SYSV
union {
key_t key;
int semid;
} me_sysv_ipc;
#endif /* MDBX_LOCKING == MDBX_LOCKING_SYSV */
bool incore;
bool prefault_write_activated;
#if MDBX_ENABLE_DBI_LOCKFREE
defer_free_item_t *defer_free;
#endif /* MDBX_ENABLE_DBI_LOCKFREE */
/* -------------------------------------------------------------- debugging */
#if MDBX_DEBUG
MDBX_assert_func *assert_func; /* Callback for assertion failures */
#endif
#ifdef ENABLE_MEMCHECK
int valgrind_handle;
#endif
#if defined(ENABLE_MEMCHECK) || defined(__SANITIZE_ADDRESS__)
pgno_t poison_edge;
#endif /* ENABLE_MEMCHECK || __SANITIZE_ADDRESS__ */
#ifndef xMDBX_DEBUG_SPILLING
#define xMDBX_DEBUG_SPILLING 0
#endif
#if xMDBX_DEBUG_SPILLING == 2
size_t debug_dirtied_est, debug_dirtied_act;
#endif /* xMDBX_DEBUG_SPILLING */
/* --------------------------------------------------- mostly volatile part */
MDBX_txn *txn; /* current write transaction */
osal_fastmutex_t dbi_lock;
unsigned n_dbi; /* number of DBs opened */
unsigned shadow_reserve_len;
page_t *__restrict shadow_reserve; /* list of malloc'ed blocks for re-use */
osal_ioring_t ioring;
#if defined(_WIN32) || defined(_WIN64)
osal_srwlock_t remap_guard;
/* Workaround for LockFileEx and WriteFile multithread bug */
CRITICAL_SECTION windowsbug_lock;
char *pathname_char; /* cache of multi-byte representation of pathname
to the DB files */
#else
osal_fastmutex_t remap_guard;
#endif
/* ------------------------------------------------- stub for lck-less mode */
mdbx_atomic_uint64_t
lckless_placeholder[(sizeof(lck_t) + MDBX_CACHELINE_SIZE - 1) /
sizeof(mdbx_atomic_uint64_t)];
};
/*----------------------------------------------------------------------------*/
/* pseudo-error code, not exposed outside libmdbx */
#define MDBX_NO_ROOT (MDBX_LAST_ADDED_ERRCODE + 33)
/* Number of slots in the reader table.
* This value was chosen somewhat arbitrarily. The 61 is a prime number,
* and such readers plus a couple mutexes fit into single 4KB page.
* Applications should set the table size using mdbx_env_set_maxreaders(). */
#define DEFAULT_READERS 61
enum db_flags {
DB_PERSISTENT_FLAGS = MDBX_REVERSEKEY | MDBX_DUPSORT | MDBX_INTEGERKEY |
MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP,
/* mdbx_dbi_open() flags */
DB_USABLE_FLAGS = DB_PERSISTENT_FLAGS | MDBX_CREATE | MDBX_DB_ACCEDE,
DB_VALID = 0x80u /* DB handle is valid, for dbs_flags */,
DB_POISON = 0x7fu /* update pending */,
DB_INTERNAL_FLAGS = DB_VALID
};
#if !defined(__cplusplus) || CONSTEXPR_ENUM_FLAGS_OPERATIONS
MDBX_MAYBE_UNUSED static void static_checks(void) {
STATIC_ASSERT(MDBX_WORDBITS == sizeof(void *) * CHAR_BIT);
STATIC_ASSERT(UINT64_C(0x80000000) == (uint32_t)ENV_FATAL_ERROR);
STATIC_ASSERT_MSG(INT16_MAX - CORE_DBS == MDBX_MAX_DBI,
"Oops, MDBX_MAX_DBI or CORE_DBS?");
STATIC_ASSERT_MSG((unsigned)(MDBX_DB_ACCEDE | MDBX_CREATE) ==
((DB_USABLE_FLAGS | DB_INTERNAL_FLAGS) &
(ENV_USABLE_FLAGS | ENV_INTERNAL_FLAGS)),
"Oops, some flags overlapped or wrong");
STATIC_ASSERT_MSG((DB_INTERNAL_FLAGS & DB_USABLE_FLAGS) == 0,
"Oops, some flags overlapped or wrong");
STATIC_ASSERT_MSG((DB_PERSISTENT_FLAGS & ~DB_USABLE_FLAGS) == 0,
"Oops, some flags overlapped or wrong");
STATIC_ASSERT(DB_PERSISTENT_FLAGS <= UINT8_MAX);
STATIC_ASSERT_MSG((ENV_INTERNAL_FLAGS & ENV_USABLE_FLAGS) == 0,
"Oops, some flags overlapped or wrong");
STATIC_ASSERT_MSG(
(txn_state_flags & (txn_rw_begin_flags | txn_ro_begin_flags)) == 0,
"Oops, some txn flags overlapped or wrong");
STATIC_ASSERT_MSG(
((txn_rw_begin_flags | txn_ro_begin_flags | txn_state_flags) &
txn_shrink_allowed) == 0,
"Oops, some txn flags overlapped or wrong");
STATIC_ASSERT(sizeof(reader_slot_t) == 32);
#if MDBX_LOCKING > 0
STATIC_ASSERT(offsetof(lck_t, wrt_lock) % MDBX_CACHELINE_SIZE == 0);
STATIC_ASSERT(offsetof(lck_t, rdt_lock) % MDBX_CACHELINE_SIZE == 0);
#else
STATIC_ASSERT(offsetof(lck_t, cached_oldest) % MDBX_CACHELINE_SIZE == 0);
STATIC_ASSERT(offsetof(lck_t, rdt_length) % MDBX_CACHELINE_SIZE == 0);
#endif /* MDBX_LOCKING */
STATIC_ASSERT(offsetof(lck_t, rdt) % MDBX_CACHELINE_SIZE == 0);
#if FLEXIBLE_ARRAY_MEMBERS
STATIC_ASSERT(NODESIZE == offsetof(node_t, payload));
STATIC_ASSERT(PAGEHDRSZ == offsetof(page_t, entries));
#endif /* FLEXIBLE_ARRAY_MEMBERS */
STATIC_ASSERT(sizeof(clc_t) == 3 * sizeof(void *));
STATIC_ASSERT(sizeof(kvx_t) == 8 * sizeof(void *));
#if MDBX_WORDBITS == 64
#define KVX_SIZE_LN2 6
#else
#define KVX_SIZE_LN2 5
#endif
STATIC_ASSERT(sizeof(kvx_t) == (1u << KVX_SIZE_LN2));
}
#endif /* Disabled for MSVC 19.0 (VisualStudio 2015) */
/******************************************************************************/
#include "node.h"
#include "dbi.h"
#include "cogs.h"
#include "cursor.h"
#include "dpl.h"
#include "gc.h"
#include "lck.h"
#include "meta.h"
#include "page-iov.h"
#include "spill.h"
#include "page-ops.h"
#include "tls.h"
#include "walk.h"
#include "sort.h"