diff options
author | George Hazan <ghazan@miranda.im> | 2020-07-08 17:05:52 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2020-07-08 17:05:52 +0300 |
commit | f365c11384642f6674ff5019ff97eda6f89ba378 (patch) | |
tree | cd5940e703567e2d0dcc5f20d8a6d9afee6bfbe8 /libs/libmdbx/src | |
parent | e5ee9bdc7089073f7b3fe6bfdb1cec594e912ca8 (diff) |
fixes #2475 (Update libmdbx to 0.8.2)
Diffstat (limited to 'libs/libmdbx/src')
-rw-r--r-- | libs/libmdbx/src/CMakeLists.txt | 26 | ||||
-rw-r--r-- | libs/libmdbx/src/ChangeLog.md | 229 | ||||
-rw-r--r-- | libs/libmdbx/src/GNUmakefile | 8 | ||||
-rw-r--r-- | libs/libmdbx/src/README.md | 110 | ||||
-rw-r--r-- | libs/libmdbx/src/appveyor.yml | 2 | ||||
-rw-r--r-- | libs/libmdbx/src/cmake/compiler.cmake | 109 | ||||
-rw-r--r-- | libs/libmdbx/src/cmake/profile.cmake | 3 | ||||
-rw-r--r-- | libs/libmdbx/src/mdbx.h | 35 | ||||
-rw-r--r-- | libs/libmdbx/src/src/core.c | 465 | ||||
-rw-r--r-- | libs/libmdbx/src/src/defs.h | 31 | ||||
-rw-r--r-- | libs/libmdbx/src/src/internals.h | 66 | ||||
-rw-r--r-- | libs/libmdbx/src/src/lck-posix.c | 19 | ||||
-rw-r--r-- | libs/libmdbx/src/src/osal.c | 75 | ||||
-rw-r--r-- | libs/libmdbx/src/src/osal.h | 2 | ||||
-rw-r--r-- | libs/libmdbx/src/test/CMakeLists.txt | 32 | ||||
-rw-r--r-- | libs/libmdbx/src/test/config.cc | 2 | ||||
-rw-r--r-- | libs/libmdbx/src/test/config.h | 7 | ||||
-rw-r--r-- | libs/libmdbx/src/test/long_stochastic.sh | 8 | ||||
-rw-r--r-- | libs/libmdbx/src/test/main.cc | 5 | ||||
-rw-r--r-- | libs/libmdbx/src/test/osal-unix.cc | 12 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.cc | 8 | ||||
-rw-r--r-- | libs/libmdbx/src/test/test.h | 3 | ||||
-rw-r--r-- | libs/libmdbx/src/version.c | 8 |
23 files changed, 806 insertions, 459 deletions
diff --git a/libs/libmdbx/src/CMakeLists.txt b/libs/libmdbx/src/CMakeLists.txt index 40355c27d3..e00f34034a 100644 --- a/libs/libmdbx/src/CMakeLists.txt +++ b/libs/libmdbx/src/CMakeLists.txt @@ -299,7 +299,9 @@ if(NOT HAS_C11 LESS 0) else() set(MDBX_C_STANDARD 99) endif() -message(STATUS "Use C${MDBX_C_STANDARD} for libmdbx") +if(MDBX_C_STANDARD) + message(STATUS "Use C${MDBX_C_STANDARD} for libmdbx") +endif() if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND EXISTS "${MDBX_SOURCE_DIR}/ntdll.def") if(MSVC) @@ -485,9 +487,11 @@ if(MDBX_INSTALL_STATIC) else() add_library(mdbx-static STATIC EXCLUDE_FROM_ALL ${LIBMDBX_SOURCES}) endif() -set_target_properties(mdbx-static PROPERTIES - C_STANDARD ${MDBX_C_STANDARD} C_STANDARD_REQUIRED ON - PUBLIC_HEADER mdbx.h) +set_target_properties(mdbx-static PROPERTIES PUBLIC_HEADER mdbx.h) +if(MDBX_C_STANDARD) + set_target_properties(mdbx-static PROPERTIES + C_STANDARD ${MDBX_C_STANDARD} C_STANDARD_REQUIRED ON) +endif() target_compile_definitions(mdbx-static PRIVATE MDBX_BUILD_SHARED_LIBRARY=0) target_setup_options(mdbx-static) libmdbx_setup_libs(mdbx-static INTERFACE) @@ -502,9 +506,11 @@ endif() # build shared library if(MDBX_BUILD_SHARED_LIBRARY) add_library(mdbx SHARED ${LIBMDBX_SOURCES}) - set_target_properties(mdbx PROPERTIES - C_STANDARD ${MDBX_C_STANDARD} C_STANDARD_REQUIRED ON - PUBLIC_HEADER mdbx.h) + set_target_properties(mdbx PROPERTIES PUBLIC_HEADER mdbx.h) + if(MDBX_C_STANDARD) + set_target_properties(mdbx PROPERTIES + C_STANDARD ${MDBX_C_STANDARD} C_STANDARD_REQUIRED ON) + endif() target_compile_definitions(mdbx PRIVATE LIBMDBX_EXPORTS MDBX_BUILD_SHARED_LIBRARY=1 INTERFACE LIBMDBX_IMPORTS) target_setup_options(mdbx) libmdbx_setup_libs(mdbx PRIVATE) @@ -557,8 +563,10 @@ if(MDBX_BUILD_TOOLS) foreach(TOOL mdbx_chk mdbx_copy mdbx_stat mdbx_dump mdbx_load) add_executable(${TOOL} mdbx.h ${MDBX_SOURCE_DIR}/${TOOL}.c ${WINGETOPT_SRC}) - set_target_properties(${TOOL} PROPERTIES - C_STANDARD ${MDBX_C_STANDARD} C_STANDARD_REQUIRED ON) + if(MDBX_C_STANDARD) + set_target_properties(${TOOL} PROPERTIES + C_STANDARD ${MDBX_C_STANDARD} C_STANDARD_REQUIRED ON) + endif() target_setup_options(${TOOL}) target_link_libraries(${TOOL} ${TOOL_MDBX_LIB}) endforeach() diff --git a/libs/libmdbx/src/ChangeLog.md b/libs/libmdbx/src/ChangeLog.md index 1c4beb635b..2208b212ed 100644 --- a/libs/libmdbx/src/ChangeLog.md +++ b/libs/libmdbx/src/ChangeLog.md @@ -1,113 +1,128 @@ -v0.9.x (in the development): - - TODO: Native bindings for C++. - - TODO: Packages for AltLinux, Fedora/RHEL, Debian/Ubuntu. +## v0.9.x (in the development): +- TODO: API for explicit threads (de)registration. +- TODO: Native bindings for C++. +- TODO: Packages for AltLinux, Fedora/RHEL, Debian/Ubuntu. -v0.8.1 2020-06-12: - - Minor change versioning. The last number in version now mean the number of commits since last release/tag. - - Provide ChangeLog file. - - Fix for using libmdbx as C-only sub-project with CMake. - - Fix `mdbx_env_set_geometry()` for case it called from opened environment outside of write transaction. - - Add support for huge transactions and `MDBX_HUGE_TRANSACTIONS` build-option (default `OFF`). - - Refine LTO (link time optimization) for clang. - - Force enabling exceptions handling for MSVC (`/EHsc` option). +## v0.8.2 2020-07-06: +- Added support multi-opening the same DB in a process with SysV locking (BSD). +- Fixed warnings & minors for LCC compiler (E2K). +- Enabled to simultaneously open the same database from processes with and without the `MDBX_WRITEMAP` option. +- Added key-to-value, `mdbx_get_keycmp()` and `mdbx_get_datacmp()` functions (helpful to avoid using custom comparators). +- Added `ENABLE_UBSAN` CMake option to enabling the UndefinedBehaviorSanitizer from GCC/CLANG. +- Workaround for [CLANG bug](https://bugs.llvm.org/show_bug.cgi?id=43275). +- Returning `MDBX_CORRUPTED` in case all meta-pages are weak and no other error. +- Refined mode bits while auto-creating LCK-file. +- Avoids unnecessary database file re-mapping in case geometry changed by another process(es). + From the user's point of view, the `MDBX_UNABLE_EXTEND_MAPSIZE` error will now be returned less frequently and only when using the DB in the current process really requires it to be reopened. +- Remapping on-the-fly and of the database file was implemented. + Now remapping with a change of address is performed automatically if there are no dependent readers in the current process. -v0.8.0 2020-06-05: - - Support for Android/Bionic. - - Support for iOS. - - Auto-handling `MDBX_NOSUBDIR` while opening for any exists database. - - Engage github-actions to make release-assets. - - Clarify API description. - - Extended keygen-cases in stochastic test. - - Fix fetching of first/lower key from LEAF2-page during page merge. - - Fix missing comma in array of error messages. - - Fix div-by-zero while copy-with-compaction for non-resizeable environment. - - Fixes & enhancements for custom-comparators. - - Fix `MDBX_AVOID_CRT` option and missing `ntdll.def`. - - Fix `mdbx_env_close()` to work correctly called concurrently from several threads. - - Fix null-deref in an ASAN-enabled builds while opening environment with error and/or read-only. - - Fix AddressSanitizer errors after closing environment. - - Fix/workaround to avoid GCC 10.x pedantic warnings. - - Fix using `ENODATA` for FreeBSD. - - Avoid invalidation of DBI-handle(s) when it just closing. - - Avoid using `pwritev()` for single-writes (up to 10% speedup for some kernels & scenarios). - - Avoiding `MDBX_UTTERLY_NOSYNC` as result of flags merge. - - Add `mdbx_dbi_dupsort_depthmask()` function. - - Add `MDBX_CP_FORCE_RESIZEABLE` option. - - Add deprecated `MDBX_MAP_RESIZED` for compatibility. - - Add `MDBX_BUILD_TOOLS` option (default `ON`). - - Refine `mdbx_dbi_open_ex()` to safe concurrently opening the same handle from difference threads. - - Truncate clk-file during environment closing. So zero-length lck file indicates that environment was closed properly. - - Refine `mdbx_update_gc()` for huge transactions with small sizes of database page. - - Extends dump/load to support all MDBX attributes. - - Avoid upsertion the same key-value data, fix related assertions. - - Rework min/max length checking for keys & values. - - Checking the order of keys on all pages during checking. - - Support `CFLAGS_EXTRA` make-option for convenience. - - Preserve last txnid while copy with compactification. - - Auto-reset running transaction in mdbx_txn_renew(). - - Automatically abort errored transaction in mdbx_txn_commit(). - - Auto-choose pagesize for a large databases. - - Rearrange source files, rework build, options-support by CMake. - - Crutch for WSL1 (Windows subsystem for Linux). - - Refine install/uninstall targets. - - Support for Valgrind 3.14 and later. - - Add check-analyzer check-ubsan check-asan check-leak targets to Makefile. - - Minor fix/workaround to avoid UBSAN traps for `memcpy(ptr, NULL, 0)`. - - Avoid some GCC-analyzer false-positive warnings. +## v0.8.1 2020-06-12: +- Minor change versioning. The last number in the version now means the number of commits since last release/tag. +- Provide ChangeLog file. +- Fix for using libmdbx as a C-only sub-project with CMake. +- Fix `mdbx_env_set_geometry()` for case it is called from an opened environment outside of a write transaction. +- Add support for huge transactions and `MDBX_HUGE_TRANSACTIONS` build-option (default `OFF`). +- Refine LTO (link time optimization) for clang. +- Force enabling exceptions handling for MSVC (`/EHsc` option). -v0.7.0 2020-03-18: - - Workarounds for Wine (Windows compatibility layer for Linux). - - `MDBX_MAP_RESIZED` renamed to `MDBX_UNABLE_EXTEND_MAPSIZE`. - - Clarify API description, fix typos. - - Speedup runtime checks in debug/checked builds. - - Added checking for read/write transactions overlapping for the same thread, added `MDBX_TXN_OVERLAPPING` error and `MDBX_DBG_LEGACY_OVERLAP` option. - - Added `mdbx_key_from_jsonInteger()`, `mdbx_key_from_double()`, `mdbx_key_from_float()`, `mdbx_key_from_int64()` and `mdbx_key_from_int32()` functions. See `mdbx.h` for description. - - Fix compatibility (use zero for invalid DBI). - - Refine/clarify error messages. - - Avoids extra error messages "bad txn" from mdbx_chk when DB is corrupted. +## v0.8.0 2020-06-05: +- Support for Android/Bionic. +- Support for iOS. +- Auto-handling `MDBX_NOSUBDIR` while opening for any existing database. +- Engage github-actions to make release-assets. +- Clarify API description. +- Extended keygen-cases in stochastic test. +- Fix fetching of first/lower key from LEAF2-page during page merge. +- Fix missing comma in array of error messages. +- Fix div-by-zero while copy-with-compaction for non-resizable environments. +- Fixes & enhancements for custom-comparators. +- Fix `MDBX_AVOID_CRT` option and missing `ntdll.def`. +- Fix `mdbx_env_close()` to work correctly called concurrently from several threads. +- Fix null-deref in an ASAN-enabled builds while opening the environment with error and/or read-only. +- Fix AddressSanitizer errors after closing the environment. +- Fix/workaround to avoid GCC 10.x pedantic warnings. +- Fix using `ENODATA` for FreeBSD. +- Avoid invalidation of DBI-handle(s) when it just closes. +- Avoid using `pwritev()` for single-writes (up to 10% speedup for some kernels & scenarios). +- Avoiding `MDBX_UTTERLY_NOSYNC` as result of flags merge. +- Add `mdbx_dbi_dupsort_depthmask()` function. +- Add `MDBX_CP_FORCE_RESIZEABLE` option. +- Add deprecated `MDBX_MAP_RESIZED` for compatibility. +- Add `MDBX_BUILD_TOOLS` option (default `ON`). +- Refine `mdbx_dbi_open_ex()` to safe concurrently opening the same handle from different threads. +- Truncate clk-file during environment closing. So a zero-length lck-file indicates that the environment was closed properly. +- Refine `mdbx_update_gc()` for huge transactions with small sizes of database page. +- Extends dump/load to support all MDBX attributes. +- Avoid upsertion the same key-value data, fix related assertions. +- Rework min/max length checking for keys & values. +- Checking the order of keys on all pages during checking. +- Support `CFLAGS_EXTRA` make-option for convenience. +- Preserve the last txnid while copying with compactification. +- Auto-reset running transaction in mdbx_txn_renew(). +- Automatically abort errored transaction in mdbx_txn_commit(). +- Auto-choose page size for large databases. +- Rearrange source files, rework build, options-support by CMake. +- Crutch for WSL1 (Windows subsystem for Linux). +- Refine install/uninstall targets. +- Support for Valgrind 3.14 and later. +- Add check-analyzer check-ubsan check-asan check-leak targets to Makefile. +- Minor fix/workaround to avoid UBSAN traps for `memcpy(ptr, NULL, 0)`. +- Avoid some GCC-analyzer false-positive warnings. -v0.6.0 2020-01-21: - - Fix `mdbx_load` utility for custom comparators. - - Fix checks related to `MDBX_APPEND` flag inside `mdbx_cursor_put()`. - - Refine/fix dbi_bind() internals. - - Refine/fix handling STATUS_CONFLICTING_ADDRESSES. - - Rework `MDBX_DBG_DUMP` option to avoid disk I/O performance degradation. - - Add built-in help to test tool. - - Fix `mdbx_env_set_geometry()` for large page size. - - Fix env_set_geometry() for large pagesize. - - Clarify API description & comments, fix typos. +## v0.7.0 2020-03-18: +- Workarounds for Wine (Windows compatibility layer for Linux). +- `MDBX_MAP_RESIZED` renamed to `MDBX_UNABLE_EXTEND_MAPSIZE`. +- Clarify API description, fix typos. +- Speedup runtime checks in debug/checked builds. +- Added checking for read/write transactions overlapping for the same thread, added `MDBX_TXN_OVERLAPPING` error and `MDBX_DBG_LEGACY_OVERLAP` option. +- Added `mdbx_key_from_jsonInteger()`, `mdbx_key_from_double()`, `mdbx_key_from_float()`, `mdbx_key_from_int64()` and `mdbx_key_from_int32()` functions. See `mdbx.h` for description. +- Fix compatibility (use zero for invalid DBI). +- Refine/clarify error messages. +- Avoids extra error messages "bad txn" from mdbx_chk when DB is corrupted. -v0.5.0 2019-12-31: - - Fix returning MDBX_RESULT_TRUE from page_alloc(). - - Fix false-positive ASAN issue. - - Fix assertion for `MDBX_NOTLS` option. - - Rework MADV_DONTNEED threshold. - - Fix `mdbx_chk` utility for don't checking some numbers if walking of B-tree was disabled. - - Use page's mp_txnid for basic integrity checking. - - Add MDBX_FORCE_ASSERTIONS built-time option. - - Rework MDBX_DBG_DUMP to avoid performance degradation. - - Rename MDBX_NOSYNC to MDBX_SAFE_NOSYNC for clarity. - - Interpret `ERROR_ACCESS_DENIED` from `OpenProcess()` as 'process exists'. - - Avoid using FILE_FLAG_NO_BUFFERING for compatibility with small database pages. - - Added install section for CMake. +## v0.6.0 2020-01-21: +- Fix `mdbx_load` utility for custom comparators. +- Fix checks related to `MDBX_APPEND` flag inside `mdbx_cursor_put()`. +- Refine/fix dbi_bind() internals. +- Refine/fix handling `STATUS_CONFLICTING_ADDRESSES`. +- Rework `MDBX_DBG_DUMP` option to avoid disk I/O performance degradation. +- Add built-in help to test tool. +- Fix `mdbx_env_set_geometry()` for large page size. +- Fix env_set_geometry() for large pagesize. +- Clarify API description & comments, fix typos. -v0.4.0 2019-12-02: - - Support for Mac OSX, FreeBSD, NetBSD, OpenBSD, DragonFly BSD, OpenSolaris, OpenIndiana (AIX and HP-UX pending). - - Use bootid for decisions of rollback. - - Counting retired pages and extended transaction info. - - Add MDBX_ACCEDE flag for database opening. - - Using OFD-locks and tracking for in-process multi-opening. - - Hot backup into pipe. - - Support for cmake & amalgamated sources. - - Fastest internal sort implementation. - - New internal dirty-list implementation with lazy sorting. - - Support for lazy-sync-to-disk with polling. - - Extended key length. - - Last update transaction number for each sub-database. - - Automatic read ahead enabling/disabling. - - More auto-compactification. - - Using -fsanitize=undefined and -Wpedantic options. - - Rework page merging. - - Nested transactions. - - API description. - - Checking for non-local filesystems to avoid DB corruption. +## v0.5.0 2019-12-31: +- Fix returning MDBX_RESULT_TRUE from page_alloc(). +- Fix false-positive ASAN issue. +- Fix assertion for `MDBX_NOTLS` option. +- Rework `MADV_DONTNEED` threshold. +- Fix `mdbx_chk` utility for don't checking some numbers if walking on the B-tree was disabled. +- Use page's mp_txnid for basic integrity checking. +- Add `MDBX_FORCE_ASSERTIONS` built-time option. +- Rework `MDBX_DBG_DUMP` to avoid performance degradation. +- Rename `MDBX_NOSYNC` to `MDBX_SAFE_NOSYNC` for clarity. +- Interpret `ERROR_ACCESS_DENIED` from `OpenProcess()` as 'process exists'. +- Avoid using `FILE_FLAG_NO_BUFFERING` for compatibility with small database pages. +- Added install section for CMake. + +## v0.4.0 2019-12-02: +- Support for Mac OSX, FreeBSD, NetBSD, OpenBSD, DragonFly BSD, OpenSolaris, OpenIndiana (AIX and HP-UX pending). +- Use bootid for decisions of rollback. +- Counting retired pages and extended transaction info. +- Add `MDBX_ACCEDE` flag for database opening. +- Using OFD-locks and tracking for in-process multi-opening. +- Hot backup into pipe. +- Support for cmake & amalgamated sources. +- Fastest internal sort implementation. +- New internal dirty-list implementation with lazy sorting. +- Support for lazy-sync-to-disk with polling. +- Extended key length. +- Last update transaction number for each sub-database. +- Automatic read ahead enabling/disabling. +- More auto-compactification. +- Using -fsanitize=undefined and -Wpedantic options. +- Rework page merging. +- Nested transactions. +- API description. +- Checking for non-local filesystems to avoid DB corruption. diff --git a/libs/libmdbx/src/GNUmakefile b/libs/libmdbx/src/GNUmakefile index 22a1c7874d..0123a36923 100644 --- a/libs/libmdbx/src/GNUmakefile +++ b/libs/libmdbx/src/GNUmakefile @@ -81,8 +81,8 @@ MAN_SRCDIR := man1/ config.h: mdbx.c $(lastword $(MAKEFILE_LIST)) (echo '#define MDBX_BUILD_TIMESTAMP "$(shell date +%Y-%m-%dT%H:%M:%S%z)"' \ && echo '#define MDBX_BUILD_FLAGS "$(CFLAGS) $(LDFLAGS) $(LIBS)"' \ - && echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"' \ - && echo '#define MDBX_BUILD_TARGET "$(shell set -o pipefail; LC_ALL=C $(CC) -v 2>&1 | grep -i '^Target:' | cut -d ' ' -f 2- || echo 'Please use GCC or CLANG compatible compiler')"' \ + && echo '#define MDBX_BUILD_COMPILER "$(shell (LC_ALL=C $(CC) --version || echo 'Please use GCC or CLANG compatible compiler') | head -1)"' \ + && echo '#define MDBX_BUILD_TARGET "$(shell set -o pipefail; (LC_ALL=C $(CC) -v 2>&1 | grep -i '^Target:' | cut -d ' ' -f 2- || (LC_ALL=C $(CC) --version | grep -qi e2k && echo E2K) || echo 'Please use GCC or CLANG compatible compiler') | head -1)"' \ ) > $@ mdbx-dylib.o: config.h mdbx.c $(lastword $(MAKEFILE_LIST)) @@ -229,8 +229,8 @@ src/version.c: src/version.c.in $(lastword $(MAKEFILE_LIST)) $(git_DIR)/HEAD $(g src/config.h: src/version.c $(lastword $(MAKEFILE_LIST)) (echo '#define MDBX_BUILD_TIMESTAMP "$(shell date +%Y-%m-%dT%H:%M:%S%z)"' \ && echo '#define MDBX_BUILD_FLAGS "$(CFLAGS) $(LDFLAGS) $(LIBS)"' \ - && echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"' \ - && echo '#define MDBX_BUILD_TARGET "$(shell set -o pipefail; LC_ALL=C $(CC) -v 2>&1 | grep -i '^Target:' | cut -d ' ' -f 2- || echo 'Please use GCC or CLANG compatible compiler')"' \ + && echo '#define MDBX_BUILD_COMPILER "$(shell (LC_ALL=C $(CC) --version || echo 'Please use GCC or CLANG compatible compiler') | head -1)"' \ + && echo '#define MDBX_BUILD_TARGET "$(shell set -o pipefail; (LC_ALL=C $(CC) -v 2>&1 | grep -i '^Target:' | cut -d ' ' -f 2- || (LC_ALL=C $(CC) --version | grep -qi e2k && echo E2K) || echo 'Please use GCC or CLANG compatible compiler') | head -1)"' \ && echo '#define MDBX_BUILD_SOURCERY $(MDBX_BUILD_SOURCERY)' \ ) > $@ diff --git a/libs/libmdbx/src/README.md b/libs/libmdbx/src/README.md index 6f02ab467d..2191c563fa 100644 --- a/libs/libmdbx/src/README.md +++ b/libs/libmdbx/src/README.md @@ -9,10 +9,10 @@ database, with [permissive license](LICENSE). _MDBX_ has a specific set of properties and capabilities, focused on creating unique lightweight solutions with extraordinary performance. -1. Allows **swarm of multi-threaded processes to +1. Allows **a swarm of multi-threaded processes to [ACID]((https://en.wikipedia.org/wiki/ACID))ly read and update** several key-value [maps](https://en.wikipedia.org/wiki/Associative_array) and -[multimaps](https://en.wikipedia.org/wiki/Multimap) in a localy-shared +[multimaps](https://en.wikipedia.org/wiki/Multimap) in a locally-shared database. 2. Provides **extraordinary performance**, minimal overhead through @@ -20,11 +20,11 @@ database. `Olog(N)` operations costs by virtue of [B+ tree](https://en.wikipedia.org/wiki/B%2B_tree). -3. Requires **no maintenance and no crash recovery** since doesn't use +3. Requires **no maintenance and no crash recovery** since it doesn't use [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging), but that might be a caveat for write-intensive workloads with durability requirements. -4. **Compact and friendly for fully embeddeding**. Only 25KLOC of `C11`, +4. **Compact and friendly for fully embedding**. Only 25KLOC of `C11`, 64K x86 binary code, no internal threads neither processes, but implements a simplified variant of the [Berkeley DB](https://en.wikipedia.org/wiki/Berkeley_DB) and @@ -44,20 +44,20 @@ neglected in favour of write performance. OpenSolaris, OpenIndiana, NetBSD, OpenBSD and other systems compliant with **POSIX.1-2008**. -Historically, _MDBX_ is deeply revised and extended descendant of amazing +Historically, _MDBX_ is a deeply revised and extended descendant of the amazing [Lightning Memory-Mapped Database](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database). -_MDBX_ inherits all benefits from _LMDB_, but resolves some issues and adds set of improvements. +_MDBX_ inherits all benefits from _LMDB_, but resolves some issues and adds [a set of improvements](#improvements-beyond-lmdb). The next version is under active non-public development from scratch and will be released as **_MithrilDB_** and `libmithrildb` for libraries & packages. Admittedly mythical [Mithril](https://en.wikipedia.org/wiki/Mithril) is resembling silver but being stronger and lighter than steel. Therefore -_MithrilDB_ is rightly relevant name. +_MithrilDB_ is a rightly relevant name. > _MithrilDB_ will be radically different from _libmdbx_ by the new > database format and API based on C++17, as well as the [Apache 2.0 > License](https://www.apache.org/licenses/LICENSE-2.0). The goal of this > revolution is to provide a clearer and robust API, add more features and - > new valuable properties of database. + > new valuable properties of the database. [](https://t.me/libmdbx) [](https://travis-ci.org/erthink/libmdbx) @@ -112,13 +112,13 @@ and [CoW](https://en.wikipedia.org/wiki/Copy-on-write). - Transactions for readers and writers, ones do not block others. -- Writes are strongly serialized. No transactions conflicts nor deadlocks. +- Writes are strongly serialized. No transaction conflicts nor deadlocks. - Readers are [non-blocking](https://en.wikipedia.org/wiki/Non-blocking_algorithm), notwithstanding [snapshot isolation](https://en.wikipedia.org/wiki/Snapshot_isolation). - Nested write transactions. -- Reads scales linearly across CPUs. +- Reads scale linearly across CPUs. - Continuous zero-overhead database compactification. @@ -153,7 +153,7 @@ transaction journal. No crash recovery needed. No maintenance is required. 2. MDBX is based on [B+ tree](https://en.wikipedia.org/wiki/B%2B_tree), so access to database pages is mostly random. Thus SSDs provide a significant performance boost over spinning disks for large databases. -3. MDBX uses [shadow paging](https://en.wikipedia.org/wiki/Shadow_paging) instead of [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging). Thus syncing data to disk might be bottleneck for write intensive workload. +3. MDBX uses [shadow paging](https://en.wikipedia.org/wiki/Shadow_paging) instead of [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging). Thus syncing data to disk might be a bottleneck for write intensive workload. 4. MDBX uses [copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write) for [snapshot isolation](https://en.wikipedia.org/wiki/Snapshot_isolation) during updates, 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, @@ -161,9 +161,9 @@ the database can grow quickly, and result in performance degradation. Try to avoid long running read transactions. 5. MDBX is extraordinarily fast and provides minimal overhead for data access, -so you should reconsider about use brute force techniques and double check your code. +so you should reconsider using brute force techniques and double check your code. On the one hand, in the case of MDBX, a simple linear search may be more profitable than complex indexes. -On the other hand, if you make something suboptimally, you can notice a detrimentally only on sufficiently large data. +On the other hand, if you make something suboptimally, you can notice detrimentally only on sufficiently large data. ### Comparison with other databases For now please refer to [chapter of "BoltDB comparison with other @@ -189,13 +189,13 @@ the user's point of view. 2. Up to 20% faster than _LMDB_ in [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) benchmarks. > Benchmarks of the in-[tmpfs](https://en.wikipedia.org/wiki/Tmpfs) scenarios, - > that tests the speed of engine itself, shown that _libmdbx_ 10-20% faster than _LMDB_. - > These and other results could be easily reproduced with [ioArena](https://github.com/pmwkaa/ioarena) just by `make bench-quartet`, + > that tests the speed of the engine itself, showned that _libmdbx_ 10-20% faster than _LMDB_. + > These and other results could be easily reproduced with [ioArena](https://github.com/pmwkaa/ioarena) just by `make bench-quartet` command, > including comparisons with [RockDB](https://en.wikipedia.org/wiki/RocksDB) > and [WiredTiger](https://en.wikipedia.org/wiki/WiredTiger). 3. Automatic on-the-fly database size adjustment, both increment and reduction. - > _libmdbx_ manage the database size according to parameters specified + > _libmdbx_ manages the database size according to parameters specified > by `mdbx_env_set_geometry()` function, > ones include the growth step and the truncation threshold. > @@ -204,19 +204,19 @@ the user's point of view. 4. Automatic continuous zero-overhead database compactification. > During each commit _libmdbx_ merges suitable freeing pages into unallocated area - > at the end of file, and then truncate unused space when a lot enough of. + > at the end of file, and then truncates unused space when a lot enough of. 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. - > LIFO means that for reuse will be taken latest became unused pages. + > LIFO means that for reuse will be taken the latest becames 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 -be found between a `KEY1` and a `KEY2`. This is prerequisite for build +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. @@ -228,7 +228,7 @@ and/or optimize query execution plans. 11. Callback for lack-of-space condition of database that allows you to control and/or resolve such situations. -12. Support for opening database in the exclusive mode, including on a network share. +12. Support for opening databases in the exclusive mode, including on a network share. ### Added Abilities: @@ -245,14 +245,14 @@ pair, to the first, to the last, or not set to anything. > for a write transaction, reading lag and holdover space for read transactions. 5. Extended update and delete operations. - > _libmdbx_ allows ones _at once_ with getting previous value + > _libmdbx_ allows one _at once_ with getting previous value > and addressing the particular item from multi-value with the same key. ### Other fixes and specifics: -1. Fixed more than 10 significant errors, in particular: page leaks, wrong sub-database statistics, segfault in several conditions, unoptimal page merge strategy, updating an existing record with a change in data size (including for multimap), etc. +1. Fixed more than 10 significant errors, in particular: page leaks, wrong sub-database statistics, segfault in several conditions, nonoptimal page merge strategy, updating an existing record with a change in data size (including for multimap), etc. -2. All cursors can be reused and should be closed explicitly, regardless ones were opened within write or read transaction. +2. All cursors can be reused and should be closed explicitly, regardless ones were opened within a write or read transaction. 3. Opening database handles are spared from race conditions and pre-opening is not needed. @@ -262,15 +262,15 @@ pre-opening is not needed. 5. Guarantee of database integrity even in asynchronous unordered write-to-disk mode. > _libmdbx_ propose additional trade-off by implementing append-like manner for updates > in `MDBX_SAFE_NOSYNC` and `MDBX_WRITEMAP|MDBX_MAPASYNC` modes, that avoid database corruption after a system crash - > contrary to LMDB. Nevertheless, the `MDBX_UTTERLY_NOSYNC` mode available to match LMDB behaviour, - > and for a special use-cases. + > contrary to LMDB. Nevertheless, the `MDBX_UTTERLY_NOSYNC` mode is available to match LMDB behaviour, + > and for special use-cases. 6. On **MacOS & iOS** the `fcntl(F_FULLFSYNC)` syscall is used _by default_ to synchronize data with the disk, as this is [the only way to guarantee data durability](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html) in case of power failure. Unfortunately, in scenarios with high write -intensity, the use of `F_FULLFSYNC` significant degrades performance +intensity, the use of `F_FULLFSYNC` significantly degrades performance compared to LMDB, where the `fsync()` syscall is used. Therefore, _libmdbx_ allows you to override this behavior by defining the `MDBX_OSX_SPEED_INSTEADOF_DURABILITY=1` option while build the library. @@ -279,13 +279,13 @@ _libmdbx_ allows you to override this behavior by defining the it allows place the database on network drives, and provides protection against incompetent user actions (aka [poka-yoke](https://en.wikipedia.org/wiki/Poka-yoke)). Therefore -_libmdbx_ may be a little lag in performance tests from LMDB where a +_libmdbx_ may be a little lag in performance tests from LMDB where the named mutexes are used. ### History At first the development was carried out within the [ReOpenLDAP](https://github.com/erthink/ReOpenLDAP) project. About a -year later _libmdbx_ was separated into standalone project, which was +year later _libmdbx_ was separated into a standalone project, which was [presented at Highload++ 2015 conference](http://www.highload.ru/2015/abstracts/1831.html). @@ -297,7 +297,7 @@ Howard Chu <hyc@openldap.org> is the author of LMDB, from which originated the MDBX in 2015. Martin Hedenfalk <martin@bzero.se> is the author of `btree.c` code, which -was used for begin development of LMDB. +was used to begin development of LMDB. -------------------------------------------------------------------------------- @@ -309,7 +309,7 @@ Usage _libmdbx_ provides two official ways for integration in source code form: 1. Using the amalgamated source code. - > The amalgamated source code includes all files requires to build and + > The amalgamated source code includes all files required to build and > use _libmdbx_, but not for testing _libmdbx_ itself. 2. Adding the complete original source code as a `git submodule`. @@ -319,7 +319,7 @@ _libmdbx_ provides two official ways for integration in source code form: **_Please, avoid using any other techniques._** Otherwise, at least don't ask for support and don't name such chimeras `libmdbx`. -The amalgamated source code could be created from original clone of git +The amalgamated source code could be created from the original clone of git repository on Linux by executing `make dist`. As a result, the desired set of files will be formed in the `dist` subdirectory. @@ -335,7 +335,7 @@ are completely traditional and have minimal prerequirements like target platform. Obviously you need building tools itself, i.e. `git`, `cmake` or GNU `make` with `bash`. -So just use CMake or GNU Make in your habitual manner and feel free to +So just using CMake or GNU Make in your habitual manner and feel free to fill an issue or make pull request in the case something will be unexpected or broken down. @@ -367,7 +367,7 @@ where there are no similar bugs in the pthreads implementation. ### Linux and other platforms with GNU Make To build the library it is enough to execute `make all` in the directory -of source code, and `make check` for execute the basic tests. +of source code, and `make check` to execute the basic tests. If the `make` installed on the system is not GNU Make, there will be a lot of errors from make when trying to build. In this case, perhaps you @@ -380,14 +380,14 @@ Make is called by the gmake command or may be missing. In addition, You need to install the required components: GNU Make, bash, C and C++ compilers compatible with GCC or CLANG. After that, to build the -library, it is enough execute `gmake all` (or `make all`) in the +library, it is enough to execute `gmake all` (or `make all`) in the directory with source code, and `gmake check` (or `make check`) to run the basic tests. ### Windows For build _libmdbx_ on Windows the _original_ CMake and [Microsoft Visual Studio 2019](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio) are -recommended. Otherwise do not forget add `ntdll.lib` to linking. +recommended. Otherwise do not forget to add `ntdll.lib` to linking. Building by MinGW, MSYS or Cygwin is potentially possible. However, these scripts are not tested and will probably require you to modify the @@ -395,15 +395,22 @@ CMakeLists.txt or Makefile respectively. It should be noted that in _libmdbx_ was efforts to resolve runtime dependencies from CRT and other libraries Visual Studio. -For this is enough define the `MDBX_AVOID_CRT` during build. +For this is enough to define the `MDBX_AVOID_CRT` during build. 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), [bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) is required, and -the such testing is recommended with place the test data on the +such testing is recommended with placing the test data on the [RAM-disk](https://en.wikipedia.org/wiki/RAM_drive). +### Windows Subsystem for Linux +_libmdbx_ could be used in [WSL2](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_2) +but NOT in [WSL1](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_1) environment. +This is a consequence of the fundamental shortcomings of _WSL1_ and cannot be fixed. +To avoid data loss, _libmdbx_ returns the `ENOLCK` (37, "No record locks available") +error when opening the database in a _WSL1_ environment. + ### MacOS Current [native build tools](https://en.wikipedia.org/wiki/Xcode) for MacOS include GNU Make, CLANG and an outdated version of bash. @@ -427,13 +434,6 @@ To build _libmdbx_ for iOS, we recommend using CMake with the "[toolchain file](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html)" from the [ios-cmake](https://github.com/leetal/ios-cmake) project. -### Windows Subsystem for Linux -_libmdbx_ could be using in [WSL2](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_2) -but NOT in [WSL1](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_1) environment. -This is a consequence of the fundamental shortcomings of _WSL1_ and cannot be fixed. -To avoid data loss, _libmdbx_ returns the `ENOLCK` (37, "No record locks available") -error when opening the database in a _WSL1_ environment. - ## API description For more information and API description see the [mdbx.h](mdbx.h) header. Please do not hesitate to point out errors in the documentation, @@ -461,7 +461,7 @@ SSD SAMSUNG MZNTD512HAGL-000L1 (DXT23L0Q) 512 Gb. Here showed sum of performance metrics in 3 benchmarks: - - Read/Search on machine with 4 logical CPU in HyperThreading mode (i.e. actually 2 physical CPU cores); + - Read/Search on the machine with 4 logical CPUs in HyperThreading mode (i.e. actually 2 physical CPU cores); - Transactions with [CRUD](https://en.wikipedia.org/wiki/CRUD) operations in sync-write mode (fdatasync is called after each @@ -486,7 +486,7 @@ Here showed sum of performance metrics in 3 benchmarks: ## Read Scalability Summary performance with concurrent read/search queries in 1-2-4-8 -threads on machine with 4 logical CPU in HyperThreading mode (i.e. actually 2 physical CPU cores). +threads on the machine with 4 logical CPUs in HyperThreading mode (i.e. actually 2 physical CPU cores).  @@ -502,12 +502,12 @@ threads on machine with 4 logical CPU in HyperThreading mode (i.e. actually 2 ph execution time, cross marks standard deviation. **10,000 transactions in sync-write mode**. In case of a crash all data -is consistent and state is right after last successful transaction. +is consistent and conforms to the last successful transaction. The [fdatasync](https://linux.die.net/man/2/fdatasync) syscall is used after each write transaction in this mode. In the benchmark each transaction contains combined CRUD operations (2 -inserts, 1 read, 1 update, 1 delete). Benchmark starts on empty database +inserts, 1 read, 1 update, 1 delete). Benchmark starts on an empty database and after full run the database contains 10,000 small key-value records.  @@ -524,15 +524,15 @@ and after full run the database contains 10,000 small key-value records. execution time, cross marks standard deviation. **100,000 transactions in lazy-write mode**. In case of a crash all data -is consistent and state is right after one of last transactions, but +is consistent and conforms to the one of last successful transactions, but transactions after it will be lost. Other DB engines use [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) or transaction -journal for that, which in turn depends on order of operations in +journal for that, which in turn depends on order of operations in the journaled filesystem. _libmdbx_ doesn't use WAL and hands I/O operations to filesystem and OS kernel (mmap). In the benchmark each transaction contains combined CRUD operations (2 -inserts, 1 read, 1 update, 1 delete). Benchmark starts on empty database +inserts, 1 read, 1 update, 1 delete). Benchmark starts on an empty database and after full run the database contains 100,000 small key-value records. @@ -550,15 +550,13 @@ records. execution time of transactions. Each interval shows minimal and maximum execution time, cross marks standard deviation. -**1,000,000 transactions in async-write mode**. In case of a crash all -data will be consistent and state will be right after one of last -transactions, but lost transaction count is much higher than in +**1,000,000 transactions in async-write mode**. In case of a crash all data is consistent and conforms to the one of last successful transactions, but lost transaction count is much higher than in lazy-write mode. All DB engines in this mode do as little writes as possible on persistent storage. _libmdbx_ uses [msync(MS_ASYNC)](https://linux.die.net/man/2/msync) in this mode. In the benchmark each transaction contains combined CRUD operations (2 -inserts, 1 read, 1 update, 1 delete). Benchmark starts on empty database +inserts, 1 read, 1 update, 1 delete). Benchmark starts on an empty database and after full run the database contains 10,000 small key-value records.  @@ -583,7 +581,7 @@ which prevents to meaningfully compare it with them. All benchmark data is gathered by [getrusage()](http://man7.org/linux/man-pages/man2/getrusage.2.html) -syscall and by scanning data directory. +syscall and by scanning the data directory.  diff --git a/libs/libmdbx/src/appveyor.yml b/libs/libmdbx/src/appveyor.yml index 15fd7dbb25..66323a6037 100644 --- a/libs/libmdbx/src/appveyor.yml +++ b/libs/libmdbx/src/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.8.0.{build} +version: 0.8.2.{build} environment: matrix: diff --git a/libs/libmdbx/src/cmake/compiler.cmake b/libs/libmdbx/src/cmake/compiler.cmake index 544a22d9fb..e13225d63d 100644 --- a/libs/libmdbx/src/cmake/compiler.cmake +++ b/libs/libmdbx/src/cmake/compiler.cmake @@ -73,23 +73,6 @@ if(CMAKE_CXX_COMPILER_LOADED AND CMAKE_CXX_COMPILER_ID MATCHES ".*[Cc][Ll][Aa][N set(CMAKE_COMPILER_IS_GNUCXX OFF) endif() -# Hard coding the compiler version is ugly from cmake POV, but -# at least gives user a friendly error message. The most critical -# demand for C++ compiler is support of C++11 lambdas, added -# only in version 4.5 https://gcc.gnu.org/projects/cxx0x.html -if(CMAKE_COMPILER_IS_GNUCC) - if(CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5) - message(FATAL_ERROR " - Your GCC version is ${CMAKE_C_COMPILER_VERSION}, please update") - endif() -endif() -if(CMAKE_COMPILER_IS_GNUCXX) - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.5) - message(FATAL_ERROR " - Your G++ version is ${CMAKE_CXX_COMPILER_VERSION}, please update") - endif() -endif() - if(CMAKE_C_COMPILER_LOADED) # Check for Elbrus lcc execute_process(COMMAND ${CMAKE_C_COMPILER} --version @@ -138,6 +121,25 @@ if(CMAKE_CXX_COMPILER_LOADED) unset(tmp_lxx_probe_result) endif() +# Hard coding the compiler version is ugly from cmake POV, but +# at least gives user a friendly error message. The most critical +# demand for C++ compiler is support of C++11 lambdas, added +# only in version 4.5 https://gcc.gnu.org/projects/cxx0x.html +if(CMAKE_COMPILER_IS_GNUCC) + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5 + AND NOT CMAKE_COMPILER_IS_ELBRUSC) + message(FATAL_ERROR " + Your GCC version is ${CMAKE_C_COMPILER_VERSION}, please update") + endif() +endif() +if(CMAKE_COMPILER_IS_GNUCXX) + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.5 + AND NOT CMAKE_COMPILER_IS_ELBRUSCXX) + message(FATAL_ERROR " + Your G++ version is ${CMAKE_CXX_COMPILER_VERSION}, please update") + endif() +endif() + if(CMAKE_CL_64) set(MSVC64 1) endif() @@ -252,6 +254,17 @@ else() set(CMAKE_REQUIRED_FLAGS "") endif() +# Crutch for old C++ compilers and/or CMake to enabling C++11 +if(CMAKE_CXX_COMPILER_LOADED) + list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_11 HAS_CXX11) + if(HAS_CXX11 LESS 0) + check_compiler_flag("-std=gnu++11" CXX_FALLBACK_STDGNU11) + if(NOT CXX_FALLBACK_STDGNU11) + check_compiler_flag("-std=c++11" CXX_FALLBACK_STD11) + endif() + endif() +endif() + # Check for LTO support by GCC if(CMAKE_COMPILER_IS_GNU${CMAKE_PRIMARY_LANG}) unset(gcc_collect) @@ -485,8 +498,12 @@ endif() macro(setup_compile_flags) # save initial C/CXX flags if(NOT INITIAL_CMAKE_FLAGS_SAVED) - set(INITIAL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "Initial CMake's flags" FORCE) - set(INITIAL_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} CACHE STRING "Initial CMake's flags" FORCE) + if(CMAKE_CXX_COMPILER_LOADED) + set(INITIAL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "Initial CMake's flags" FORCE) + endif() + if(CMAKE_C_COMPILER_LOADED) + set(INITIAL_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} CACHE STRING "Initial CMake's flags" FORCE) + endif() set(INITIAL_CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} CACHE STRING "Initial CMake's flags" FORCE) set(INITIAL_CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} CACHE STRING "Initial CMake's flags" FORCE) set(INITIAL_CMAKE_STATIC_LINKER_FLAGS ${CMAKE_STATIC_LINKER_FLAGS} CACHE STRING "Initial CMake's flags" FORCE) @@ -495,8 +512,18 @@ macro(setup_compile_flags) endif() # reset C/CXX flags - set(CXX_FLAGS ${INITIAL_CMAKE_CXX_FLAGS}) - set(C_FLAGS ${INITIAL_CMAKE_C_FLAGS}) + if(CMAKE_CXX_COMPILER_LOADED) + set(CXX_FLAGS ${INITIAL_CMAKE_CXX_FLAGS}) + # Crutch for old C++ compilers and/or CMake to enabling C++11 + if(CXX_FALLBACK_STDGNU11) + add_compile_flags("CXX" "-std=gnu++11") + elseif(CXX_FALLBACK_STD11) + add_compile_flags("CXX" "-std=c++11") + endif() + endif() + if(CMAKE_C_COMPILER_LOADED) + set(C_FLAGS ${INITIAL_CMAKE_C_FLAGS}) + endif() set(EXE_LINKER_FLAGS ${INITIAL_CMAKE_EXE_LINKER_FLAGS}) set(SHARED_LINKER_FLAGS ${INITIAL_CMAKE_SHARED_LINKER_FLAGS}) set(STATIC_LINKER_FLAGS ${INITIAL_CMAKE_STATIC_LINKER_FLAGS}) @@ -624,6 +651,10 @@ macro(setup_compile_flags) add_compile_flags("C;CXX" -fsanitize=address) endif() + if(ENABLE_UBSAN) + add_compile_flags("C;CXX" -fsanitize=undefined) + endif() + if(ENABLE_GCOV) if(NOT HAVE_GCOV) message(FATAL_ERROR @@ -719,31 +750,37 @@ macro(setup_compile_flags) endif() # push C/CXX flags into the cache - set(CMAKE_CXX_FLAGS ${CXX_FLAGS} CACHE STRING "Flags used by the C++ compiler during all build types" FORCE) - set(CMAKE_C_FLAGS ${C_FLAGS} CACHE STRING "Flags used by the C compiler during all build types" FORCE) + if(CMAKE_CXX_COMPILER_LOADED) + set(CMAKE_CXX_FLAGS ${CXX_FLAGS} CACHE STRING "Flags used by the C++ compiler during all build types" FORCE) + unset(CXX_FLAGS) + endif() + if(CMAKE_C_COMPILER_LOADED) + set(CMAKE_C_FLAGS ${C_FLAGS} CACHE STRING "Flags used by the C compiler during all build types" FORCE) + unset(C_FLAGS) + endif() set(CMAKE_EXE_LINKER_FLAGS ${EXE_LINKER_FLAGS} CACHE STRING "Flags used by the linker" FORCE) set(CMAKE_SHARED_LINKER_FLAGS ${SHARED_LINKER_FLAGS} CACHE STRING "Flags used by the linker during the creation of dll's" FORCE) set(CMAKE_STATIC_LINKER_FLAGS ${STATIC_LINKER_FLAGS} CACHE STRING "Flags used by the linker during the creation of static libraries" FORCE) set(CMAKE_MODULE_LINKER_FLAGS ${MODULE_LINKER_FLAGS} CACHE STRING "Flags used by the linker during the creation of modules" FORCE) - unset(CXX_FLAGS) - unset(C_FLAGS) unset(EXE_LINKER_FLAGS) unset(SHARED_LINKER_FLAGS) unset(STATIC_LINKER_FLAGS) unset(MODULE_LINKER_FLAGS) endmacro(setup_compile_flags) -# determine library for for std::filesystem -set(LIBCXX_FILESYSTEM "") -if(CMAKE_COMPILER_IS_GNUCXX) - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) - set(LIBCXX_FILESYSTEM "stdc++fs") - endif() -elseif(CMAKE_COMPILER_IS_CLANG) - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) - set(LIBCXX_FILESYSTEM "c++experimental") - else() - set(LIBCXX_FILESYSTEM "stdc++fs") +if(CMAKE_CXX_COMPILER_LOADED) + # determine library for for std::filesystem + set(LIBCXX_FILESYSTEM "") + if(CMAKE_COMPILER_IS_GNUCXX) + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) + set(LIBCXX_FILESYSTEM "stdc++fs") + endif() + elseif(CMAKE_COMPILER_IS_CLANG) + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) + set(LIBCXX_FILESYSTEM "c++experimental") + else() + set(LIBCXX_FILESYSTEM "stdc++fs") + endif() endif() endif() diff --git a/libs/libmdbx/src/cmake/profile.cmake b/libs/libmdbx/src/cmake/profile.cmake index c1c4f78e19..cf285653c5 100644 --- a/libs/libmdbx/src/cmake/profile.cmake +++ b/libs/libmdbx/src/cmake/profile.cmake @@ -42,4 +42,7 @@ endif() option(ENABLE_ASAN "Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF) +option(ENABLE_UBSAN + "Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector based on compiler instrumentation" OFF) + cmake_policy(POP) diff --git a/libs/libmdbx/src/mdbx.h b/libs/libmdbx/src/mdbx.h index 0c6b3de65b..97750bc05d 100644 --- a/libs/libmdbx/src/mdbx.h +++ b/libs/libmdbx/src/mdbx.h @@ -658,7 +658,7 @@ typedef pthread_t mdbx_tid_t; /*----------------------------------------------------------------------------*/ -/* MDBX version 0.8.0, released 2020-06-05 */ +/* MDBX version 0.8.2, released 2020-07-06 */ #define MDBX_VERSION_MAJOR 0 #define MDBX_VERSION_MINOR 8 @@ -1004,9 +1004,6 @@ LIBMDBX_API const char *mdbx_dump_val(const MDBX_val *key, char *const buf, * database pages and more work for tracking ones, which neuters a * performance boost caused by the MDBX_WRITEMAP mode. * - * NOTE: MDBX don't allow to mix processes with and without MDBX_WRITEMAP on - * the same environment. In such case MDBX_INCOMPATIBLE will be generated. - * * - with MDBX_WRITEMAP = all data will be mapped into memory in the read-write * mode. This offers a significant performance benefit, since the data will * be modified directly in mapped memory and then flushed to disk by @@ -1616,9 +1613,9 @@ LIBMDBX_API int mdbx_env_create(MDBX_env **penv); * NOTE: MDB_NOLOCK flag don't supported by MDBX, * try use MDBX_EXCLUSIVE as a replacement. * - * NOTE: MDBX don't allow to mix processes with different MDBX_WRITEMAP, - * MDBX_SAFE_NOSYNC, MDBX_NOMETASYNC, MDBX_MAPASYNC flags on the same - * environment. In such case MDBX_INCOMPATIBLE will be returned. + * NOTE: MDBX don't allow to mix processes with different MDBX_SAFE_NOSYNC, + * MDBX_NOMETASYNC, MDBX_MAPASYNC flags on the same environment. + * In such case MDBX_INCOMPATIBLE will be returned. * * If the database is already exist and parameters specified early by * mdbx_env_set_geometry() are incompatible (i.e. for instance, different page @@ -1642,9 +1639,8 @@ LIBMDBX_API int mdbx_env_create(MDBX_env **penv); * or the current process tries to open environment * more than once. * - MDBX_INCOMPATIBLE = Environment is already opened by another process, - * but with different set of MDBX_WRITEMAP, - * MDBX_SAFE_NOSYNC, MDBX_NOMETASYNC, MDBX_MAPASYNC - * flags. + * but with different set of MDBX_SAFE_NOSYNC, + * MDBX_NOMETASYNC, MDBX_MAPASYNC flags. * Or if the database is already exist and * parameters specified early by * mdbx_env_set_geometry() are incompatible (i.e. @@ -2690,7 +2686,7 @@ LIBMDBX_API int mdbx_dbi_open_ex(MDBX_txn *txn, const char *name, LIBMDBX_API int mdbx_dbi_open(MDBX_txn *txn, const char *name, unsigned flags, MDBX_dbi *dbi); -/* Key-making functions to avoid custom comparators. +/* Key-making (value-to-key) functions to avoid custom comparators. * * The mdbx_key_from_jsonInteger() build key which are comparable with * keys created by mdbx_key_from_double(). So this allow mix int64 and IEEE754 @@ -2709,6 +2705,13 @@ __inline uint32_t mdbx_key_from_int32(const int32_t i32) { return UINT32_C(0x80000000) + i32; } +/* Key-reverse (key-to-value) functions to avoid custom comparators. */ +LIBMDBX_API int64_t mdbx_jsonInteger_from_key(const MDBX_val); +LIBMDBX_API double mdbx_double_from_key(const MDBX_val); +LIBMDBX_API float mdbx_float_from_key(const MDBX_val); +LIBMDBX_API int32_t mdbx_int32_from_key(const MDBX_val); +LIBMDBX_API int64_t mdbx_int64_from_key(const MDBX_val); + /* Retrieve statistics for a database. * * [in] txn A transaction handle returned by mdbx_txn_begin(). @@ -2748,10 +2751,10 @@ LIBMDBX_API int mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi, * discarding result from the last argument. * * Returns A non-zero error value on failure and 0 on success. */ -#define MDBX_TBL_DIRTY 0x01 /* DB was written in this txn */ -#define MDBX_TBL_STALE 0x02 /* Named-DB record is older than txnID */ -#define MDBX_TBL_FRESH 0x04 /* Named-DB handle opened in this txn */ -#define MDBX_TBL_CREAT 0x08 /* Named-DB handle created in this txn */ +#define MDBX_DBI_DIRTY 0x01 /* DB was written in this txn */ +#define MDBX_DBI_STALE 0x02 /* Named-DB record is older than txnID */ +#define MDBX_DBI_FRESH 0x04 /* Named-DB handle opened in this txn */ +#define MDBX_DBI_CREAT 0x08 /* Named-DB handle created in this txn */ LIBMDBX_API int mdbx_dbi_flags_ex(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags, unsigned *state); LIBMDBX_API int mdbx_dbi_flags(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags); @@ -3376,6 +3379,7 @@ LIBMDBX_API int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result, * Returns < 0 if a < b, 0 if a == b, > 0 if a > b */ LIBMDBX_API int mdbx_cmp(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, const MDBX_val *b); +LIBMDBX_API MDBX_cmp_func *mdbx_get_keycmp(unsigned flags); /* Compare two data items according to a particular database. * @@ -3390,6 +3394,7 @@ LIBMDBX_API int mdbx_cmp(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, * Returns < 0 if a < b, 0 if a == b, > 0 if a > b */ LIBMDBX_API int mdbx_dcmp(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, const MDBX_val *b); +LIBMDBX_API MDBX_cmp_func *mdbx_get_datacmp(unsigned flags); /* A callback function used to enumerate the reader lock table. * diff --git a/libs/libmdbx/src/src/core.c b/libs/libmdbx/src/src/core.c index 21a0e46fcb..62d7c8e38c 100644 --- a/libs/libmdbx/src/src/core.c +++ b/libs/libmdbx/src/src/core.c @@ -3187,11 +3187,11 @@ static int __must_check_result mdbx_setup_dbx(MDBX_dbx *const dbx, const MDBX_db *const db, const unsigned pagesize); -static MDBX_cmp_func mdbx_cmp_memn, mdbx_cmp_memnr, mdbx_cmp_int_align4, - mdbx_cmp_int_align2, mdbx_cmp_int_unaligned, mdbx_cmp_lenfast; +static MDBX_cmp_func cmp_lexical, cmp_reverse, cmp_int_align4, cmp_int_align2, + cmp_int_unaligned, cmp_lenfast; -static MDBX_cmp_func *mdbx_default_keycmp(unsigned flags); -static MDBX_cmp_func *mdbx_default_datacmp(unsigned flags); +static __inline MDBX_cmp_func *get_default_keycmp(unsigned flags); +static __inline MDBX_cmp_func *get_default_datacmp(unsigned flags); static const char *__mdbx_strerr(int errnum) { /* Table of descriptions for MDBX errors */ @@ -4128,7 +4128,7 @@ mark_done: if (all) { /* Mark dirty root pages */ for (i = 0; i < txn->mt_numdbs; i++) { - if (txn->mt_dbstate[i] & DB_DIRTY) { + if (txn->mt_dbistate[i] & DBI_DIRTY) { pgno_t pgno = txn->mt_dbs[i].md_root; if (pgno == P_INVALID) continue; @@ -4658,7 +4658,7 @@ static int __cold mdbx_set_readahead(MDBX_env *env, const size_t offset, static __cold int mdbx_mapresize(MDBX_env *env, const pgno_t used_pgno, const pgno_t size_pgno, - const pgno_t limit_pgno) { + const pgno_t limit_pgno, const bool implicit) { if ((env->me_flags & MDBX_WRITEMAP) && *env->me_unsynced_pages) { int err = mdbx_msync(&env->me_dxb_mmap, 0, pgno_align2os_bytes(env, used_pgno), true); @@ -4711,16 +4711,40 @@ static __cold int mdbx_mapresize(MDBX_env *env, const pgno_t used_pgno, mdbx_error("failed suspend-for-remap: errcode %d", rc); goto bailout; } -#else + const bool mapping_can_be_moved = !implicit; +#else /* Windows */ /* Acquire guard to avoid collision between read and write txns * around env->me_dbgeo */ + bool mapping_can_be_moved = false; int rc = mdbx_fastmutex_acquire(&env->me_remap_guard); if (unlikely(rc != MDBX_SUCCESS)) return rc; if (limit_bytes == env->me_dxb_mmap.limit && size_bytes == env->me_dxb_mmap.current) goto bailout; -#endif /* Windows */ + + if (limit_bytes != env->me_dxb_mmap.limit && env->me_lck && !implicit) { + rc = mdbx_rdt_lock(env) /* lock readers table until remap done */; + if (unlikely(rc != MDBX_SUCCESS)) + goto bailout; + + /* looking for readers from this process */ + MDBX_lockinfo *const lck = env->me_lck; + const unsigned snap_nreaders = lck->mti_numreaders; + mapping_can_be_moved = true; + for (unsigned i = 0; i < snap_nreaders; ++i) { + if (lck->mti_readers[i].mr_pid == env->me_pid && + lck->mti_readers[i].mr_tid != mdbx_thread_self()) { + /* the base address of the mapping can't be changed since + * the other reader thread from this process exists. */ + mdbx_rdt_unlock(env); + mapping_can_be_moved = false; + break; + } + } + } + +#endif /* ! Windows */ const size_t prev_size = env->me_dxb_mmap.current; if (size_bytes < prev_size) { @@ -4758,7 +4782,8 @@ static __cold int mdbx_mapresize(MDBX_env *env, const pgno_t used_pgno, *env->me_discarded_tail = size_pgno; } - rc = mdbx_mresize(env->me_flags, &env->me_dxb_mmap, size_bytes, limit_bytes); + rc = mdbx_mresize(env->me_flags, &env->me_dxb_mmap, size_bytes, limit_bytes, + mapping_can_be_moved); if (rc == MDBX_SUCCESS && (env->me_flags & MDBX_NORDAHEAD) == 0) { const int readahead = mdbx_is_readahead_reasonable(size_bytes, 0); if (readahead == MDBX_RESULT_FALSE) @@ -4829,6 +4854,8 @@ bailout: mdbx_free(suspended); } #else + if (env->me_lck && mapping_can_be_moved) + mdbx_rdt_unlock(env); int err = mdbx_fastmutex_release(&env->me_remap_guard); #endif /* Windows */ if (err != MDBX_SUCCESS) { @@ -4838,6 +4865,21 @@ bailout: return rc; } +static __cold int mdbx_mapresize_implicit(MDBX_env *env, const pgno_t used_pgno, + const pgno_t size_pgno, + const pgno_t limit_pgno) { + const pgno_t mapped_pgno = bytes2pgno(env, env->me_dxb_mmap.limit); + mdbx_assert(env, mapped_pgno >= used_pgno); + return mdbx_mapresize( + env, used_pgno, size_pgno, + (size_pgno > mapped_pgno) + ? limit_pgno + : /* The actual mapsize may be less since the geo.upper may be changed + by other process. So, avoids remapping until it necessary. */ + mapped_pgno, + true); +} + static int mdbx_meta_unsteady(MDBX_env *env, const txnid_t last_steady, MDBX_meta *const meta) { const uint64_t wipe = MDBX_DATASIGN_NONE; @@ -5302,7 +5344,8 @@ skip_cache: mdbx_verbose("try growth datafile to %" PRIaPGNO " pages (+%" PRIaPGNO ")", aligned, aligned - txn->mt_end_pgno); - rc = mdbx_mapresize(env, txn->mt_next_pgno, aligned, txn->mt_geo.upper); + rc = mdbx_mapresize_implicit(env, txn->mt_next_pgno, aligned, + txn->mt_geo.upper); if (rc == MDBX_SUCCESS) { env->me_txn->mt_end_pgno = aligned; goto done; @@ -5711,7 +5754,7 @@ static int mdbx_cursor_shadow(MDBX_txn *src, MDBX_txn *dst) { * user may not use mc until dst ends. But we need a valid * txn pointer here for cursor fixups to keep working. */ mc->mc_txn = dst; - mc->mc_dbstate = &dst->mt_dbstate[i]; + mc->mc_dbistate = &dst->mt_dbistate[i]; if ((mx = mc->mc_xcursor) != NULL) { *(MDBX_xcursor *)(bk + 1) = *mx; mx->mx_cursor.mc_txn = dst; @@ -5750,7 +5793,7 @@ static void mdbx_cursors_eot(MDBX_txn *txn, unsigned merge) { mc->mc_backup = bk->mc_backup; mc->mc_txn = bk->mc_txn; mc->mc_db = bk->mc_db; - mc->mc_dbstate = bk->mc_dbstate; + mc->mc_dbistate = bk->mc_dbistate; if ((mx = mc->mc_xcursor) != NULL) mx->mx_cursor.mc_txn = bk->mc_txn; } else { @@ -6080,13 +6123,13 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) { /* Setup db info */ mdbx_compiler_barrier(); for (unsigned i = CORE_DBS; i < txn->mt_numdbs; i++) { - unsigned x = env->me_dbflags[i]; - txn->mt_dbs[i].md_flags = x & PERSISTENT_FLAGS; - txn->mt_dbstate[i] = - (x & MDBX_VALID) ? DB_VALID | DB_USRVALID | DB_STALE : 0; + const unsigned db_flags = env->me_dbflags[i]; + txn->mt_dbs[i].md_flags = db_flags & DB_PERSISTENT_FLAGS; + txn->mt_dbistate[i] = + (db_flags & DB_VALID) ? DBI_VALID | DBI_USRVALID | DBI_STALE : 0; } - txn->mt_dbstate[MAIN_DBI] = DB_VALID | DB_USRVALID; - txn->mt_dbstate[FREE_DBI] = DB_VALID; + txn->mt_dbistate[MAIN_DBI] = DBI_VALID | DBI_USRVALID; + txn->mt_dbistate[FREE_DBI] = DBI_VALID; if (unlikely(env->me_flags & MDBX_FATAL_ERROR)) { mdbx_warning("%s", "environment had fatal error, must shutdown!"); @@ -6101,7 +6144,8 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) { goto bailout; } rc = mdbx_mapresize(env, txn->mt_next_pgno, txn->mt_end_pgno, - txn->mt_geo.upper); + txn->mt_geo.upper, + (txn->mt_flags & MDBX_RDONLY) ? true : false); if (rc != MDBX_SUCCESS) goto bailout; } @@ -6279,7 +6323,7 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, memset(txn, 0, tsize); txn->mt_dbxs = env->me_dbxs; /* static */ txn->mt_dbs = (MDBX_db *)((char *)txn + tsize); - txn->mt_dbstate = (uint8_t *)txn + size - env->me_maxdbs; + txn->mt_dbistate = (uint8_t *)txn + size - env->me_maxdbs; txn->mt_flags = flags; txn->mt_env = env; @@ -6327,9 +6371,9 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, txn->mt_numdbs = parent->mt_numdbs; txn->mt_owner = parent->mt_owner; memcpy(txn->mt_dbs, parent->mt_dbs, txn->mt_numdbs * sizeof(MDBX_db)); - /* Copy parent's mt_dbstate, but clear DB_NEW */ + /* Copy parent's mt_dbistate, but clear DB_NEW */ for (unsigned i = 0; i < txn->mt_numdbs; i++) - txn->mt_dbstate[i] = parent->mt_dbstate[i] & ~(DB_FRESH | DB_CREAT); + txn->mt_dbistate[i] = parent->mt_dbistate[i] & ~(DBI_FRESH | DBI_CREAT); mdbx_tassert(parent, parent->mt_parent || parent->tw.dirtyroom + parent->tw.dirtylist->length == @@ -6503,10 +6547,9 @@ static void mdbx_dbis_update(MDBX_txn *txn, int keep) { if (n) { bool locked = false; MDBX_env *env = txn->mt_env; - uint8_t *tdbflags = txn->mt_dbstate; for (unsigned i = n; --i >= CORE_DBS;) { - if (likely((tdbflags[i] & DB_CREAT) == 0)) + if (likely((txn->mt_dbistate[i] & DBI_CREAT) == 0)) continue; if (!locked) { mdbx_ensure(env, @@ -6514,7 +6557,7 @@ static void mdbx_dbis_update(MDBX_txn *txn, int keep) { locked = true; } if (keep) { - env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDBX_VALID; + env->me_dbflags[i] = txn->mt_dbs[i].md_flags | DB_VALID; mdbx_compiler_barrier(); if (env->me_numdbs <= i) env->me_numdbs = i + 1; @@ -6648,8 +6691,8 @@ static int mdbx_txn_end(MDBX_txn *txn, unsigned mode) { if (parent->mt_geo.upper != txn->mt_geo.upper || parent->mt_geo.now != txn->mt_geo.now) { /* undo resize performed by child txn */ - rc = mdbx_mapresize(env, parent->mt_next_pgno, parent->mt_geo.now, - parent->mt_geo.upper); + rc = mdbx_mapresize_implicit(env, parent->mt_next_pgno, + parent->mt_geo.now, parent->mt_geo.upper); if (rc == MDBX_RESULT_TRUE) { /* unable undo resize (it is regular for Windows), * therefore promote size changes from child to the parent txn */ @@ -6755,16 +6798,16 @@ static __cold int mdbx_audit_ex(MDBX_txn *txn, unsigned retired_stored, mdbx_tassert(txn, rc == MDBX_NOTFOUND); for (MDBX_dbi i = FREE_DBI; i < txn->mt_numdbs; i++) - txn->mt_dbstate[i] &= ~DB_AUDITED; + txn->mt_dbistate[i] &= ~DBI_AUDITED; pgno_t count = 0; for (MDBX_dbi i = FREE_DBI; i <= MAIN_DBI; i++) { - if (!(txn->mt_dbstate[i] & DB_VALID)) + if (!(txn->mt_dbistate[i] & DBI_VALID)) continue; rc = mdbx_cursor_init(&cx.outer, txn, i); if (unlikely(rc != MDBX_SUCCESS)) return rc; - txn->mt_dbstate[i] |= DB_AUDITED; + txn->mt_dbistate[i] |= DBI_AUDITED; if (txn->mt_dbs[i].md_root == P_INVALID) continue; count += txn->mt_dbs[i].md_branch_pages + txn->mt_dbs[i].md_leaf_pages + @@ -6784,14 +6827,14 @@ static __cold int mdbx_audit_ex(MDBX_txn *txn, unsigned retired_stored, memcpy(db = &db_copy, node_data(node), sizeof(db_copy)); if ((txn->mt_flags & MDBX_RDONLY) == 0) { for (MDBX_dbi k = txn->mt_numdbs; --k > MAIN_DBI;) { - if ((txn->mt_dbstate[k] & DB_VALID) && + if ((txn->mt_dbistate[k] & DBI_VALID) && /* txn->mt_dbxs[k].md_name.iov_len > 0 && */ node_ks(node) == txn->mt_dbxs[k].md_name.iov_len && memcmp(node_key(node), txn->mt_dbxs[k].md_name.iov_base, node_ks(node)) == 0) { - txn->mt_dbstate[k] |= DB_AUDITED; - if (txn->mt_dbstate[k] & DB_DIRTY) { - mdbx_tassert(txn, (txn->mt_dbstate[k] & DB_STALE) == 0); + txn->mt_dbistate[k] |= DBI_AUDITED; + if (txn->mt_dbistate[k] & DBI_DIRTY) { + mdbx_tassert(txn, (txn->mt_dbistate[k] & DBI_STALE) == 0); db = txn->mt_dbs + k; } break; @@ -6808,9 +6851,10 @@ static __cold int mdbx_audit_ex(MDBX_txn *txn, unsigned retired_stored, } for (MDBX_dbi i = FREE_DBI; i < txn->mt_numdbs; i++) { - if ((txn->mt_dbstate[i] & (DB_VALID | DB_AUDITED | DB_STALE)) != DB_VALID) + if ((txn->mt_dbistate[i] & (DBI_VALID | DBI_AUDITED | DBI_STALE)) != + DBI_VALID) continue; - if (F_ISSET(txn->mt_dbstate[i], DB_DIRTY | DB_CREAT)) { + if (F_ISSET(txn->mt_dbistate[i], DBI_DIRTY | DBI_CREAT)) { count += txn->mt_dbs[i].md_branch_pages + txn->mt_dbs[i].md_leaf_pages + txn->mt_dbs[i].md_overflow_pages; } else { @@ -6819,7 +6863,7 @@ static __cold int mdbx_audit_ex(MDBX_txn *txn, unsigned retired_stored, txn->mt_parent ? "nested-" : "", txn->mt_txnid, i, (int)txn->mt_dbxs[i].md_name.iov_len, (const char *)txn->mt_dbxs[i].md_name.iov_base, - txn->mt_dbstate[i]); + txn->mt_dbistate[i]); } } @@ -7701,24 +7745,24 @@ static __cold bool mdbx_txn_import_dbi(MDBX_txn *txn, MDBX_dbi dbi) { mdbx_compiler_barrier(); for (unsigned i = CORE_DBS; i < snap_numdbs; ++i) { if (i >= txn->mt_numdbs) - txn->mt_dbstate[i] = 0; - if (!(txn->mt_dbstate[i] & DB_USRVALID) && - (env->me_dbflags[i] & MDBX_VALID)) { - txn->mt_dbs[i].md_flags = env->me_dbflags[i] & PERSISTENT_FLAGS; - txn->mt_dbstate[i] = DB_VALID | DB_USRVALID | DB_STALE; + txn->mt_dbistate[i] = 0; + if (!(txn->mt_dbistate[i] & DBI_USRVALID) && + (env->me_dbflags[i] & DB_VALID)) { + txn->mt_dbs[i].md_flags = env->me_dbflags[i] & DB_PERSISTENT_FLAGS; + txn->mt_dbistate[i] = DBI_VALID | DBI_USRVALID | DBI_STALE; mdbx_tassert(txn, txn->mt_dbxs[i].md_cmp != NULL); } } txn->mt_numdbs = snap_numdbs; mdbx_ensure(env, mdbx_fastmutex_release(&env->me_dbi_lock) == MDBX_SUCCESS); - return txn->mt_dbstate[dbi] & DB_USRVALID; + return txn->mt_dbistate[dbi] & DBI_USRVALID; } /* Check txn and dbi arguments to a function */ static __always_inline bool mdbx_txn_dbi_exists(MDBX_txn *txn, MDBX_dbi dbi, unsigned validity) { - if (likely(dbi < txn->mt_numdbs && (txn->mt_dbstate[dbi] & validity))) + if (likely(dbi < txn->mt_numdbs && (txn->mt_dbistate[dbi] & validity))) return true; return mdbx_txn_import_dbi(txn, dbi); @@ -7799,12 +7843,12 @@ int mdbx_txn_commit(MDBX_txn *txn) { /* Update parent's DB table. */ memcpy(parent->mt_dbs, txn->mt_dbs, txn->mt_numdbs * sizeof(MDBX_db)); parent->mt_numdbs = txn->mt_numdbs; - parent->mt_dbstate[FREE_DBI] = txn->mt_dbstate[FREE_DBI]; - parent->mt_dbstate[MAIN_DBI] = txn->mt_dbstate[MAIN_DBI]; + parent->mt_dbistate[FREE_DBI] = txn->mt_dbistate[FREE_DBI]; + parent->mt_dbistate[MAIN_DBI] = txn->mt_dbistate[MAIN_DBI]; for (unsigned i = CORE_DBS; i < txn->mt_numdbs; i++) { /* preserve parent's DB_NEW status */ - parent->mt_dbstate[i] = - txn->mt_dbstate[i] | (parent->mt_dbstate[i] & (DB_CREAT | DB_FRESH)); + parent->mt_dbistate[i] = txn->mt_dbistate[i] | (parent->mt_dbistate[i] & + (DBI_CREAT | DBI_FRESH)); } /* Remove refunded pages from parent's dirty & spill lists */ @@ -8021,7 +8065,7 @@ int mdbx_txn_commit(MDBX_txn *txn) { if (txn->tw.dirtylist->length == 0 && (txn->mt_flags & (MDBX_TXN_DIRTY | MDBX_TXN_SPILLS)) == 0) { for (int i = txn->mt_numdbs; --i >= 0;) - mdbx_tassert(txn, (txn->mt_dbstate[i] & DB_DIRTY) == 0); + mdbx_tassert(txn, (txn->mt_dbistate[i] & DBI_DIRTY) == 0); rc = MDBX_SUCCESS; goto done; } @@ -8041,7 +8085,7 @@ int mdbx_txn_commit(MDBX_txn *txn) { if (unlikely(rc != MDBX_SUCCESS)) goto fail; for (MDBX_dbi i = CORE_DBS; i < txn->mt_numdbs; i++) { - if (txn->mt_dbstate[i] & DB_DIRTY) { + if (txn->mt_dbistate[i] & DBI_DIRTY) { if (unlikely(TXN_DBI_CHANGED(txn, i))) { rc = MDBX_BAD_DBI; goto fail; @@ -8071,7 +8115,7 @@ int mdbx_txn_commit(MDBX_txn *txn) { rc = mdbx_page_flush(txn, 0); if (likely(rc == MDBX_SUCCESS)) { - if (txn->mt_dbs[MAIN_DBI].md_flags & DB_DIRTY) + if (txn->mt_dbs[MAIN_DBI].md_flags & DBI_DIRTY) txn->mt_dbs[MAIN_DBI].md_mod_txnid = txn->mt_txnid; MDBX_meta meta, *head = mdbx_meta_head(env); @@ -8371,6 +8415,11 @@ static int __cold mdbx_read_header(MDBX_env *env, MDBX_meta *dest, (!META_IS_STEADY(dest) && !meta_weak_acceptable(env, dest, lck_exclusive))) { mdbx_error("%s", "no usable meta-pages, database is corrupted"); + if (rc == MDBX_SUCCESS) { + /* TODO: try to restore the database by fully checking b-tree structure + * for the each meta page, if the corresponding option was given */ + return MDBX_CORRUPTED; + } return rc; } @@ -8741,8 +8790,8 @@ static int mdbx_sync_locked(MDBX_env *env, unsigned flags, if (unlikely(shrink)) { mdbx_verbose("shrink to %" PRIaPGNO " pages (-%" PRIaPGNO ")", pending->mm_geo.now, shrink); - rc = mdbx_mapresize(env, pending->mm_geo.next, pending->mm_geo.now, - pending->mm_geo.upper); + rc = mdbx_mapresize_implicit(env, pending->mm_geo.next, pending->mm_geo.now, + pending->mm_geo.upper); if (MDBX_IS_ERROR(rc)) goto fail; } @@ -9172,7 +9221,8 @@ mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, intptr_t size_now, if (new_geo.now != current_geo->now || new_geo.upper != current_geo->upper) { - rc = mdbx_mapresize(env, current_geo->next, new_geo.now, new_geo.upper); + rc = mdbx_mapresize(env, current_geo->next, new_geo.now, new_geo.upper, + false); if (unlikely(rc != MDBX_SUCCESS)) goto bailout; mdbx_assert(env, (head == nullptr) == inside_txn); @@ -9981,15 +10031,21 @@ __cold int mdbx_is_readahead_reasonable(size_t volume, intptr_t redundancy) { /* 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. */ -#define CHANGEABLE \ +#define ENV_CHANGEABLE_FLAGS \ (MDBX_SAFE_NOSYNC | MDBX_NOMETASYNC | MDBX_MAPASYNC | MDBX_NOMEMINIT | \ MDBX_COALESCE | MDBX_PAGEPERTURB | MDBX_ACCEDE) -#define CHANGELESS \ +#define ENV_CHANGELESS_FLAGS \ (MDBX_NOSUBDIR | MDBX_RDONLY | MDBX_WRITEMAP | MDBX_NOTLS | MDBX_NORDAHEAD | \ MDBX_LIFORECLAIM | MDBX_EXCLUSIVE) +#define ENV_USABLE_FLAGS (ENV_CHANGEABLE_FLAGS | ENV_CHANGELESS_FLAGS) + +#if ENV_INTERNAL_FLAGS & ENV_USABLE_FLAGS +#error "Opps, some flags overlapped or wrong" +#endif -#if VALID_FLAGS & PERSISTENT_FLAGS & (CHANGEABLE | CHANGELESS) -#error "Persistent DB flags & env flags overlap, but both go in mm_flags" +#if (MDBX_ACCEDE | MDBX_CREATE) != ((DB_USABLE_FLAGS | DB_INTERNAL_FLAGS) & \ + (ENV_USABLE_FLAGS | ENV_INTERNAL_FLAGS)) +#error "Opps, some flags overlapped or wrong" #endif /* Merge flags and avoid false MDBX_UTTERLY_NOSYNC */ @@ -10013,7 +10069,7 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags, if (unlikely(env->me_signature != MDBX_ME_SIGNATURE)) return MDBX_EBADSIGN; - if (flags & ~(CHANGEABLE | CHANGELESS)) + if (flags & ~ENV_USABLE_FLAGS) return MDBX_EINVAL; if (env->me_lazy_fd != INVALID_HANDLE_VALUE || @@ -10147,9 +10203,8 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags, rc = MDBX_ENOMEM; goto bailout; } - env->me_dbxs[FREE_DBI].md_cmp = - mdbx_cmp_int_align4; /* aligned MDBX_INTEGERKEY */ - env->me_dbxs[FREE_DBI].md_dcmp = mdbx_cmp_lenfast; + env->me_dbxs[FREE_DBI].md_cmp = cmp_int_align4; /* aligned MDBX_INTEGERKEY */ + env->me_dbxs[FREE_DBI].md_dcmp = cmp_lenfast; rc = mdbx_openfile(F_ISSET(flags, MDBX_RDONLY) ? MDBX_OPEN_DXB_READ : MDBX_OPEN_DXB_LAZY, @@ -10180,7 +10235,13 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags, rc = errno; goto bailout; } - mode = st.st_mode; + mode = (/* inherit read permissions for group and others */ st.st_mode & + (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) | + /* always add read/write/search for owner */ S_IRUSR | S_IWUSR | + ((st.st_mode & S_IRGRP) ? /* +write if readable by group */ S_IWGRP + : 0) | + ((st.st_mode & S_IROTH) ? /* +write if readable by others */ S_IWOTH + : 0); } #endif /* !Windows */ const int lck_rc = mdbx_setup_lck(env, lck_pathname, mode); @@ -10189,8 +10250,7 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags, goto bailout; } - const unsigned rigorous_flags = - MDBX_WRITEMAP | MDBX_SAFE_NOSYNC | MDBX_MAPASYNC; + const unsigned rigorous_flags = MDBX_SAFE_NOSYNC | MDBX_MAPASYNC; const unsigned mode_flags = rigorous_flags | MDBX_NOMETASYNC | MDBX_LIFORECLAIM | MDBX_COALESCE | MDBX_NORDAHEAD; @@ -10266,7 +10326,7 @@ int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags, txn->mt_dbs = (MDBX_db *)((char *)txn + tsize); txn->mt_cursors = (MDBX_cursor **)(txn->mt_dbs + env->me_maxdbs); txn->mt_dbiseqs = (unsigned *)(txn->mt_cursors + env->me_maxdbs); - txn->mt_dbstate = (uint8_t *)(txn->mt_dbiseqs + env->me_maxdbs); + txn->mt_dbistate = (uint8_t *)(txn->mt_dbiseqs + env->me_maxdbs); txn->mt_env = env; txn->mt_dbxs = env->me_dbxs; txn->mt_flags = MDBX_TXN_FINISHED; @@ -10465,7 +10525,7 @@ __cold int mdbx_env_close(MDBX_env *env) { } /* Compare two items pointing at aligned unsigned int's. */ -static int __hot mdbx_cmp_int_align4(const MDBX_val *a, const MDBX_val *b) { +static int __hot cmp_int_align4(const MDBX_val *a, const MDBX_val *b) { mdbx_assert(NULL, a->iov_len == b->iov_len); switch (a->iov_len) { case 4: @@ -10482,7 +10542,7 @@ static int __hot mdbx_cmp_int_align4(const MDBX_val *a, const MDBX_val *b) { } /* Compare two items pointing at 2-byte aligned unsigned int's. */ -static int __hot mdbx_cmp_int_align2(const MDBX_val *a, const MDBX_val *b) { +static int __hot cmp_int_align2(const MDBX_val *a, const MDBX_val *b) { mdbx_assert(NULL, a->iov_len == b->iov_len); switch (a->iov_len) { case 4: @@ -10501,7 +10561,7 @@ static int __hot mdbx_cmp_int_align2(const MDBX_val *a, const MDBX_val *b) { /* Compare two items pointing at unsigneds of unknown alignment. * * This is also set as MDBX_INTEGERDUP|MDBX_DUPFIXED's MDBX_dbx.md_dcmp. */ -static int __hot mdbx_cmp_int_unaligned(const MDBX_val *a, const MDBX_val *b) { +static int __hot cmp_int_unaligned(const MDBX_val *a, const MDBX_val *b) { mdbx_assert(NULL, a->iov_len == b->iov_len); switch (a->iov_len) { case 4: @@ -10518,7 +10578,7 @@ static int __hot mdbx_cmp_int_unaligned(const MDBX_val *a, const MDBX_val *b) { } /* Compare two items lexically */ -static int __hot mdbx_cmp_memn(const MDBX_val *a, const MDBX_val *b) { +static int __hot cmp_lexical(const MDBX_val *a, const MDBX_val *b) { if (a->iov_len == b->iov_len) return memcmp(a->iov_base, b->iov_base, a->iov_len); @@ -10529,7 +10589,7 @@ static int __hot mdbx_cmp_memn(const MDBX_val *a, const MDBX_val *b) { } /* Compare two items in reverse byte order */ -static int __hot mdbx_cmp_memnr(const MDBX_val *a, const MDBX_val *b) { +static int __hot cmp_reverse(const MDBX_val *a, const MDBX_val *b) { const uint8_t *pa = (const uint8_t *)a->iov_base + a->iov_len; const uint8_t *pb = (const uint8_t *)b->iov_base + b->iov_len; const size_t shortest = (a->iov_len < b->iov_len) ? a->iov_len : b->iov_len; @@ -10544,7 +10604,7 @@ static int __hot mdbx_cmp_memnr(const MDBX_val *a, const MDBX_val *b) { } /* Fast non-lexically comparator */ -static int __hot mdbx_cmp_lenfast(const MDBX_val *a, const MDBX_val *b) { +static int __hot cmp_lenfast(const MDBX_val *a, const MDBX_val *b) { int diff = CMP2INT(a->iov_len, b->iov_len); return likely(diff) ? diff : memcmp(a->iov_base, b->iov_base, a->iov_len); } @@ -10605,10 +10665,10 @@ static MDBX_node *__hot mdbx_node_search(MDBX_cursor *mc, const MDBX_val *key, : /* There is no entry larger or equal to the key. */ NULL; } - if (cmp == mdbx_cmp_int_align2 && IS_BRANCH(mp)) + if (cmp == cmp_int_align2 && IS_BRANCH(mp)) /* Branch pages have no data, so if using integer keys, * alignment is guaranteed. Use faster mdbx_cmp_int_align4(). */ - cmp = mdbx_cmp_int_align4; + cmp = cmp_int_align4; MDBX_node *node; do { @@ -10861,8 +10921,8 @@ __hot static int mdbx_page_search_root(MDBX_cursor *mc, const MDBX_val *key, static int mdbx_setup_dbx(MDBX_dbx *const dbx, const MDBX_db *const db, const unsigned pagesize) { if (unlikely(!dbx->md_cmp)) { - dbx->md_cmp = mdbx_default_keycmp(db->md_flags); - dbx->md_dcmp = mdbx_default_datacmp(db->md_flags); + dbx->md_cmp = get_default_keycmp(db->md_flags); + dbx->md_dcmp = get_default_datacmp(db->md_flags); } dbx->md_klen_min = @@ -10916,7 +10976,7 @@ static int mdbx_fetch_sdb(MDBX_txn *txn, MDBX_dbi dbi) { /* The txn may not know this DBI, or another process may * have dropped and recreated the DB with other flags. */ MDBX_db *const db = &txn->mt_dbs[dbi]; - if (unlikely((db->md_flags & PERSISTENT_FLAGS) != md_flags)) + if (unlikely((db->md_flags & DB_PERSISTENT_FLAGS) != md_flags)) return MDBX_INCOMPATIBLE; memcpy(db, data.iov_base, sizeof(MDBX_db)); @@ -10924,7 +10984,7 @@ static int mdbx_fetch_sdb(MDBX_txn *txn, MDBX_dbi dbi) { if (unlikely(rc != MDBX_SUCCESS)) return rc; - txn->mt_dbstate[dbi] &= ~DB_STALE; + txn->mt_dbistate[dbi] &= ~DBI_STALE; return MDBX_SUCCESS; } @@ -10975,7 +11035,7 @@ __hot static int mdbx_page_search(MDBX_cursor *mc, const MDBX_val *key, } /* Make sure we're using an up-to-date root */ - if (unlikely(*mc->mc_dbstate & DB_STALE)) { + if (unlikely(*mc->mc_dbistate & DBI_STALE)) { rc = mdbx_fetch_sdb(mc->mc_txn, mc->mc_dbi); if (unlikely(rc != MDBX_SUCCESS)) return rc; @@ -11045,7 +11105,7 @@ int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data) { if (unlikely(!key || !data)) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; MDBX_cursor_couple cx; @@ -11069,7 +11129,7 @@ int mdbx_get_nearest(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, if (unlikely(!key || !data)) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) @@ -11109,7 +11169,7 @@ int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, if (unlikely(!key || !data)) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; MDBX_cursor_couple cx; @@ -11891,7 +11951,7 @@ static int mdbx_cursor_touch(MDBX_cursor *mc) { int rc = MDBX_SUCCESS; if (mc->mc_dbi >= CORE_DBS && - (*mc->mc_dbstate & (DB_DIRTY | DB_DUPDATA)) == 0) { + (*mc->mc_dbistate & (DBI_DIRTY | DBI_DUPDATA)) == 0) { mdbx_cassert(mc, (mc->mc_flags & C_RECLAIMING) == 0); /* Touch DB record of named DB */ MDBX_cursor_couple cx; @@ -11903,7 +11963,7 @@ static int mdbx_cursor_touch(MDBX_cursor *mc) { rc = mdbx_page_search(&cx.outer, &mc->mc_dbx->md_name, MDBX_PS_MODIFY); if (unlikely(rc)) return rc; - *mc->mc_dbstate |= DB_DIRTY; + *mc->mc_dbistate |= DBI_DIRTY; } mc->mc_top = 0; if (mc->mc_snum) { @@ -12155,7 +12215,7 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data, mc->mc_xcursor->mx_dbx.md_klen_min = mc->mc_xcursor->mx_dbx.md_klen_max = data->iov_len; } - *mc->mc_dbstate |= DB_DIRTY; + *mc->mc_dbistate |= DBI_DIRTY; if ((mc->mc_db->md_flags & (MDBX_DUPSORT | MDBX_DUPFIXED)) == MDBX_DUPFIXED) np->mp_flags |= P_LEAF2; mc->mc_flags |= C_INITIALIZED; @@ -13104,7 +13164,7 @@ static int mdbx_xcursor_init0(MDBX_cursor *mc) { mx->mx_cursor.mc_db = &mx->mx_db; mx->mx_cursor.mc_dbx = &mx->mx_dbx; mx->mx_cursor.mc_dbi = mc->mc_dbi; - mx->mx_cursor.mc_dbstate = &mx->mx_dbstate; + mx->mx_cursor.mc_dbistate = &mx->mx_dbistate; mx->mx_cursor.mc_snum = 0; mx->mx_cursor.mc_top = 0; mx->mx_cursor.mc_flags = C_SUB | (mc->mc_flags & (C_COPYING | C_SKIPORD)); @@ -13172,7 +13232,7 @@ static int mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) { mdbx_debug("Sub-db -%u root page %" PRIaPGNO, mx->mx_cursor.mc_dbi, mx->mx_db.md_root); - mx->mx_dbstate = DB_VALID | DB_USRVALID | DB_DUPDATA; + mx->mx_dbistate = DBI_VALID | DBI_USRVALID | DBI_DUPDATA; return MDBX_SUCCESS; } @@ -13194,7 +13254,7 @@ static int mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx, mx->mx_cursor.mc_top = 0; mx->mx_cursor.mc_flags |= C_INITIALIZED; mx->mx_cursor.mc_ki[0] = 0; - mx->mx_dbstate = DB_VALID | DB_USRVALID | DB_DUPDATA; + mx->mx_dbistate = DBI_VALID | DBI_USRVALID | DBI_DUPDATA; } mx->mx_dbx.md_klen_min = src_mx->mx_dbx.md_klen_min; @@ -13220,7 +13280,7 @@ static __inline int mdbx_couple_init(MDBX_cursor_couple *couple, couple->outer.mc_txn = txn; couple->outer.mc_db = db; couple->outer.mc_dbx = dbx; - couple->outer.mc_dbstate = dbstate; + couple->outer.mc_dbistate = dbstate; couple->outer.mc_snum = 0; couple->outer.mc_top = 0; couple->outer.mc_pg[0] = 0; @@ -13229,7 +13289,7 @@ static __inline int mdbx_couple_init(MDBX_cursor_couple *couple, couple->outer.mc_xcursor = NULL; int rc = MDBX_SUCCESS; - if (unlikely(*couple->outer.mc_dbstate & DB_STALE)) { + if (unlikely(*couple->outer.mc_dbistate & DBI_STALE)) { rc = mdbx_page_search(&couple->outer, NULL, MDBX_PS_ROOTONLY); rc = (rc != MDBX_NOTFOUND) ? rc : MDBX_SUCCESS; } else if (unlikely(couple->outer.mc_dbx->md_klen_max == 0)) { @@ -13254,7 +13314,7 @@ static int mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi) { STATIC_ASSERT(offsetof(MDBX_cursor_couple, outer) == 0); return mdbx_couple_init(container_of(mc, MDBX_cursor_couple, outer), dbi, txn, &txn->mt_dbs[dbi], &txn->mt_dbxs[dbi], - &txn->mt_dbstate[dbi]); + &txn->mt_dbistate[dbi]); } int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { @@ -13266,7 +13326,7 @@ int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { if (unlikely(rc != MDBX_SUCCESS)) return rc; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_VALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_VALID))) return MDBX_EINVAL; if (unlikely(dbi == FREE_DBI && !F_ISSET(txn->mt_flags, MDBX_RDONLY))) @@ -13308,7 +13368,7 @@ int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *mc) { if (unlikely(rc != MDBX_SUCCESS)) return rc; - if (unlikely(!mdbx_txn_dbi_exists(txn, mc->mc_dbi, DB_VALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, mc->mc_dbi, DBI_VALID))) return MDBX_EINVAL; if (unlikely(mc->mc_backup)) @@ -14784,7 +14844,7 @@ int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, if (unlikely(!key)) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; if (unlikely(txn->mt_flags & (MDBX_RDONLY | MDBX_TXN_BLOCKED))) @@ -15326,7 +15386,7 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data, if (unlikely(!key || !data)) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; if (unlikely(flags & ~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_RESERVE | @@ -16005,7 +16065,7 @@ int __cold mdbx_env_set_flags(MDBX_env *env, unsigned flags, int onoff) { if (unlikely(env->me_signature != MDBX_ME_SIGNATURE)) return MDBX_EBADSIGN; - if (unlikely(flags & ~CHANGEABLE)) + if (unlikely(flags & ~ENV_CHANGEABLE_FLAGS)) return MDBX_EPERM; if (unlikely(env->me_flags & MDBX_RDONLY)) @@ -16034,7 +16094,7 @@ int __cold mdbx_env_get_flags(const MDBX_env *env, unsigned *arg) { if (unlikely(env->me_signature != MDBX_ME_SIGNATURE)) return MDBX_EBADSIGN; - *arg = env->me_flags & (CHANGEABLE | CHANGELESS); + *arg = env->me_flags & ENV_USABLE_FLAGS; return MDBX_SUCCESS; } @@ -16159,7 +16219,7 @@ int __cold mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi, if (unlikely(!mask)) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_VALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_VALID))) return MDBX_EINVAL; MDBX_cursor_couple cx; @@ -16328,19 +16388,18 @@ int __cold mdbx_env_info_ex(const MDBX_env *env, const MDBX_txn *txn, return MDBX_SUCCESS; } -static MDBX_cmp_func *mdbx_default_keycmp(unsigned flags) { +static __inline MDBX_cmp_func *get_default_keycmp(unsigned flags) { return (flags & MDBX_REVERSEKEY) - ? mdbx_cmp_memnr - : (flags & MDBX_INTEGERKEY) ? mdbx_cmp_int_align2 : mdbx_cmp_memn; + ? cmp_reverse + : (flags & MDBX_INTEGERKEY) ? cmp_int_align2 : cmp_lexical; } -static MDBX_cmp_func *mdbx_default_datacmp(unsigned flags) { +static __inline MDBX_cmp_func *get_default_datacmp(unsigned flags) { return !(flags & MDBX_DUPSORT) - ? mdbx_cmp_lenfast + ? cmp_lenfast : ((flags & MDBX_INTEGERDUP) - ? mdbx_cmp_int_unaligned - : ((flags & MDBX_REVERSEDUP) ? mdbx_cmp_memnr - : mdbx_cmp_memn)); + ? cmp_int_unaligned + : ((flags & MDBX_REVERSEDUP) ? cmp_reverse : cmp_lexical)); } static int mdbx_dbi_bind(MDBX_txn *txn, const MDBX_dbi dbi, unsigned user_flags, @@ -16353,7 +16412,7 @@ static int mdbx_dbi_bind(MDBX_txn *txn, const MDBX_dbi dbi, unsigned user_flags, * 3) user_flags differs, but table is empty and MDBX_CREATE is provided * = assume that a properly create request with custom flags; */ - if ((user_flags ^ txn->mt_dbs[dbi].md_flags) & PERSISTENT_FLAGS) { + if ((user_flags ^ txn->mt_dbs[dbi].md_flags) & DB_PERSISTENT_FLAGS) { /* flags are differs, check other conditions */ if ((!user_flags && (!keycmp || keycmp == txn->mt_dbxs[dbi].md_cmp) && (!datacmp || datacmp == txn->mt_dbxs[dbi].md_dcmp)) || @@ -16365,7 +16424,7 @@ static int mdbx_dbi_bind(MDBX_txn *txn, const MDBX_dbi dbi, unsigned user_flags, if (txn->mt_flags & MDBX_RDONLY) return /* FIXME: return extended info */ MDBX_EACCESS; /* make sure flags changes get committed */ - txn->mt_dbs[dbi].md_flags = user_flags & PERSISTENT_FLAGS; + txn->mt_dbs[dbi].md_flags = user_flags & DB_PERSISTENT_FLAGS; txn->mt_flags |= MDBX_TXN_DIRTY; } else { return /* FIXME: return extended info */ MDBX_INCOMPATIBLE; @@ -16374,7 +16433,7 @@ static int mdbx_dbi_bind(MDBX_txn *txn, const MDBX_dbi dbi, unsigned user_flags, if (!keycmp) keycmp = txn->mt_dbxs[dbi].md_cmp ? txn->mt_dbxs[dbi].md_cmp - : mdbx_default_keycmp(user_flags); + : get_default_keycmp(user_flags); if (txn->mt_dbxs[dbi].md_cmp != keycmp) { if (txn->mt_dbxs[dbi].md_cmp) return MDBX_EINVAL; @@ -16383,7 +16442,7 @@ static int mdbx_dbi_bind(MDBX_txn *txn, const MDBX_dbi dbi, unsigned user_flags, if (!datacmp) datacmp = txn->mt_dbxs[dbi].md_dcmp ? txn->mt_dbxs[dbi].md_dcmp - : mdbx_default_datacmp(user_flags); + : get_default_datacmp(user_flags); if (txn->mt_dbxs[dbi].md_dcmp != datacmp) { if (txn->mt_dbxs[dbi].md_dcmp) return MDBX_EINVAL; @@ -16400,7 +16459,7 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags, if (unlikely(!dbi)) return rc; - if (unlikely((user_flags & ~VALID_FLAGS) != 0)) { + if (unlikely((user_flags & ~DB_USABLE_FLAGS) != 0)) { early_bailout: *dbi = 0; return rc; @@ -16441,9 +16500,9 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags, if (txn->mt_dbxs[MAIN_DBI].md_cmp == NULL) { txn->mt_dbxs[MAIN_DBI].md_cmp = - mdbx_default_keycmp(txn->mt_dbs[MAIN_DBI].md_flags); + get_default_keycmp(txn->mt_dbs[MAIN_DBI].md_flags); txn->mt_dbxs[MAIN_DBI].md_dcmp = - mdbx_default_datacmp(txn->mt_dbs[MAIN_DBI].md_flags); + get_default_datacmp(txn->mt_dbs[MAIN_DBI].md_flags); } /* Is the DB already open? */ @@ -16528,10 +16587,10 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags, if (txn->mt_numdbs < env->me_numdbs) { /* Import handles from env */ for (unsigned i = txn->mt_numdbs; i < env->me_numdbs; ++i) { - txn->mt_dbstate[i] = 0; - if (env->me_dbflags[i] & MDBX_VALID) { - txn->mt_dbs[i].md_flags = env->me_dbflags[i] & PERSISTENT_FLAGS; - txn->mt_dbstate[i] = DB_VALID | DB_USRVALID | DB_STALE; + txn->mt_dbistate[i] = 0; + if (env->me_dbflags[i] & DB_VALID) { + txn->mt_dbs[i].md_flags = env->me_dbflags[i] & DB_PERSISTENT_FLAGS; + txn->mt_dbistate[i] = DBI_VALID | DBI_USRVALID | DBI_STALE; mdbx_tassert(txn, txn->mt_dbxs[i].md_cmp != NULL); } } @@ -16560,14 +16619,14 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags, goto later_bailout; } - unsigned dbflag = DB_FRESH | DB_VALID | DB_USRVALID; + unsigned dbiflags = DBI_FRESH | DBI_VALID | DBI_USRVALID; MDBX_db db_dummy; if (unlikely(rc)) { /* MDBX_NOTFOUND and MDBX_CREATE: Create new DB */ mdbx_tassert(txn, rc == MDBX_NOTFOUND); memset(&db_dummy, 0, sizeof(db_dummy)); db_dummy.md_root = P_INVALID; - db_dummy.md_flags = user_flags & PERSISTENT_FLAGS; + db_dummy.md_flags = user_flags & DB_PERSISTENT_FLAGS; data.iov_len = sizeof(db_dummy); data.iov_base = &db_dummy; WITH_CURSOR_TRACKING(couple.outer, @@ -16577,7 +16636,7 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags, if (unlikely(rc != MDBX_SUCCESS)) goto later_bailout; - dbflag |= DB_DIRTY | DB_CREAT; + dbiflags |= DBI_DIRTY | DBI_CREAT; } /* Got info, register DBI in this txn */ @@ -16586,18 +16645,18 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags, env->me_dbflags[slot] = 0; rc = mdbx_dbi_bind(txn, slot, user_flags, keycmp, datacmp); if (unlikely(rc != MDBX_SUCCESS)) { - mdbx_tassert(txn, (dbflag & DB_CREAT) == 0); + mdbx_tassert(txn, (dbiflags & DBI_CREAT) == 0); later_bailout: *dbi = 0; later_exit: mdbx_free(namedup); } else { - txn->mt_dbstate[slot] = (uint8_t)dbflag; + txn->mt_dbistate[slot] = (uint8_t)dbiflags; txn->mt_dbxs[slot].md_name.iov_base = namedup; txn->mt_dbxs[slot].md_name.iov_len = len; txn->mt_numdbs += (slot == txn->mt_numdbs); - if ((dbflag & DB_CREAT) == 0) { - env->me_dbflags[slot] = txn->mt_dbs[slot].md_flags | MDBX_VALID; + if ((dbiflags & DBI_CREAT) == 0) { + env->me_dbflags[slot] = txn->mt_dbs[slot].md_flags | DB_VALID; mdbx_compiler_barrier(); if (env->me_numdbs <= slot) env->me_numdbs = slot + 1; @@ -16626,7 +16685,7 @@ int __cold mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *dest, if (unlikely(!dest)) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_VALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_VALID))) return MDBX_EINVAL; const size_t size_before_modtxnid = offsetof(MDBX_stat, ms_mod_txnid); @@ -16636,7 +16695,7 @@ int __cold mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *dest, if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) return MDBX_BAD_TXN; - if (unlikely(txn->mt_dbstate[dbi] & DB_STALE)) { + if (unlikely(txn->mt_dbistate[dbi] & DBI_STALE)) { rc = mdbx_fetch_sdb(txn, dbi); if (unlikely(rc != MDBX_SUCCESS)) return rc; @@ -16690,11 +16749,12 @@ int mdbx_dbi_flags_ex(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags, if (unlikely(!flags || !state)) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_VALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_VALID))) return MDBX_EINVAL; - *flags = txn->mt_dbs[dbi].md_flags & PERSISTENT_FLAGS; - *state = txn->mt_dbstate[dbi] & (DB_FRESH | DB_CREAT | DB_DIRTY | DB_STALE); + *flags = txn->mt_dbs[dbi].md_flags & DB_PERSISTENT_FLAGS; + *state = + txn->mt_dbistate[dbi] & (DBI_FRESH | DBI_CREAT | DBI_DIRTY | DBI_STALE); return MDBX_SUCCESS; } @@ -16804,7 +16864,7 @@ int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, int del) { if (unlikely(1 < (unsigned)del)) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; if (unlikely(TXN_DBI_CHANGED(txn, dbi))) @@ -16815,7 +16875,7 @@ int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, int del) { if (unlikely(rc != MDBX_SUCCESS)) return rc; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) { + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) { rc = MDBX_EINVAL; goto bailout; } @@ -16836,7 +16896,7 @@ int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, int del) { if (del && dbi >= CORE_DBS) { rc = mdbx_del0(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL, F_SUBDATA); if (likely(rc == MDBX_SUCCESS)) { - txn->mt_dbstate[dbi] = DB_STALE; + txn->mt_dbistate[dbi] = DBI_STALE; MDBX_env *env = txn->mt_env; rc = mdbx_fastmutex_acquire(&env->me_dbi_lock); if (unlikely(rc != MDBX_SUCCESS)) { @@ -16852,7 +16912,7 @@ int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, int del) { } } else { /* reset the DB record, mark it dirty */ - txn->mt_dbstate[dbi] |= DB_DIRTY; + txn->mt_dbistate[dbi] |= DBI_DIRTY; txn->mt_dbs[dbi].md_depth = 0; txn->mt_dbs[dbi].md_branch_pages = 0; txn->mt_dbs[dbi].md_leaf_pages = 0; @@ -16873,7 +16933,7 @@ int mdbx_set_compare(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cmp_func *cmp) { if (unlikely(rc != MDBX_SUCCESS)) return rc; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; txn->mt_dbxs[dbi].md_cmp = cmp; @@ -16885,7 +16945,7 @@ int mdbx_set_dupsort(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cmp_func *cmp) { if (unlikely(rc != MDBX_SUCCESS)) return rc; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; txn->mt_dbxs[dbi].md_dcmp = cmp; @@ -17576,8 +17636,8 @@ static int __cold mdbx_walk_sdb(mdbx_walk_ctx_t *ctx, MDBX_db *const db, MDBX_cursor_couple couple; MDBX_dbx dbx = {.md_klen_min = INT_MAX}; - uint8_t dbstate = DB_VALID | DB_AUDITED; - int rc = mdbx_couple_init(&couple, ~0u, ctx->mw_txn, db, &dbx, &dbstate); + uint8_t dbistate = DBI_VALID | DBI_AUDITED; + int rc = mdbx_couple_init(&couple, ~0u, ctx->mw_txn, db, &dbx, &dbistate); if (unlikely(rc != MDBX_SUCCESS)) return rc; @@ -17958,7 +18018,7 @@ int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *begin_key, if (unlikely(begin_key == MDBX_EPSILON && end_key == MDBX_EPSILON)) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; MDBX_cursor_couple begin; @@ -18131,7 +18191,7 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, if (unlikely(new_data == NULL && !(flags & MDBX_CURRENT))) return MDBX_EINVAL; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; if (unlikely(flags & ~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_RESERVE | @@ -18327,13 +18387,13 @@ int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result, if (unlikely(rc != MDBX_SUCCESS)) return rc; - if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DB_USRVALID))) + if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID))) return MDBX_EINVAL; if (unlikely(TXN_DBI_CHANGED(txn, dbi))) return MDBX_BAD_DBI; - if (unlikely(txn->mt_dbstate[dbi] & DB_STALE)) { + if (unlikely(txn->mt_dbistate[dbi] & DBI_STALE)) { rc = mdbx_fetch_sdb(txn, dbi); if (unlikely(rc != MDBX_SUCCESS)) return rc; @@ -18354,7 +18414,7 @@ int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result, mdbx_tassert(txn, new > dbs->md_seq); dbs->md_seq = new; txn->mt_flags |= MDBX_TXN_DIRTY; - txn->mt_dbstate[dbi] |= DB_DIRTY; + txn->mt_dbistate[dbi] |= DBI_DIRTY; } return MDBX_SUCCESS; @@ -18403,18 +18463,50 @@ __cold intptr_t mdbx_limits_txnsize_max(intptr_t pagesize) { /*** Key-making functions to avoid custom comparators *************************/ +static __always_inline double key2double(const int64_t key) { + union { + uint64_t u; + double f; + } casting; + + casting.u = (key < 0) ? key + UINT64_C(0x8000000000000000) + : UINT64_C(0xffffFFFFffffFFFF) - key; + return casting.f; +} + static __always_inline uint64_t double2key(const double *const ptr) { STATIC_ASSERT(sizeof(double) == sizeof(int64_t)); - const int64_t i64 = *(const int64_t *)ptr; - return (i64 >= 0) ? /* positive */ UINT64_C(0x8000000000000000) + i64 - : /* negative */ (uint64_t)-i64; + const int64_t i = *(const int64_t *)ptr; + const uint64_t u = (i < 0) ? UINT64_C(0xffffFFFFffffFFFF) - i + : i + UINT64_C(0x8000000000000000); + if (mdbx_assert_enabled()) { + const double f = key2double(u); + assert(memcmp(&f, ptr, 8) == 0); + } + return u; +} + +static __always_inline float key2float(const int32_t key) { + union { + uint32_t u; + float f; + } casting; + + casting.u = + (key < 0) ? key + UINT32_C(0x80000000) : UINT32_C(0xffffFFFF) - key; + return casting.f; } static __always_inline uint32_t float2key(const float *const ptr) { STATIC_ASSERT(sizeof(float) == sizeof(int32_t)); - const int32_t i32 = *(const int32_t *)ptr; - return (i32 >= 0) ? /* positive */ UINT32_C(0x80000000) + i32 - : /* negative */ (uint32_t)-i32; + const int32_t i = *(const int32_t *)ptr; + const uint32_t u = + (i < 0) ? UINT32_C(0xffffFFFF) - i : i + UINT32_C(0x80000000); + if (mdbx_assert_enabled()) { + const float f = key2float(u); + assert(memcmp(&f, ptr, 4) == 0); + } + return u; } uint64_t mdbx_key_from_double(const double ieee754_64bit) { @@ -18434,8 +18526,8 @@ uint32_t mdbx_key_from_ptrfloat(const float *const ieee754_32bit) { } #define IEEE754_DOUBLE_MANTISSA_SIZE 52 -#define IEEE754_DOUBLE_BIAS 0x3FF -#define IEEE754_DOUBLE_MAX 0x7FF +#define IEEE754_DOUBLE_EXPONENTA_BIAS 0x3FF +#define IEEE754_DOUBLE_EXPONENTA_MAX 0x7FF #define IEEE754_DOUBLE_IMPLICIT_LEAD UINT64_C(0x0010000000000000) #define IEEE754_DOUBLE_MANTISSA_MASK UINT64_C(0x000FFFFFFFFFFFFF) #define IEEE754_DOUBLE_MANTISSA_AMAX UINT64_C(0x001FFFFFFFFFFFFF) @@ -18481,7 +18573,7 @@ static __inline int clz64(uint64_t value) { return debruijn_clz64[value * UINT64_C(0x03F79D71B4CB0A89) >> 58]; } -static uint64_t round_mantissa(const uint64_t u64, int shift) { +static __inline uint64_t round_mantissa(const uint64_t u64, int shift) { assert(shift < 0 && u64 > 0); shift = -shift; const unsigned half = 1 << (shift - 1); @@ -18491,7 +18583,7 @@ static uint64_t round_mantissa(const uint64_t u64, int shift) { } uint64_t mdbx_key_from_jsonInteger(const int64_t json_integer) { - const uint64_t biased_zero = UINT64_C(0x8000000000000000); + const uint64_t bias = UINT64_C(0x8000000000000000); if (json_integer > 0) { const uint64_t u64 = json_integer; int shift = clz64(u64) - (64 - IEEE754_DOUBLE_MANTISSA_SIZE - 1); @@ -18505,10 +18597,9 @@ uint64_t mdbx_key_from_jsonInteger(const int64_t json_integer) { assert(mantissa >= IEEE754_DOUBLE_IMPLICIT_LEAD && mantissa <= IEEE754_DOUBLE_MANTISSA_AMAX); const uint64_t exponent = - IEEE754_DOUBLE_BIAS + IEEE754_DOUBLE_MANTISSA_SIZE - shift; - assert(exponent > 0 && exponent <= IEEE754_DOUBLE_MAX); - const uint64_t key = biased_zero + - (exponent << IEEE754_DOUBLE_MANTISSA_SIZE) + + IEEE754_DOUBLE_EXPONENTA_BIAS + IEEE754_DOUBLE_MANTISSA_SIZE - shift; + assert(exponent > 0 && exponent <= IEEE754_DOUBLE_EXPONENTA_MAX); + const uint64_t key = bias + (exponent << IEEE754_DOUBLE_MANTISSA_SIZE) + (mantissa - IEEE754_DOUBLE_IMPLICIT_LEAD); #if !defined(_MSC_VER) || \ defined( \ @@ -18532,10 +18623,9 @@ uint64_t mdbx_key_from_jsonInteger(const int64_t json_integer) { assert(mantissa >= IEEE754_DOUBLE_IMPLICIT_LEAD && mantissa <= IEEE754_DOUBLE_MANTISSA_AMAX); const uint64_t exponent = - IEEE754_DOUBLE_BIAS + IEEE754_DOUBLE_MANTISSA_SIZE - shift; - assert(exponent > 0 && exponent <= IEEE754_DOUBLE_MAX); - const uint64_t key = biased_zero - - (exponent << IEEE754_DOUBLE_MANTISSA_SIZE) - + IEEE754_DOUBLE_EXPONENTA_BIAS + IEEE754_DOUBLE_MANTISSA_SIZE - shift; + assert(exponent > 0 && exponent <= IEEE754_DOUBLE_EXPONENTA_MAX); + const uint64_t key = bias - 1 - (exponent << IEEE754_DOUBLE_MANTISSA_SIZE) - (mantissa - IEEE754_DOUBLE_IMPLICIT_LEAD); #if !defined(_MSC_VER) || \ defined( \ @@ -18546,7 +18636,60 @@ uint64_t mdbx_key_from_jsonInteger(const int64_t json_integer) { return key; } - return biased_zero; + return bias; +} + +int64_t mdbx_jsonInteger_from_key(const MDBX_val v) { + assert(v.iov_len == 8); + const uint64_t key = unaligned_peek_u64(2, v.iov_base); + const uint64_t bias = UINT64_C(0x8000000000000000); + const uint64_t covalent = (key > bias) ? key - bias : bias - key - 1; + const int shift = IEEE754_DOUBLE_EXPONENTA_BIAS + 63 - + (IEEE754_DOUBLE_EXPONENTA_MAX & + (int)(covalent >> IEEE754_DOUBLE_MANTISSA_SIZE)); + if (unlikely(shift < 1)) + return (key < bias) ? INT64_MIN : INT64_MAX; + if (unlikely(shift > 63)) + return 0; + + const uint64_t unscaled = ((covalent & IEEE754_DOUBLE_MANTISSA_MASK) + << (63 - IEEE754_DOUBLE_MANTISSA_SIZE)) + + bias; + const int64_t absolute = unscaled >> shift; + const int64_t value = (key < bias) ? -absolute : absolute; + assert(key == mdbx_key_from_jsonInteger(value) || + (mdbx_key_from_jsonInteger(value - 1) < key && + key < mdbx_key_from_jsonInteger(value + 1))); + return value; +} + +double mdbx_double_from_key(const MDBX_val v) { + assert(v.iov_len == 8); + return key2double(unaligned_peek_u64(2, v.iov_base)); +} + +float mdbx_float_from_key(const MDBX_val v) { + assert(v.iov_len == 4); + return key2float(unaligned_peek_u32(2, v.iov_base)); +} + +int32_t mdbx_int32_from_key(const MDBX_val v) { + assert(v.iov_len == 4); + return (int32_t)(unaligned_peek_u32(2, v.iov_base) - UINT32_C(0x80000000)); +} + +int64_t mdbx_int64_from_key(const MDBX_val v) { + assert(v.iov_len == 8); + return (int64_t)(unaligned_peek_u64(2, v.iov_base) - + UINT64_C(0x8000000000000000)); +} + +__cold MDBX_cmp_func *mdbx_get_keycmp(unsigned flags) { + return get_default_keycmp(flags); +} + +__cold MDBX_cmp_func *mdbx_get_datacmp(unsigned flags) { + return get_default_datacmp(flags); } /*** Attribute support functions for Nexenta **********************************/ diff --git a/libs/libmdbx/src/src/defs.h b/libs/libmdbx/src/src/defs.h index b2710eede8..f4672d5b99 100644 --- a/libs/libmdbx/src/src/defs.h +++ b/libs/libmdbx/src/src/defs.h @@ -67,6 +67,10 @@ # define __has_include(x) (0) #endif +#ifndef __has_cpp_attribute +# define __has_cpp_attribute(x) (0) +#endif + #if __has_feature(thread_sanitizer) # define __SANITIZE_THREAD__ 1 #endif @@ -144,11 +148,20 @@ #endif /* __noop */ #ifndef __fallthrough -# if __GNUC_PREREQ(7, 0) || __has_attribute(__fallthrough__) -# define __fallthrough __attribute__((__fallthrough__)) -# else -# define __fallthrough __noop() -# endif +# if defined(__cplusplus) && __has_cpp_attribute(fallthrough) +# define __fallthrough [[fallthrough]] +# elif __GNUC_PREREQ(8, 0) && defined(__cplusplus) && __cplusplus >= 201103L +# define __fallthrough [[fallthrough]] +# elif __GNUC_PREREQ(7, 0) && \ + (!defined(__LCC__) || (__LCC__ == 124 && __LCC_MINOR__ >= 12) || \ + (__LCC__ == 125 && __LCC_MINOR__ >= 5) || (__LCC__ >= 126)) +# define __fallthrough __attribute__((__fallthrough__)) +# elif defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201103L &&\ + __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") +# define __fallthrough [[clang::fallthrough]] +# else +# define __fallthrough +# endif #endif /* __fallthrough */ #ifndef __unreachable @@ -201,7 +214,9 @@ * Such a function can be subject to common subexpression elimination * and loop optimization just as an arithmetic operator would be. * These functions should be declared with the attribute pure. */ -# if defined(__GNUC__) || __has_attribute(__pure__) +# if (defined(__GNUC__) || __has_attribute(__pure__)) && \ + (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ + || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) # define __pure_function __attribute__((__pure__)) # else # define __pure_function @@ -218,7 +233,9 @@ * data pointed to must not be declared const. Likewise, a function * that calls a non-const function usually must not be const. * It does not make sense for a const function to return void. */ -# if defined(__GNUC__) || __has_attribute(__const__) +# if (defined(__GNUC__) || __has_attribute(__pure__)) && \ + (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ + || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) # define __const_function __attribute__((__const__)) # else # define __const_function diff --git a/libs/libmdbx/src/src/internals.h b/libs/libmdbx/src/src/internals.h index c751e912af..d9edc9d53a 100644 --- a/libs/libmdbx/src/src/internals.h +++ b/libs/libmdbx/src/src/internals.h @@ -13,7 +13,7 @@ #pragma once #ifdef MDBX_CONFIG_H -#include "../config.h" +#include MDBX_CONFIG_H #endif /* *INDENT-OFF* */ @@ -716,6 +716,9 @@ struct MDBX_txn { #define MDBX_TXN_BEGIN_FLAGS \ (MDBX_NOMETASYNC | MDBX_SAFE_NOSYNC | MDBX_MAPASYNC | MDBX_RDONLY | \ MDBX_TRYTXN) + /* Additional flag for mdbx_sync_locked() */ +#define MDBX_SHRINK_ALLOWED UINT32_C(0x40000000) + /* internal txn flags */ #define MDBX_TXN_FINISHED 0x01 /* txn is finished or never began */ #define MDBX_TXN_ERROR 0x02 /* txn is unusable after an error */ @@ -725,6 +728,16 @@ struct MDBX_txn { /* most operations on the txn are currently illegal */ #define MDBX_TXN_BLOCKED \ (MDBX_TXN_FINISHED | MDBX_TXN_ERROR | MDBX_TXN_HAS_CHILD) + +#define TXN_FLAGS \ + (MDBX_TXN_FINISHED | MDBX_TXN_ERROR | MDBX_TXN_DIRTY | MDBX_TXN_SPILLS | \ + MDBX_TXN_HAS_CHILD) + +#if (TXN_FLAGS & MDBX_TXN_BEGIN_FLAGS) || \ + ((MDBX_TXN_BEGIN_FLAGS | TXN_FLAGS) & MDBX_SHRINK_ALLOWED) +#error "Opps, some flags overlapped or wrong" +#endif + unsigned mt_flags; /* The ID of this transaction. IDs are integers incrementing from 1. * Only committed write transactions increment the ID. If a transaction @@ -740,17 +753,17 @@ struct MDBX_txn { /* In write txns, array of cursors for each DB */ MDBX_cursor **mt_cursors; - /* Transaction DB Flags */ -#define DB_DIRTY MDBX_TBL_DIRTY /* DB was written in this txn */ -#define DB_STALE MDBX_TBL_STALE /* Named-DB record is older than txnID */ -#define DB_FRESH MDBX_TBL_FRESH /* Named-DB handle opened in this txn */ -#define DB_CREAT MDBX_TBL_CREAT /* Named-DB handle created in this txn */ -#define DB_VALID 0x10 /* DB handle is valid, see also MDBX_VALID */ -#define DB_USRVALID 0x20 /* As DB_VALID, but not set for FREE_DBI */ -#define DB_DUPDATA 0x40 /* DB is MDBX_DUPSORT data */ -#define DB_AUDITED 0x80 /* Internal flag for accounting during audit */ + /* Transaction DBI Flags */ +#define DBI_DIRTY MDBX_DBI_DIRTY /* DB was written in this txn */ +#define DBI_STALE MDBX_DBI_STALE /* Named-DB record is older than txnID */ +#define DBI_FRESH MDBX_DBI_FRESH /* Named-DB handle opened in this txn */ +#define DBI_CREAT MDBX_DBI_CREAT /* Named-DB handle created in this txn */ +#define DBI_VALID 0x10 /* DB handle is valid, see also DB_VALID */ +#define DBI_USRVALID 0x20 /* As DB_VALID, but not set for FREE_DBI */ +#define DBI_DUPDATA 0x40 /* DB is MDBX_DUPSORT data */ +#define DBI_AUDITED 0x80 /* Internal flag for accounting during audit */ /* Array of flags for each DB */ - uint8_t *mt_dbstate; + uint8_t *mt_dbistate; /* Number of DB records in use, or 0 when the txn is finished. * This number only ever increments until the txn finishes; we * don't decrement it when individual DB handles are closed. */ @@ -833,8 +846,8 @@ struct MDBX_cursor { MDBX_db *mc_db; /* The database auxiliary record for this cursor */ MDBX_dbx *mc_dbx; - /* The mt_dbstate for this database */ - uint8_t *mc_dbstate; + /* The mt_dbistate for this database */ + uint8_t *mc_dbistate; unsigned mc_snum; /* number of pushed pages */ unsigned mc_top; /* index of top page, normally mc_snum-1 */ @@ -869,8 +882,8 @@ typedef struct MDBX_xcursor { MDBX_db mx_db; /* The auxiliary DB record for this Dup DB */ MDBX_dbx mx_dbx; - /* The mt_dbstate for this Dup DB */ - uint8_t mx_dbstate; + /* The mt_dbistate for this Dup DB */ + uint8_t mx_dbistate; } MDBX_xcursor; typedef struct MDBX_cursor_couple { @@ -884,12 +897,11 @@ struct MDBX_env { uint32_t me_signature; /* Failed to update the meta page. Probably an I/O error. */ #define MDBX_FATAL_ERROR UINT32_C(0x80000000) - /* Additional flag for mdbx_sync_locked() */ -#define MDBX_SHRINK_ALLOWED UINT32_C(0x40000000) /* Some fields are initialized. */ #define MDBX_ENV_ACTIVE UINT32_C(0x20000000) /* me_txkey is set */ #define MDBX_ENV_TXKEY UINT32_C(0x10000000) +#define ENV_INTERNAL_FLAGS (MDBX_FATAL_ERROR | MDBX_ENV_ACTIVE | MDBX_ENV_TXKEY) uint32_t me_flags; mdbx_mmap_t me_dxb_mmap; /* The main data file */ #define me_map me_dxb_mmap.dxb @@ -1287,12 +1299,22 @@ typedef struct MDBX_node { uint8_t mn_data[/* C99 */]; /* key and data are appended here */ } MDBX_node; -#define MDBX_VALID 0x8000 /* DB handle is valid, for me_dbflags */ -#define PERSISTENT_FLAGS (0xffff & ~(MDBX_VALID | MDBX_NOSUBDIR)) -/* mdbx_dbi_open() flags */ -#define VALID_FLAGS \ +#define DB_PERSISTENT_FLAGS \ (MDBX_REVERSEKEY | MDBX_DUPSORT | MDBX_INTEGERKEY | MDBX_DUPFIXED | \ - MDBX_INTEGERDUP | MDBX_REVERSEDUP | MDBX_CREATE | MDBX_ACCEDE) + MDBX_INTEGERDUP | MDBX_REVERSEDUP) + +/* mdbx_dbi_open() flags */ +#define DB_USABLE_FLAGS (DB_PERSISTENT_FLAGS | MDBX_CREATE | MDBX_ACCEDE) + +#define DB_VALID 0x8000 /* DB handle is valid, for me_dbflags */ +#define DB_INTERNAL_FLAGS DB_VALID + +#if DB_INTERNAL_FLAGS & DB_USABLE_FLAGS +#error "Opps, some flags overlapped or wrong" +#endif +#if DB_PERSISTENT_FLAGS & ~DB_USABLE_FLAGS +#error "Opps, some flags overlapped or wrong" +#endif /* max number of pages to commit in one writev() call */ #define MDBX_COMMIT_PAGES 64 diff --git a/libs/libmdbx/src/src/lck-posix.c b/libs/libmdbx/src/src/lck-posix.c index 9038739f1e..f908cb6fc0 100644 --- a/libs/libmdbx/src/src/lck-posix.c +++ b/libs/libmdbx/src/src/lck-posix.c @@ -538,13 +538,11 @@ MDBX_INTERNAL_FUNC int __cold mdbx_lck_destroy(MDBX_env *env, MDBX_INTERNAL_FUNC int __cold mdbx_lck_init(MDBX_env *env, MDBX_env *inprocess_neighbor, int global_uniqueness_flag) { - if (inprocess_neighbor) - return MDBX_SUCCESS /* currently don't need any initialization - if LCK already opened/used inside current process */ - ; #if MDBX_LOCKING == MDBX_LOCKING_SYSV int semid = -1; - if (global_uniqueness_flag) { + /* don't initialize semaphores twice */ + (void)inprocess_neighbor; + if (global_uniqueness_flag == MDBX_RESULT_TRUE) { struct stat st; if (fstat(env->me_lazy_fd, &st)) return errno; @@ -588,14 +586,17 @@ MDBX_INTERNAL_FUNC int __cold mdbx_lck_init(MDBX_env *env, } env->me_sysv_ipc.semid = semid; - return MDBX_SUCCESS; #elif MDBX_LOCKING == MDBX_LOCKING_FUTEX -#warning "TODO" + (void)inprocess_neighbor; + if (global_uniqueness_flag != MDBX_RESULT_TRUE) + return MDBX_SUCCESS; +#error "FIXME: Not implemented" #elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988 /* don't initialize semaphores twice */ + (void)inprocess_neighbor; if (global_uniqueness_flag == MDBX_RESULT_TRUE) { if (sem_init(&env->me_lck->mti_rlock, true, 1)) return errno; @@ -606,6 +607,10 @@ MDBX_INTERNAL_FUNC int __cold mdbx_lck_init(MDBX_env *env, #elif MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \ MDBX_LOCKING == MDBX_LOCKING_POSIX2008 + if (inprocess_neighbor) + return MDBX_SUCCESS /* don't need any initialization for mutexes + if LCK already opened/used inside current process */ + ; /* FIXME: Unfortunately, there is no other reliable way but to long testing * on each platform. On the other hand, behavior like FreeBSD is incorrect diff --git a/libs/libmdbx/src/src/osal.c b/libs/libmdbx/src/src/osal.c index e9fff57399..e7c00dd59c 100644 --- a/libs/libmdbx/src/src/osal.c +++ b/libs/libmdbx/src/src/osal.c @@ -1406,7 +1406,7 @@ MDBX_INTERNAL_FUNC int mdbx_munmap(mdbx_mmap_t *map) { } MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t size, - size_t limit) { + size_t limit, const bool may_move) { assert(size <= limit); #if defined(_WIN32) || defined(_WIN64) assert(size != map->current || limit != map->limit || size < map->filesize); @@ -1485,9 +1485,9 @@ MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t size, if (status != /* STATUS_CONFLICTING_ADDRESSES */ 0xC0000018) goto bailout_ntstatus /* no way to recovery */; - /* assume we can change base address if mapping size changed or prev address - * couldn't be used */ - map->address = NULL; + if (may_move) + /* the base address could be changed */ + map->address = NULL; } retry_file_and_section: @@ -1544,7 +1544,7 @@ retry_mapview:; if (!NT_SUCCESS(status)) { if (status == /* STATUS_CONFLICTING_ADDRESSES */ 0xC0000018 && - map->address) { + map->address && may_move) { /* try remap at another base address */ map->address = NULL; goto retry_mapview; @@ -1568,6 +1568,7 @@ retry_mapview:; map->current = (size_t)SectionSize.QuadPart; map->limit = ViewSize; + #else uint64_t filesize = 0; @@ -1588,7 +1589,8 @@ retry_mapview:; if (limit != map->limit) { #if defined(MREMAP_MAYMOVE) - void *ptr = mremap(map->address, map->limit, limit, 0); + void *ptr = + mremap(map->address, map->limit, limit, may_move ? MREMAP_MAYMOVE : 0); if (ptr == MAP_FAILED) { rc = errno; switch (rc) { @@ -1599,7 +1601,59 @@ retry_mapview:; } return rc; } - map->address = ptr; +#else + if (!may_move) + /* TODO: Perhaps here it is worth to implement suspend/resume threads + * and perform unmap/map as like for Windows. */ + return MDBX_UNABLE_EXTEND_MAPSIZE; + + if (unlikely(munmap(map->address, map->limit))) + return errno; + + unsigned mmap_flags = + MAP_CONCEAL | MAP_SHARED | MAP_FILE | + (F_ISSET(flags, MDBX_UTTERLY_NOSYNC) ? MAP_NOSYNC : 0); +#ifdef MAP_FIXED + if (!may_move) + mmap_flags |= MAP_FIXED; +#endif + + void *ptr = + mmap(map->address, limit, + (flags & MDBX_WRITEMAP) ? PROT_READ | PROT_WRITE : PROT_READ, + mmap_flags, map->fd, 0); + if (unlikely(ptr == MAP_FAILED)) { + ptr = mmap(map->address, map->limit, + (flags & MDBX_WRITEMAP) ? PROT_READ | PROT_WRITE : PROT_READ, + mmap_flags, map->fd, 0); + if (unlikely(ptr == MAP_FAILED)) { + VALGRIND_MAKE_MEM_NOACCESS(map->address, map->current); + /* Unpoisoning is required for ASAN to avoid false-positive diagnostic + * when this memory will re-used by malloc or another mmaping. + * See https://github.com/erthink/libmdbx/pull/93#issuecomment-613687203 + */ + ASAN_UNPOISON_MEMORY_REGION(map->address, map->limit); + map->limit = 0; + map->current = 0; + map->address = nullptr; + return errno; + } + return MDBX_UNABLE_EXTEND_MAPSIZE; + } +#endif /* !MREMAP_MAYMOVE */ + + if (map->address != ptr) { + VALGRIND_MAKE_MEM_NOACCESS(map->address, map->current); + /* Unpoisoning is required for ASAN to avoid false-positive diagnostic + * when this memory will re-used by malloc or another mmaping. + * See https://github.com/erthink/libmdbx/pull/93#issuecomment-613687203 + */ + ASAN_UNPOISON_MEMORY_REGION(map->address, map->limit); + + VALGRIND_MAKE_MEM_DEFINED(ptr, map->current); + ASAN_UNPOISON_MEMORY_REGION(ptr, map->current); + map->address = ptr; + } map->limit = limit; #ifdef MADV_DONTFORK @@ -1610,14 +1664,9 @@ retry_mapview:; #ifdef MADV_NOHUGEPAGE (void)madvise(map->address, map->limit, MADV_NOHUGEPAGE); #endif /* MADV_NOHUGEPAGE */ - -#else /* MREMAP_MAYMOVE */ - /* TODO: Perhaps here it is worth to implement suspend/resume threads - * and perform unmap/map as like for Windows. */ - rc = MDBX_UNABLE_EXTEND_MAPSIZE; -#endif /* !MREMAP_MAYMOVE */ } #endif + return rc; } diff --git a/libs/libmdbx/src/src/osal.h b/libs/libmdbx/src/src/osal.h index a6188bb382..ff1c4af1bf 100644 --- a/libs/libmdbx/src/src/osal.h +++ b/libs/libmdbx/src/src/osal.h @@ -627,7 +627,7 @@ MDBX_INTERNAL_FUNC int mdbx_mmap(const int flags, mdbx_mmap_t *map, const unsigned options); MDBX_INTERNAL_FUNC int mdbx_munmap(mdbx_mmap_t *map); MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t current, - size_t wanna); + size_t wanna, const bool may_move); #if defined(_WIN32) || defined(_WIN64) typedef struct { unsigned limit, count; diff --git a/libs/libmdbx/src/test/CMakeLists.txt b/libs/libmdbx/src/test/CMakeLists.txt index 2014cb57fa..3e0a929b76 100644 --- a/libs/libmdbx/src/test/CMakeLists.txt +++ b/libs/libmdbx/src/test/CMakeLists.txt @@ -27,9 +27,37 @@ add_executable(mdbx_test nested.cc ) +list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_20 HAS_CXX20) +list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_17 HAS_CXX17) +list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_14 HAS_CXX14) +list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_11 HAS_CXX11) +if(NOT DEFINED MDBX_CXX_STANDARD) + if(DEFINED CMAKE_CXX_STANDARD) + set(MDBX_CXX_STANDARD ${CMAKE_CXX_STANDARD}) + elseif(NOT HAS_CXX20 LESS 0) + set(MDBX_CXX_STANDARD 20) + elseif(NOT HAS_CXX17 LESS 0) + set(MDBX_CXX_STANDARD 17) + elseif(NOT HAS_CXX14 LESS 0) + set(MDBX_CXX_STANDARD 14) + elseif(NOT HAS_CXX11 LESS 0) + set(MDBX_CXX_STANDARD 11) + endif() +endif() +if(MDBX_CXX_STANDARD) + message(STATUS "Use C++${MDBX_CXX_STANDARD} for libmdbx") + if(NOT SUBPROJECT OR NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD ${MDBX_CXX_STANDARD}) + endif() +endif() + +if(MDBX_CXX_STANDARD) + set_target_properties(mdbx_test PROPERTIES + CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON) +endif() + set_target_properties(mdbx_test PROPERTIES - INTERPROCEDURAL_OPTIMIZATION $<BOOL:${INTERPROCEDURAL_OPTIMIZATION}> - CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) + INTERPROCEDURAL_OPTIMIZATION $<BOOL:${INTERPROCEDURAL_OPTIMIZATION}>) target_setup_options(mdbx_test) target_link_libraries(mdbx_test ${TOOL_MDBX_LIB} ${LIB_MATH} ${CMAKE_THREAD_LIBS_INIT}) diff --git a/libs/libmdbx/src/test/config.cc b/libs/libmdbx/src/test/config.cc index 5e1979718a..f8e6866f94 100644 --- a/libs/libmdbx/src/test/config.cc +++ b/libs/libmdbx/src/test/config.cc @@ -373,6 +373,8 @@ void dump(const char *title) { i->params.pagesize); dump_verbs("mode", i->params.mode_flags, mode_bits); + log_verbose("random-writemap: %s\n", + i->params.random_writemap ? "Yes" : "No"); dump_verbs("table", i->params.table_flags, table_bits); if (i->params.test_nops) diff --git a/libs/libmdbx/src/test/config.h b/libs/libmdbx/src/test/config.h index 2ab4742a2e..5f0331291b 100644 --- a/libs/libmdbx/src/test/config.h +++ b/libs/libmdbx/src/test/config.h @@ -265,9 +265,10 @@ struct actor_params_pod { keygen_params_pod keygen; uint8_t loglevel{0}; - bool drop_table{0}; - bool ignore_dbfull{0}; - bool speculum{0}; + bool drop_table{false}; + bool ignore_dbfull{false}; + bool speculum{false}; + bool random_writemap{true}; }; struct actor_config_pod { diff --git a/libs/libmdbx/src/test/long_stochastic.sh b/libs/libmdbx/src/test/long_stochastic.sh index 7141ee62c5..45d1247af7 100644 --- a/libs/libmdbx/src/test/long_stochastic.sh +++ b/libs/libmdbx/src/test/long_stochastic.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash -if ! which make cc c++ tee lz4 banner >/dev/null; then +if ! which make cc c++ tee lz4 >/dev/null; then echo "Please install the following prerequisites: make cc c++ tee lz4 banner" >&2 exit 1 fi -set -euo pipefail - +BANNER="$(which banner 2>/dev/null | echo echo)" UNAME="$(uname -s 2>/dev/null || echo Unknown)" +set -euo pipefail ## NOTE: Valgrind could produce some false-positive warnings ## in multi-process environment with shared memory. @@ -193,7 +193,7 @@ for nops in 10 100 1000 10000 100000 1000000 10000000 100000000 1000000000; do speculum=$([ $nops -le 1000 ] && echo '--speculum' || true) while true; do echo "=======================================================================" - banner "$nops / $wbatch" + ${BANNER} "$nops / $wbatch" subcase=0 for ((bits=2**${#options[@]}; --bits >= 0; )); do seed=$(($(date +%s) + RANDOM)) diff --git a/libs/libmdbx/src/test/main.cc b/libs/libmdbx/src/test/main.cc index c6b15ea29b..10016ab3d5 100644 --- a/libs/libmdbx/src/test/main.cc +++ b/libs/libmdbx/src/test/main.cc @@ -101,6 +101,7 @@ void __noreturn usage(void) { " notls == MDBX_NOTLS\n" " nordahead == MDBX_NORDAHEAD\n" " nomeminit == MDBX_NOMEMINIT\n" + " --random-writemap[=YES|no] Toggle MDBX_WRITEMAP randomly\n" "Key-value space/table options:\n" " --table={[+-]FLAG}[,[+-]FLAG]...\n" " key.reverse == MDBX_REVERSEKEY\n" @@ -165,6 +166,7 @@ void actor_params::set_defaults(const std::string &tmpdir) { drop_table = false; ignore_dbfull = false; speculum = false; + random_writemap = true; max_readers = 42; max_tables = 42; @@ -259,6 +261,9 @@ int main(int argc, char *const argv[]) { if (config::parse_option(argc, argv, narg, "mode", params.mode_flags, config::mode_bits)) continue; + if (config::parse_option(argc, argv, narg, "random-writemap", + params.random_writemap)) + continue; if (config::parse_option(argc, argv, narg, "table", params.table_flags, config::table_bits)) { if ((params.table_flags & MDBX_DUPFIXED) == 0) diff --git a/libs/libmdbx/src/test/osal-unix.cc b/libs/libmdbx/src/test/osal-unix.cc index 757c8583c0..a85ce32a11 100644 --- a/libs/libmdbx/src/test/osal-unix.cc +++ b/libs/libmdbx/src/test/osal-unix.cc @@ -23,6 +23,8 @@ #include <sys/wait.h> #include <unistd.h> +#include <atomic> + #ifndef MDBX_LOCKING #error "Opps, MDBX_LOCKING is undefined!" #endif @@ -307,14 +309,14 @@ bool actor_config::osal_deserialize(const char *str, const char *end, static pid_t overlord_pid; -static volatile sig_atomic_t sigusr1_head, sigusr2_head; +static std::atomic<int> sigusr1_head, sigusr2_head; static void handler_SIGUSR(int signum) { switch (signum) { case SIGUSR1: - sigusr1_head += 1; + ++sigusr1_head; return; case SIGUSR2: - sigusr2_head += 1; + ++sigusr2_head; return; default: abort(); @@ -335,10 +337,10 @@ bool osal_progress_push(bool active) { static std::unordered_map<pid_t, actor_status> childs; -static volatile sig_atomic_t sigalarm_head; +static std::atomic<int> sigalarm_head; static void handler_SIGCHLD(int signum) { if (signum == SIGALRM) - sigalarm_head += 1; + ++sigalarm_head; } mdbx_pid_t osal_getpid(void) { return getpid(); } diff --git a/libs/libmdbx/src/test/test.cc b/libs/libmdbx/src/test/test.cc index beeba7abab..9af04ac9b0 100644 --- a/libs/libmdbx/src/test/test.cc +++ b/libs/libmdbx/src/test/test.cc @@ -144,8 +144,14 @@ void testcase::db_open() { db_prepare(); jitter_delay(true); + + unsigned mode = (unsigned)config.params.mode_flags; + if (config.params.random_writemap && flipcoin()) + mode ^= MDBX_WRITEMAP; + + actual_db_mode = mode; int rc = mdbx_env_open(db_guard.get(), config.params.pathname_db.c_str(), - (unsigned)config.params.mode_flags, 0640); + mode, 0640); if (unlikely(rc != MDBX_SUCCESS)) failure_perror("mdbx_env_open()", rc); diff --git a/libs/libmdbx/src/test/test.h b/libs/libmdbx/src/test/test.h index 8ecf5c46fe..43c6c03848 100644 --- a/libs/libmdbx/src/test/test.h +++ b/libs/libmdbx/src/test/test.h @@ -169,8 +169,9 @@ protected: static int oom_callback(MDBX_env *env, mdbx_pid_t pid, mdbx_tid_t tid, uint64_t txn, unsigned gap, size_t space, int retry); + unsigned actual_db_mode{0}; bool is_nested_txn_available() const { - return (config.params.mode_flags & MDBX_WRITEMAP) == 0; + return (actual_db_mode & MDBX_WRITEMAP) == 0; } void kick_progress(bool active) const; void db_prepare(); diff --git a/libs/libmdbx/src/version.c b/libs/libmdbx/src/version.c index 53bb45848d..f091d689ba 100644 --- a/libs/libmdbx/src/version.c +++ b/libs/libmdbx/src/version.c @@ -25,10 +25,10 @@ __dll_export const mdbx_version_info mdbx_version = { 0, 8, - 1, - 1, - {"2020-06-14T13:32:46+03:00", "968e4f98dd38e2a4e5cdf12c8203aa7b7abb149e", "0c5496d4d50ccedf8a182c01a76a8fbc7430d469", - "v0.8.1-1-g0c5496d"}, + 2, + 2, + {"2020-07-08T16:46:29+03:00", "91ba90ae5964c49852b2fe8ec5c6733e10d0414c", "4fffd033615ec45fd2669edcf26abd4134f76b94", + "v0.8.2-2-g4fffd03"}, sourcery}; __dll_export |