Compare commits

...

116 Commits

Author SHA1 Message Date
Leonid Yuriev
bf603bdffc mdbx: release v0.10.3
Acknowledgements:
-----------------
 - [Francisco Vallarino](https://github.com/fjvallarino) for [Haskell bindings for libmdbx](https://hackage.haskell.org/package/libmdbx).
 - [Alex Sharov](https://github.com/AskAlexSharov) for reporting and testing.
 - [Andrea Lanfranchi](https://github.com/AndreaLanfranchi) for contributing.

Extensions and improvements:
----------------------------
 - Added `cursor::erase()` overloads for `key` and for `key-value`.
 - Resolve minor Coverity Scan issues (no fixes but some hint/comment were added).
 - Resolve minor UndefinedBehaviorSanitizer issues (no fixes but some workaround were added).

Fixes:
------
 - Always setup `madvise` while opening DB (fixes https://github.com/erthink/libmdbx/issues/231).
 - Fixed checking legacy `P_DIRTY` flag (`0x10`) for nested/sub-pages.

Minors:
-------
 - Fixed getting revision number from middle of history during amalgamation (GNU Makefile).
 - Fixed search GCC tools for LTO (CMake scripts).
 - Fixed/reorder dirs list for search CLANG tools for LTO (CMake scripts).
 - Fixed/workarounds for CLANG < 9.x
 - Fixed CMake warning about compatibility with 3.8.2
2021-08-27 22:47:12 +03:00
Leonid Yuriev
cd73caac1c mdbx-test: remove entropy source and use fully determined PRNG. 2021-08-27 15:03:59 +03:00
Leonid Yuriev
aec884f0d1 mdbx-make: extend cross-qemu target.
Change-Id: I889e53207904a8aaec8469165ba2de7574bf417b
2021-08-26 17:56:39 +03:00
Leonid Yuriev
e3ce6d645a mdbx-make: fix/filter-out cross-qemu target arch list.
Change-Id: Ibafe6217aefcbee9f7a14fa216cf331d2e56c7ce
2021-08-26 17:56:10 +03:00
Leonid Yuriev
d2cba98f70 mdbx: always setup madvise while opening DB.
This partially revert 7da64b725d.

Hope fix https://github.com/erthink/libmdbx/issues/231
2021-08-25 14:13:07 +03:00
Leonid Yuriev
217e951e68 mdbx-make: fix getting revision number from middle of history. 2021-08-25 13:24:22 +03:00
Leonid Yuriev
99b75b5004 mdbx: fix/model minor Coverity issues. 2021-08-16 23:45:56 +03:00
Leonid Yuriev
42d545e579 mdbx: fix minor zero-length memcmp() UB. 2021-08-14 17:46:34 +03:00
Leonid Yuriev
e3300259ff mdbx: add minor enum-related workarounds for UndefinedBeheviorSanitizer. 2021-08-14 16:43:16 +03:00
Leonid Yuriev
68273acc2a mdbx: add and use MDBX_NOSANITIZE_ENUM macro. 2021-08-14 16:43:12 +03:00
Leonid Yuriev
e20664fe55 mdbx: Merge branch 'master' into devel branch. 2021-08-14 16:01:16 +03:00
Leonid Yuriev
8c761f5774 mdbx: update Ethereum address. 2021-08-14 00:19:40 +03:00
Leonid Yuriev
b6ffec12e4 "mdbx: ignore legacy P_DIRTY flag (0x10) for nested/sub-pages. 2021-08-11 15:49:46 +03:00
Leonid Yuriev
5d4d02617d mdbx-cmake: fix search GCC tools for LTO. 2021-08-05 02:26:36 +03:00
Leonid Yuriev
327b283136 mdbx-cmake: fix/reorder dirs list for search CLANG tools for LTO. 2021-08-05 02:26:36 +03:00
Leonid Yuriev
4bb0c57e29 mdbx: minor fixes/workarounds for CLANG < 9.x 2021-08-04 15:43:32 +03:00
Leonid Yuriev
a9cae5e314 mdbx-cmake: avoid CMake warning about compatibility with 3.8.2 2021-08-04 14:09:47 +03:00
Leonid Yuriev
b9ac607a5e mdbx: update patch for buildroot (old versions). 2021-08-03 00:57:22 +03:00
Leonid Yuriev
7e035115bb mdbx-doc: add ref to Haskell bindings. 2021-08-02 09:05:50 +03:00
Leonid Yuriev
099dd68630 mdbx-doc: add a note about the need for git tags.
Related to https://github.com/erthink/libmdbx/issues/227.
2021-08-01 17:58:42 +03:00
Leonid Yuriev
5d7ef50054 mdbx-ci: disable RDP by default for AppVeyor. 2021-08-01 17:41:18 +03:00
Andrea Lanfranchi
2395564c17 mdbx++: add cursor::erase() overloads for key and for key-value.
Resolves https://github.com/erthink/libmdbx/pull/226
2021-07-27 01:27:57 +03:00
Andrea Lanfranchi
5e7a685ba8 mdbx: add basic CMakeSettings.json for Visual Studio.
* move build files out of project dir
2021-07-27 01:27:57 +03:00
Andrea Lanfranchi
f7cbf41f03 mdbx: add .vscode to gitignore. 2021-07-27 01:03:48 +03:00
Leonid Yuriev
70933d81a8 mdbx: release v0.10.2
Acknowledgements:
-----------------
 - [Alex Sharov](https://github.com/AskAlexSharov) for reporting and testing.
 - [Andrea Lanfranchi](https://github.com/AndreaLanfranchi) for reporting bugs.
 - [Lionel Debroux](https://github.com/debrouxl) for fuzzing tests and reporting bugs.
 - [Sergey Fedotov](https://github.com/SergeyFromHell/) for [`node-mdbx` NodeJS bindings](https://www.npmjs.com/package/node-mdbx).
 - [Kris Zyp](https://github.com/kriszyp) for [`lmdbx-store` NodeJS bindings](https://github.com/kriszyp/lmdbx-store).
 - [Noel Kuntze](https://github.com/Thermi) for [draft Python bindings](https://github.com/erthink/libmdbx/commits/python-bindings).

New features, extensions and improvements:
------------------------------------------
 - [Allow to predefine/override `MDBX_BUILD_TIMESTAMP` for builds reproducibility](https://github.com/erthink/libmdbx/issues/201).
 - Added options support for `long-stochastic` script.
 - Avoided `MDBX_TXN_FULL` error for large transactions when possible.
 - The `MDBX_READERS_LIMIT` increased to `32767`.
 - Raise `MDBX_TOO_LARGE` under Valgrind/ASAN if being opened DB is 100 larger than RAM (to avoid hangs and OOM).
 - Minimized the size of poisoned/unpoisoned regions to avoid Valgrind/ASAN stuck.
 - Added more workarounds for QEMU for testing builds for 32-bit platforms, Alpha and Sparc architectures.
 - `mdbx_chk` now skips iteration & checking of DB' records if corresponding page-tree is corrupted (to avoid `SIGSEGV`, ASAN failures, etc).
 - Added more checks for [rare/fuzzing corruption cases](https://github.com/erthink/libmdbx/issues/217).

Backward compatibility break:
-----------------------------
 - Use file `VERSION.txt` for version information instead of `VERSION` to avoid collision with `#include <version>`.
 - Rename `slice::from/to_FOO_bytes()` to `slice::envisage_from/to_FOO_length()'.
 - Rename `MDBX_TEST_EXTRA` make's variable to `MDBX_SMOKE_EXTRA`.
 - Some details of the C++ API have been changed for subsequent freezing.

Fixes:
------
 - Fixed excess meta-pages checks in case `mdbx_chk` is called to check the DB for a specific meta page and thus could prevent switching to the selected meta page, even if the check passed without errors.
 - Fixed [recursive use of SRW-lock on Windows cause by `MDBX_NOTLS` option](https://github.com/erthink/libmdbx/issues/203).
 - Fixed [log a warning during a new DB creation](https://github.com/erthink/libmdbx/issues/205).
 - Fixed [false-negative `mdbx_cursor_eof()` result](https://github.com/erthink/libmdbx/issues/207).
 - Fixed [`make install` with non-GNU `install` utility (OSX, BSD)](https://github.com/erthink/libmdbx/issues/208).
 - Fixed [installation by `CMake` in special cases by complete use `GNUInstallDirs`'s variables](https://github.com/erthink/libmdbx/issues/209).
 - Fixed [C++ Buffer issue with `std::string` and alignment](https://github.com/erthink/libmdbx/issues/191).
 - Fixed `safe64_reset()` for platforms without atomic 64-bit compare-and-swap.
 - Fixed hang/shutdown on big-endian platforms without `__cxa_thread_atexit()`.
 - Fixed [using bad meta-pages if DB was partially/recoverable corrupted](https://github.com/erthink/libmdbx/issues/217).
 - Fixed extra `noexcept` for `buffer::&assign_reference()`.
 - Fixed `bootid` generation on Windows for case of change system' time.
 - Fixed [test framework keygen-related issue](https://github.com/erthink/libmdbx/issues/127).
2021-07-26 05:23:52 +03:00
Leonid Yuriev
a8115267a6 mdbx++: disable using C++20 concepts for CLANG < 12. 2021-07-26 05:16:29 +03:00
Leonid Yuriev
a9e99241b7 mdbx-make: fix/clean dependencies for make dist. 2021-07-26 03:53:11 +03:00
Leonid Yuriev
bca4adcdc7 mdbx-make: show compier and version by make show-options. 2021-07-26 03:53:11 +03:00
Leonid Yuriev
79281d59c7 mdbx++: workaround macro for clang bugs. 2021-07-26 03:53:11 +03:00
Leonid Yuriev
e6a83654a8 mdbx-make: add stub bench-related targets for case no ioarena. 2021-07-26 03:53:11 +03:00
Leonid Yuriev
f4f166a66d mdbx-make: avoid multiple tips about ioarena. 2021-07-26 03:53:11 +03:00
Leonid Yuriev
65fa0cf8ed mdbx++: revive encode/decode to hex/base58/base64 (squashed). 2021-07-26 03:53:11 +03:00
Leonid Yuriev
046dc02f73 mdbx: make MDBX_STRINGIFYmacro public. 2021-07-26 03:53:11 +03:00
Leonid Yuriev
c2fa453725 mdbx-test: fix keygen-related issue.
Fixes https://github.com/erthink/libmdbx/issues/127
2021-07-26 03:52:46 +03:00
Leonid Yuriev
e731260056 mdbx: fix SIGSEGV regression while copying DB with enabled audit. 2021-07-26 02:24:15 +03:00
Leonid Yuriev
9a04c9a350 mdbx: update available Bindings list. 2021-07-21 17:39:34 +03:00
Leonid Yuriev
13912be35d mdbx: update ChangeLog. 2021-07-21 13:37:42 +03:00
Leonid Yuriev
2e68adefb3 mdbx-doc: add README paragraph for testing. 2021-07-21 02:28:44 +03:00
Leonid Yuriev
bf9e6146df mdbx-make: add missing long-test to make help output. 2021-07-21 02:25:34 +03:00
Leonid Yuriev
2c190cfb56 mdbx-doc: add README paragraph for containers. 2021-07-21 02:24:24 +03:00
Leonid Yuriev
5fa2e30709 mdbx-test: add exclusive and accede options for DB operation mode. 2021-07-21 02:23:36 +03:00
Leonid Yuriev
faafa21480 mdbx-doc: refine Dixygen comments related to use custom comparators. 2021-07-21 02:23:10 +03:00
Leonid Yuriev
3f929e3766 mdbx-make: update thunk-makefile's target-list. 2021-07-21 02:22:33 +03:00
Leonid Yuriev
9a1dffc7dc mdbx: update ChangeLog. 2021-07-19 13:21:47 +03:00
Leonid Yuriev
c81ab53eb2 mdbx-test: add usage for long-stochastic scripts. 2021-07-19 12:42:57 +03:00
Leonid Yuriev
7759e52850 mdbx-windows: use MachineGuid of any size for bootid generation. 2021-07-19 12:07:45 +03:00
Leonid Yuriev
e7336e1d5e mdbx: add checks for unexpected LEAF2-pages.
The fourth case of https://github.com/erthink/libmdbx/issues/217.
2021-07-19 12:06:08 +03:00
Leonid Yuriev
68aef96f0a mdbx-tool: minor clarify mdbx_chk' logic key/data checks. 2021-07-19 12:06:08 +03:00
Leonid Yuriev
0b120b8fa9 mdbx-windows: fix bootid generation for case of change system' time. 2021-07-19 12:05:55 +03:00
Leonid Yuriev
28c36af67c mdbx: refine built-in ASAN option. 2021-07-16 16:04:09 +03:00
Leonid Yuriev
d67b9eaf17 mdbx: fix ASAN-regression after 1740043678.
Related to https://github.com/erthink/libmdbx/issues/217
2021-07-16 16:03:49 +03:00
Leonid Yuriev
6034985686 mdbx: add MDBX_ASAN_(UN)POISON_MEMORY_REGION() macros. 2021-07-16 16:03:49 +03:00
Leonid Yuriev
7da64b725d mdbx: perform madvise only for the first process opens a DB. 2021-07-16 16:03:49 +03:00
Leonid Yuriev
ba22ae9ab3 mdbx: update .gitignore 2021-07-16 16:03:49 +03:00
Leonid Yuriev
8aaf5d071b mdbx: fix pagecheck().
Added a check that the data of the BIGDATA node (containing the target page number) is located within the boundaries of the page being checked.

The third case of https://github.com/erthink/libmdbx/issues/217.
2021-07-16 15:55:18 +03:00
Leonid Yuriev
6a6ead6cfb mdbx-make: rename buildflags_tag to buildflags.tag (cosmetic). 2021-07-15 16:55:38 +03:00
Leonid Yuriev
10fefe87a6 mdbx-make: minor fix to save dist-check logs. 2021-07-15 16:53:16 +03:00
Leonid Yuriev
de3c028f0d mdbx-make: fix buildflags_tag embedding into config.h. 2021-07-14 10:30:29 +03:00
Leonid Yuriev
b6233ae2e5 mdbx-tools: minor fix/unify error counters. 2021-07-14 10:29:54 +03:00
Leonid Yuriev
fe5f008d39 mdbx-tools: skip iteration & checking records if corresponding tree is corrupted.
Hope final for https://github.com/erthink/libmdbx/issues/217
2021-07-14 03:59:56 +03:00
Leonid Yuriev
c8743cb9c4 mdbx: fix null-deref while override invalid meta-pages.
Related to https://github.com/erthink/libmdbx/issues/217
2021-07-14 03:59:52 +03:00
Leonid Yuriev
1995754bc3 mdbx-test: add workarounds for QEMU (all 32-bit, Alpha, Sparc). 2021-07-13 17:38:08 +03:00
Leonid Yuriev
f749b3deee mdbx-test: minor refine stochastic script to be able use arithmetic in the arguments. 2021-07-13 13:51:24 +03:00
Leonid Yuriev
ebfffe3f2b mdbx-make: use --db-upto-mb option to be able testing 32-bit targets under QEMU. 2021-07-13 13:49:58 +03:00
Leonid Yuriev
de4a6baf80 mdbx-test: add --db-upto-mb option for stochastic script. 2021-07-13 13:49:33 +03:00
Leonid Yuriev
584326e9b6 mdbx-make: rename MDBX_TEST_EXTRA to MDBX_SMOKE_EXTRA. 2021-07-13 13:48:56 +03:00
Leonid Yuriev
a7becdc6b3 mdbx-test: add --size-upper-upto for simplify cross-testing 32-bit code with QEMU on 64-bit host. 2021-07-13 13:48:12 +03:00
Leonid Yuriev
4de2dcebb5 mdbx: increase the MDBX_READERS_LIMIT to 32767.
Fixes https://github.com/erthink/libmdbx/issues/219.
2021-07-11 02:44:19 +03:00
Leonid Yuriev
678a80dd19 mdbx: fix hang/shutdown on big-endian platforms without __cxa_thread_atexit().
Change-Id: I1bf706abaaf42d5b40751d85ed7c7a83d02acaf5
2021-07-11 02:25:39 +03:00
Leonid Yuriev
55d1f5e9c0 mdbx++: remove extra noexcept for buffer::&assign_reference(). 2021-07-11 02:25:39 +03:00
Leonid Yuriev
c18bf4f898 mdbx: minor clarify mdbx_mapresize(). 2021-07-11 02:25:39 +03:00
Leonid Yuriev
728e7f92b2 mdbx: minor fix mdbx_mresize() to preserve result code for read-only cases. 2021-07-11 02:25:39 +03:00
Leonid Yuriev
1740043678 mdbx: minimize the size of poisoned/unpoisoned regions to avoid ASAN hangs.
More for second case of https://github.com/erthink/libmdbx/issues/217
2021-07-11 02:25:26 +03:00
Leonid Yuriev
891d68838a mdbx: return MDBX_TOO_LARGE under Valgrind/ASAN if being opened DB is 100 larger than RAM.
More for second case of https://github.com/erthink/libmdbx/issues/217
2021-07-11 02:25:07 +03:00
Leonid Yuriev
108398c213 mdbx: refine rollback while opening weak/invalid DB.
More for https://github.com/erthink/libmdbx/issues/217
2021-07-11 02:24:51 +03:00
Leonid Yuriev
8bdee27248 mdbx: create/refactoring override_meta(). 2021-07-11 02:24:51 +03:00
Leonid Yuriev
00c6dc9788 mdbx: re-verify head and steady meta-pages while opening db by the first process.
Basic fix for https://github.com/erthink/libmdbx/issues/217
2021-07-11 02:24:06 +03:00
Leonid Yuriev
fb67682a79 mdbx: refine mdbx_validate_meta(). 2021-07-10 17:24:26 +03:00
Leonid Yuriev
5ed50a4739 mdbx: remove filesize arg from header/meta read functions (refactoring). 2021-07-10 16:10:30 +03:00
Leonid Yuriev
c30c3def8b mdbx: the filesize field of mdbx_mmap_t now is not specific for Windows. 2021-07-10 16:10:30 +03:00
Леонид Юрьев (Leonid Yuriev)
2f74f405ae mdbx: avoid returning MDBX_TXN_FULL error when possible. 2021-07-09 18:29:31 +03:00
Леонид Юрьев (Leonid Yuriev)
c7e05f63e6 mdbx-test: remove vector[...] from Valgrind's suppressions. 2021-07-09 17:44:27 +03:00
Leonid Yuriev
d65305564f mdbx-test: more suppressions for Valrgind (for case db-page less than systen-page). 2021-07-09 17:44:27 +03:00
Leonid Yuriev
660c302525 mdbx-test: adapt long-stochastic script for old bash version (Mac OS). 2021-07-09 17:44:27 +03:00
Leonid Yuriev
d7aad3a7cf mdbx-make: distinct smoke* and test* targets. 2021-07-09 17:44:27 +03:00
Leonid Yuriev
15ed0f6a84 mdbx++: workaround for compile-time 'uninitialized' warning (i.e. a GCC's bug) from GCC 10.x with enabled UB-sanitizer. 2021-07-09 17:44:27 +03:00
Leonid Yuriev
ffb8d23632 mdbx++: minor fixes for UN-sanitizer. 2021-07-09 17:44:27 +03:00
Leonid Yuriev
682632756f mdbx-test: add options support for long-stochastic script. 2021-07-09 17:44:27 +03:00
Leonid Yuriev
39c5e66ed1 mdbx: fix safe64_reset() for platforms without atomic 64-bit compare-and-swap. 2021-07-09 17:44:27 +03:00
Leonid Yuriev
b66780633e mdbx-tools: linking with math library (-lm). 2021-07-06 13:45:26 +03:00
Leonid Yuriev
bd2bb51f0f mdbx++: rework buffer::silo to avoid use std::string. 2021-07-06 13:45:26 +03:00
Леонид Юрьев (Leonid Yuriev)
ac69464143 mdbx-make: change prefix of the 'TIP' messages (cosmetics). 2021-07-04 13:23:53 +03:00
Leonid Yuriev
62889b5b7f mdbx-test: use mdbx::buffer from mdbx++. 2021-07-04 13:23:53 +03:00
Leonid Yuriev
c4a696be1d mdbx-test: add workaround for CLANG/LLVM STL stupidity of std::set<>. 2021-07-04 00:11:04 +03:00
Leonid Yuriev
cf5f31c577 mdbx: make __cold attribute first (cosmetic). 2021-07-03 01:51:04 +03:00
Leonid Yuriev
fa49e5a57b mdbx++: rename slice::from/to_FOO_bytes() to `slice::envisage_from/to_FOO_length()'. 2021-07-02 21:20:04 +03:00
Leonid Yuriev
750a17e41e mdbx: more spelling. 2021-07-02 21:19:58 +03:00
Leonid Yuriev
5d4281fbbe mdbx: minor fix spelling. 2021-06-26 18:54:00 +03:00
Leonid Yuriev
2c18225654 mdbx-make: minor cleanup shell in/out redirection spaces. 2021-06-26 17:38:52 +03:00
Leonid Yuriev
cc6610f42c mdbx: more/refine C++ crutches for mad MSVC compiler. 2021-06-24 15:42:03 +03:00
Leonid Yuriev
77a1f32e2a mdbx: use VERSION.txt instead of VERSION to avoid collision with #include <version>. 2021-06-24 14:59:29 +03:00
Leonid Yuriev
63e7276c7d mdbx: update ChangeLog. 2021-06-18 15:13:51 +03:00
Leonid Yuriev
a5e10b4ea1 mdbx: minor refine mdbx_cursor_eof(). 2021-06-18 01:17:48 +03:00
Leonid Yuriev
b8e621cb2f mdbx: mdbx: avoid log a warning about empty header during DB creation.
Resolve https://github.com/erthink/libmdbx/issues/205.
2021-06-18 01:09:26 +03:00
Leonid Yuriev
68a164da2b mdbx-test: add mdbx_cursor_eof() checking.
Related to https://github.com/erthink/libmdbx/issues/207.
2021-06-17 21:44:48 +03:00
Leonid Yuriev
5db855d728 mdbx: fix false-negative mdbx_cursor_eof() result.
Fixes https://github.com/erthink/libmdbx/issues/207.
2021-06-17 21:44:24 +03:00
Leonid Yuriev
06aa596519 mdbx-test: fix minor warnings from old GCC versions. 2021-06-17 21:43:15 +03:00
Leonid Yuriev
d47864dedf mdbx-cmake: use GNUInstallDirs variables for all cases.
Resolves https://github.com/erthink/libmdbx/issues/209.
2021-06-17 17:21:20 +03:00
Leonid Yuriev
581ca4fdf4 mdbx-ci: performs make's install target during check.
Related to https://github.com/erthink/libmdbx/issues/208.
2021-06-17 15:15:57 +03:00
Leonid Yuriev
6771b7526c mdbx-make: use only portable option of install utility.
Fixes https://github.com/erthink/libmdbx/issues/208.
2021-06-17 15:15:54 +03:00
Leonid Yuriev
d7c06b1337 mdbx: partial fix for recursive SRW-lock with MDBX_NOTLS on Windows.
Here are some changes to avoid recursive acquisition of SRW-lock,
which is still in use:
 - Read transactions don't acquire the shared SRW-lock with `MDBX_NOTLS.
 - Memory-mapping of DB is always kept while DB opened,
   therefore following limitations are:
 - DB file can't be shrinked while it used,
   including auto-shrink due to auto-compactification with corresponding geometry settings.
 - The upper limit of DB size can't be changed while DB is used.
 - The DB can grow within the upper size limit defined while opening by a first process,
   but this does not work under Wine since there is no `NtExtendSection()` function.

Partially fix https://github.com/erthink/libmdbx/issues/203
2021-06-10 13:42:30 +03:00
Leonid Yuriev
18bc28bea2 mdbx: announce more TODO. 2021-06-10 02:47:40 +03:00
Leonid Yuriev
93e6e64eb0 mdbx: update ChangeLog. 2021-06-09 13:48:03 +03:00
Leonid Yuriev
0cd1eac6a8 mdbx: don't check other meta pages if one is specified for verification/recovery.
Fixes https://github.com/erthink/libmdbx/issues/202
2021-06-08 20:27:18 +03:00
Leonid Yuriev
0e83a8e5ef mdbx-doc: add note for Reproducible builds.
More for https://github.com/erthink/libmdbx/issues/201
2021-06-02 17:08:40 +03:00
Leonid Yuriev
f951f246b8 mdbx: allow to predefine/override MDBX_BUILD_TIMESTAMP for builds reproducibility.
Resolve https://github.com/erthink/libmdbx/issues/201
2021-06-02 14:50:22 +03:00
44 changed files with 3931 additions and 2313 deletions

2
.github/FUNDING.yml vendored
View File

@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://sobe.ru/na/libmdbx', 'https://paypal.me/erthink', 'https://etherscan.io/address/0xe8a741a2a0a4850c5bdbb415aaaaf8fb46f06fb7']
custom: ['https://sobe.ru/na/libmdbx', 'https://paypal.me/erthink', 'https://etherscan.io/address/0x19291d8658f762f3baceae1700c0b9466572ceab']

View File

@@ -8,3 +8,4 @@
^\.gitignore$
^\.travis\.yml$
^packages/buildroot/
^CMakeSettings\.json$

View File

@@ -9,6 +9,7 @@ acision
AClass
adata
addprefix
addressof
addsuffix
addtogroup
advapi
@@ -22,6 +23,7 @@ alevel
alexey
Alfke
alignas
alignof
alldbs
ALLDUPS
ALLEXTERNALS
@@ -103,6 +105,7 @@ bitfield
bitmaps
bitmask
bitness
bitops
blogger
blogs
blogspot
@@ -126,6 +129,7 @@ buflen
bufsize
BUGLIST
bugzilla
buildflags
buildpack
buildroot
builtins
@@ -170,6 +174,7 @@ cifs
cinttypes
circleci
claude
climits
clockid
CLOEXEC
closefile
@@ -283,6 +288,8 @@ ddl
deadread
deadwrite
debian
Debroux
debrouxl
debruijn
DECC
declspec
@@ -471,6 +478,7 @@ exactkey
exactp
excpt
exe
executables
exename
exherbo
exitcode
@@ -489,6 +497,7 @@ FCXX
fd
fdatasync
featuredarticles
fedotov
FEEDNAME
feof
ferror
@@ -511,6 +520,7 @@ Firefox
firstvalue
fixedpoint
FIXME
fjvallarino
flagbit
flg
flipcoin
@@ -608,9 +618,11 @@ grep
gtags
gz
gzip
hackage
HAGL
hallvard
hardcoded
haskell
HASSEMAPHORE
hdiutil
hdr
@@ -681,6 +693,7 @@ img
impl
IMPLIB
inblock
INCLUDEDIR
indx
INDXSIZE
ini
@@ -775,6 +788,7 @@ klen
KMGTPEZY
knipp
kp
kriszyp
ks
ksize
kstat
@@ -785,6 +799,7 @@ kval
Lanfranchi
largedata
largepage
lastbyte
lastest
lastvalue
lastword
@@ -830,6 +845,7 @@ LLV
llvm
lmb
lmdb
lmdbx
LMEM
LNK
lnps
@@ -875,6 +891,7 @@ MACROFILE
MADV
madvise
mahlonsmith
mailto
maindb
mainpage
maj
@@ -960,6 +977,7 @@ microsoft
Mikkelson
minflt
MINGW
minimalistic
minkeys
minlen
MINSIZEREL
@@ -1097,6 +1115,7 @@ nordahead
NOREPLACE
NORESERVE
noreturn
NOSANITIZE
nospill
nosubdir
nosync
@@ -1107,8 +1126,10 @@ notls
notracking
NOWAIT
npages
npmjs
npos
npr
nproc
nptl
nreaders
nrepeat
@@ -1272,7 +1293,6 @@ ppc
pragma
pread
prealloc
prealloc'd
PREDEF
prefetch
Prefetcher
@@ -1286,7 +1306,7 @@ PRIa
PRId
PRIi
printf
Prioritization
prioritization
PRIu
prng
prno
@@ -1322,6 +1342,7 @@ putflags
pv
pvalue
PVOID
pwd
PWIM
PWIN
PWOF
@@ -1466,6 +1487,7 @@ semop
sems
sendfile
sepkey
sergey
SETALL
SETFD
setlen
@@ -1648,6 +1670,7 @@ svg
svnweb
svr
swait
swappable
symas
SYMLINKS
syncbytes
@@ -1716,6 +1739,7 @@ toolset
tooltip
torquem
TOUPPER
transcoder
transcoding
treeview
tribudubois
@@ -1771,6 +1795,7 @@ underfilled
UNDOC
unicode
UNIFORUM
uninit
uninstall
UNINSTALLING
uniq
@@ -1789,6 +1814,7 @@ updation
upsert
UPSERTING
upsertion
upto
URG
url
usec
@@ -1814,6 +1840,7 @@ Vaefnrs
valbool
valgrind
validator
Vallarino
valnum
valsize
valstr
@@ -1948,3 +1975,4 @@ zi
ZLIB
zu
zx
Zyp

View File

@@ -24,8 +24,8 @@ jobs:
run: |
echo "::set-output name=tarball::$(ls *.tar.gz)"
echo "::set-output name=zip::$(ls *.zip)"
echo "::set-output name=suffix_unix::$(sed 's|^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\.\(.*\)$|\1|' dist/VERSION)"
echo "::set-output name=suffix_dos::$(sed 's|^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\.\(.*\)$|\1|' dist/VERSION | tr . _)"
echo "::set-output name=suffix_unix::$(sed 's|^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\.\(.*\)$|\1|' dist/VERSION.txt)"
echo "::set-output name=suffix_dos::$(sed 's|^\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\.\(.*\)$|\1|' dist/VERSION.txt | tr . _)"
- name: Create Release
id: create_release
uses: actions/create-release@v1

5
.gitignore vendored
View File

@@ -15,11 +15,13 @@
.idea
.le.ini
.vs/
.vscode/
cmake-build-*
@*
core
mdbx_example
libmdbx.creator.user
CMakeLists.txt.user
mdbx_chk
mdbx_copy
mdbx_drop
@@ -36,6 +38,9 @@ valgrind.*
src/version.c
src/config.h
dist/
dist-check/
dist-checked.tag
*.tar*
docs/Doxyfile
docs/html/
buildflags.tag

View File

@@ -34,10 +34,14 @@
## The Future will (be) Positive. Всё будет хорошо.
##
cmake_minimum_required(VERSION 3.8.2)
if(CMAKE_VERSION VERSION_LESS 3.12)
cmake_minimum_required(VERSION 3.8.2)
else()
cmake_minimum_required(VERSION 3.12)
endif()
cmake_policy(PUSH)
cmake_policy(VERSION 3.8.2)
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
if(NOT CMAKE_VERSION VERSION_LESS 3.15)
cmake_policy(SET CMP0091 NEW)
endif()
@@ -70,7 +74,7 @@ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git" AND
message(SEND_ERROR "Git command-line tool not found")
endif()
set(MDBX_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" AND
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx.c" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mdbx.c++" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" AND
@@ -228,8 +232,13 @@ else()
endif()
if(CMAKE_INTERPROCEDURAL_OPTIMIZATION_AVAILABLE
OR GCC_LTO_AVAILABLE OR MSVC_LTO_AVAILABLE OR CLANG_LTO_AVAILABLE)
OR GCC_LTO_AVAILABLE OR MSVC_LTO_AVAILABLE OR
(CLANG_LTO_AVAILABLE AND
((DEFINED MDBX_ENABLE_TESTS AND NOT MDBX_ENABLE_TESTS)
OR NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.0)))
option(INTERPROCEDURAL_OPTIMIZATION "Enable interprocedural/LTO optimization" ${INTERPROCEDURAL_OPTIMIZATION_DEFAULT})
else()
set(INTERPROCEDURAL_OPTIMIZATION OFF)
endif()
if(INTERPROCEDURAL_OPTIMIZATION)
@@ -328,9 +337,11 @@ list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_20 HAS_CXX20)
if(NOT DEFINED MDBX_CXX_STANDARD)
if(DEFINED CMAKE_CXX_STANDARD)
set(MDBX_CXX_STANDARD ${CMAKE_CXX_STANDARD})
elseif(NOT HAS_CXX20 LESS 0)
elseif(NOT HAS_CXX20 LESS 0
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9))
set(MDBX_CXX_STANDARD 20)
elseif(NOT HAS_CXX17 LESS 0)
elseif(NOT HAS_CXX17 LESS 0
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5))
set(MDBX_CXX_STANDARD 17)
elseif(NOT HAS_CXX14 LESS 0)
set(MDBX_CXX_STANDARD 14)
@@ -483,7 +494,7 @@ if(CMAKE_CXX_COMPILER_LOADED AND MDBX_CXX_STANDARD GREATER_EQUAL 11 AND MDBX_CXX
endif()
if(NOT MDBX_WITHOUT_MSVC_CRT
AND NOT (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4)
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.9)
AND NOT (MSVC AND MSVC_VERSION LESS 1900))
option(MDBX_BUILD_CXX "Build C++ portion" ON)
else()
@@ -539,6 +550,13 @@ else()
message(STATUS "Use C${MDBX_C_STANDARD} for libmdbx but C++ portion is disabled")
endif()
if(SUBPROJECT AND MSVC)
if(MSVC_VERSION LESS 1900)
message(FATAL_ERROR "At least \"Microsoft C/C++ Compiler\" version 19.0.24234.1 (Visual Studio 2015 Update 3) is required.")
endif()
add_compile_options("/utf-8")
endif()
macro(target_setup_options TARGET)
if(DEFINED INTERPROCEDURAL_OPTIMIZATION)
set_target_properties(${TARGET} PROPERTIES
@@ -549,15 +567,19 @@ macro(target_setup_options TARGET)
if(MDBX_BUILD_CXX)
set_target_properties(${TARGET} PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
if(MSVC AND NOT MSVC_VERSION LESS 1910)
target_compile_options(${TARGET} INTERFACE "/Zc:__cplusplus")
endif()
endif()
if(CC_HAS_FASTMATH)
if(CC_HAS_FASTMATH
AND NOT (CMAKE_COMPILER_IS_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10))
target_compile_options(${TARGET} PRIVATE "-ffast-math")
endif()
if(CC_HAS_VISIBILITY)
target_compile_options(${TARGET} PRIVATE "-fvisibility=hidden")
endif()
if(BUILD_FOR_NATIVE_CPU AND CC_HAS_ARCH_NATIVE)
target_compile_options(${TARGET} PUBLIC "-march=native")
target_compile_options(${TARGET} PRIVATE "-march=native")
endif()
endmacro()
@@ -573,7 +595,7 @@ macro(libmdbx_setup_libs TARGET MODE)
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Android")
target_link_libraries(${TARGET} ${MODE} log)
endif()
if(LIBCXX_FILESYSTEM AND MDBX_BUILD_CXX)
if(MDBX_CXX_STANDARD GREATER_EQUAL 17 AND LIBCXX_FILESYSTEM AND MDBX_BUILD_CXX)
target_link_libraries(${TARGET} ${MODE} ${LIBCXX_FILESYSTEM})
endif()
endmacro()
@@ -670,34 +692,34 @@ endif()
# mdbx-shared-lib installation
if(NOT DEFINED MDBX_DLL_INSTALL_DESTINATION)
if(WIN32)
set(MDBX_DLL_INSTALL_DESTINATION bin)
set(MDBX_DLL_INSTALL_DESTINATION ${CMAKE_INSTALL_BINDIR})
else()
set(MDBX_DLL_INSTALL_DESTINATION lib)
set(MDBX_DLL_INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
endif()
if(MDBX_BUILD_SHARED_LIBRARY)
if(CMAKE_VERSION VERSION_LESS 3.12)
install(TARGETS mdbx EXPORT libmdbx
LIBRARY DESTINATION ${MDBX_DLL_INSTALL_DESTINATION} COMPONENT runtime
OBJECTS DESTINATION lib COMPONENT devel
ARCHIVE DESTINATION lib COMPONENT devel
PUBLIC_HEADER DESTINATION include COMPONENT devel
INCLUDES DESTINATION include COMPONENT devel)
OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel)
else()
install(TARGETS mdbx EXPORT libmdbx
LIBRARY DESTINATION ${MDBX_DLL_INSTALL_DESTINATION} COMPONENT runtime
NAMELINK_COMPONENT devel
OBJECTS DESTINATION lib COMPONENT devel
ARCHIVE DESTINATION lib COMPONENT devel
PUBLIC_HEADER DESTINATION include COMPONENT devel
INCLUDES DESTINATION include COMPONENT devel)
OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel)
endif()
endif(MDBX_BUILD_SHARED_LIBRARY)
# mdbx-tools installation
if(MDBX_BUILD_TOOLS)
if(NOT DEFINED MDBX_TOOLS_INSTALL_DESTINATION)
set(MDBX_TOOLS_INSTALL_DESTINATION bin)
set(MDBX_TOOLS_INSTALL_DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
install(
TARGETS
@@ -712,7 +734,7 @@ if(MDBX_BUILD_TOOLS)
COMPONENT runtime)
if(MDBX_INSTALL_MANPAGES)
if(NOT DEFINED MDBX_MAN_INSTALL_DESTINATION)
set(MDBX_MAN_INSTALL_DESTINATION man/man1)
set(MDBX_MAN_INSTALL_DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
endif()
install(
FILES
@@ -731,26 +753,28 @@ endif(MDBX_BUILD_TOOLS)
if(MDBX_INSTALL_STATIC)
if(CMAKE_VERSION VERSION_LESS 3.12)
install(TARGETS mdbx-static EXPORT libmdbx
LIBRARY DESTINATION lib COMPONENT devel
OBJECTS DESTINATION lib COMPONENT devel
ARCHIVE DESTINATION lib COMPONENT devel
PUBLIC_HEADER DESTINATION include COMPONENT devel
INCLUDES DESTINATION include COMPONENT devel)
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel)
else()
install(TARGETS mdbx-static EXPORT libmdbx
LIBRARY DESTINATION lib COMPONENT devel
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
NAMELINK_COMPONENT devel
OBJECTS DESTINATION lib COMPONENT devel
ARCHIVE DESTINATION lib COMPONENT devel
PUBLIC_HEADER DESTINATION include COMPONENT devel
INCLUDES DESTINATION include COMPONENT devel)
OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT devel)
endif()
endif(MDBX_INSTALL_STATIC)
################################################################################
# collect options & build info
string(TIMESTAMP MDBX_BUILD_TIMESTAMP UTC)
if(NOT DEFINED MDBX_BUILD_TIMESTAMP)
string(TIMESTAMP MDBX_BUILD_TIMESTAMP UTC)
endif()
set(MDBX_BUILD_FLAGS ${CMAKE_C_FLAGS})
if(MDBX_BUILD_CXX)
set(MDBX_BUILD_FLAGS ${CMAKE_CXX_FLAGS})

26
CMakeSettings.json Normal file
View File

@@ -0,0 +1,26 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\libmdbx\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
}
]
}

View File

@@ -1,23 +1,100 @@
ChangeLog
---------
## v0.10.2 (in development)
## v0.11.x (in development)
### TODO
- [Engage an "overlapped I/O" on Windows](https://github.com/erthink/libmdbx/issues/224).
- [Simple careful mode for working with corrupted DB](https://github.com/erthink/libmdbx/issues/223).
- [Move most of `mdbx_chk` functional to the library API](https://github.com/erthink/libmdbx/issues/204).
- [Replace SRW-lock on Windows to allow shrink DB with `MDBX_NOTLS` option](https://github.com/erthink/libmdbx/issues/210).
- [More flexible support of asynchronous runtime/framework(s)](https://github.com/erthink/libmdbx/issues/200).
- [Migration guide from LMDB to MDBX](https://github.com/erthink/libmdbx/issues/199).
- [Get rid of dirty-pages list in MDBX_WRITEMAP mode](https://github.com/erthink/libmdbx/issues/193).
- [Large/Overflow pages accounting for dirty-room](https://github.com/erthink/libmdbx/issues/192).
- [C++ Buffer issue](https://github.com/erthink/libmdbx/issues/191).
- [Support for RAW devices](https://github.com/erthink/libmdbx/issues/124).
- [Test framework issue](https://github.com/erthink/libmdbx/issues/127).
- [Support MessagePack for Keys & Values](https://github.com/erthink/libmdbx/issues/115).
- [Engage new terminology](https://github.com/erthink/libmdbx/issues/137).
- Finalize C++ API (few typos and trivia bugs are still likely for now).
- Finalize C++ API (few typos and bugs are still maybe for now).
- Packages for [Astra Linux](https://astralinux.ru/), [ALT Linux](https://www.altlinux.org/), [ROSA Linux](https://www.rosalinux.ru/), etc.
## v0.10.3 at 2021-08-27
Acknowledgements:
- [Francisco Vallarino](https://github.com/fjvallarino) for [Haskell bindings for libmdbx](https://hackage.haskell.org/package/libmdbx).
- [Alex Sharov](https://github.com/AskAlexSharov) for reporting and testing.
- [Andrea Lanfranchi](https://github.com/AndreaLanfranchi) for contributing.
Extensions and improvements:
- Added `cursor::erase()` overloads for `key` and for `key-value`.
- Resolve minor Coverity Scan issues (no fixes but some hint/comment were added).
- Resolve minor UndefinedBehaviorSanitizer issues (no fixes but some workaround were added).
Fixes:
- Always setup `madvise` while opening DB (fixes https://github.com/erthink/libmdbx/issues/231).
- Fixed checking legacy `P_DIRTY` flag (`0x10`) for nested/sub-pages.
Minors:
- Fixed getting revision number from middle of history during amalgamation (GNU Makefile).
- Fixed search GCC tools for LTO (CMake scripts).
- Fixed/reorder dirs list for search CLANG tools for LTO (CMake scripts).
- Fixed/workarounds for CLANG < 9.x
- Fixed CMake warning about compatibility with 3.8.2
## v0.10.2 at 2021-07-26
Acknowledgements:
- [Alex Sharov](https://github.com/AskAlexSharov) for reporting and testing.
- [Andrea Lanfranchi](https://github.com/AndreaLanfranchi) for reporting bugs.
- [Lionel Debroux](https://github.com/debrouxl) for fuzzing tests and reporting bugs.
- [Sergey Fedotov](https://github.com/SergeyFromHell/) for [`node-mdbx` NodeJS bindings](https://www.npmjs.com/package/node-mdbx).
- [Kris Zyp](https://github.com/kriszyp) for [`lmdbx-store` NodeJS bindings](https://github.com/kriszyp/lmdbx-store).
- [Noel Kuntze](https://github.com/Thermi) for [draft Python bindings](https://github.com/erthink/libmdbx/commits/python-bindings).
New features, extensions and improvements:
- [Allow to predefine/override `MDBX_BUILD_TIMESTAMP` for builds reproducibility](https://github.com/erthink/libmdbx/issues/201).
- Added options support for `long-stochastic` script.
- Avoided `MDBX_TXN_FULL` error for large transactions when possible.
- The `MDBX_READERS_LIMIT` increased to `32767`.
- Raise `MDBX_TOO_LARGE` under Valgrind/ASAN if being opened DB is 100 larger than RAM (to avoid hangs and OOM).
- Minimized the size of poisoned/unpoisoned regions to avoid Valgrind/ASAN stuck.
- Added more workarounds for QEMU for testing builds for 32-bit platforms, Alpha and Sparc architectures.
- `mdbx_chk` now skips iteration & checking of DB' records if corresponding page-tree is corrupted (to avoid `SIGSEGV`, ASAN failures, etc).
- Added more checks for [rare/fuzzing corruption cases](https://github.com/erthink/libmdbx/issues/217).
Backward compatibility break:
- Use file `VERSION.txt` for version information instead of `VERSION` to avoid collision with `#include <version>`.
- Rename `slice::from/to_FOO_bytes()` to `slice::envisage_from/to_FOO_length()'.
- Rename `MDBX_TEST_EXTRA` make's variable to `MDBX_SMOKE_EXTRA`.
- Some details of the C++ API have been changed for subsequent freezing.
Fixes:
- Fixed excess meta-pages checks in case `mdbx_chk` is called to check the DB for a specific meta page and thus could prevent switching to the selected meta page, even if the check passed without errors.
- Fixed [recursive use of SRW-lock on Windows cause by `MDBX_NOTLS` option](https://github.com/erthink/libmdbx/issues/203).
- Fixed [log a warning during a new DB creation](https://github.com/erthink/libmdbx/issues/205).
- Fixed [false-negative `mdbx_cursor_eof()` result](https://github.com/erthink/libmdbx/issues/207).
- Fixed [`make install` with non-GNU `install` utility (OSX, BSD)](https://github.com/erthink/libmdbx/issues/208).
- Fixed [installation by `CMake` in special cases by complete use `GNUInstallDirs`'s variables](https://github.com/erthink/libmdbx/issues/209).
- Fixed [C++ Buffer issue with `std::string` and alignment](https://github.com/erthink/libmdbx/issues/191).
- Fixed `safe64_reset()` for platforms without atomic 64-bit compare-and-swap.
- Fixed hang/shutdown on big-endian platforms without `__cxa_thread_atexit()`.
- Fixed [using bad meta-pages if DB was partially/recoverable corrupted](https://github.com/erthink/libmdbx/issues/217).
- Fixed extra `noexcept` for `buffer::&assign_reference()`.
- Fixed `bootid` generation on Windows for case of change system' time.
- Fixed [test framework keygen-related issue](https://github.com/erthink/libmdbx/issues/127).
## v0.10.1 at 2021-06-01
Acknowledgements:

View File

@@ -29,7 +29,8 @@ INSTALL ?= install
CC ?= gcc
CFLAGS_EXTRA ?=
LD ?= ld
MDBX_BUILD_OPTIONS ?= -DNDEBUG=1
MDBX_BUILD_OPTIONS ?=-DNDEBUG=1
MDBX_BUILD_TIMESTAMP ?=$(shell date +%Y-%m-%dT%H:%M:%S%z)
CFLAGS ?= -std=gnu11 -O2 -g -Wall -Werror -Wextra -Wpedantic -ffunction-sections -fPIC -fvisibility=hidden -pthread -Wno-error=attributes $(CFLAGS_EXTRA)
# -Wno-tautological-compare
CXX ?= g++
@@ -38,9 +39,9 @@ CXXSTD ?= $(eval CXXSTD := $$(shell PROBE=$$$$([ -f mdbx.c++ ] && echo mdbx.c++
CXXFLAGS = $(CXXSTD) $(filter-out -std=gnu11,$(CFLAGS))
# TIP: Try append '--no-as-needed,-lrt' for ability to built with modern glibc, but then use with the old.
LIBS ?= $(shell uname | grep -qi SunOS && echo "-lkstat") $(shell uname | grep -qi -e Darwin -e OpenBSD || echo "-lrt") $(shell uname | grep -qi Windows && echo "-lntdll")
LIBS ?= $(strip -lm $(shell uname | grep -qi SunOS && echo "-lkstat") $(shell uname | grep -qi -e Darwin -e OpenBSD || echo "-lrt") $(shell uname | grep -qi Windows && echo "-lntdll"))
LDFLAGS ?= $(shell $(LD) --help 2>/dev/null | grep -q -- --gc-sections && echo '-Wl,--gc-sections,-z,relro,-O1')$(shell $(LD) --help 2>/dev/null | grep -q -- -dead_strip && echo '-Wl,-dead_strip')
LDFLAGS ?= $(strip $(shell $(LD) --help 2>/dev/null | grep -q -- --gc-sections && echo '-Wl,--gc-sections,-z,relro,-O1')$(shell $(LD) --help 2>/dev/null | grep -q -- -dead_strip && echo '-Wl,-dead_strip'))
EXE_LDFLAGS ?= -pthread
################################################################################
@@ -58,8 +59,9 @@ HEADERS := mdbx.h mdbx.h++
LIBRARIES := libmdbx.a libmdbx.$(SO_SUFFIX)
TOOLS := mdbx_stat mdbx_copy mdbx_dump mdbx_load mdbx_chk mdbx_drop
MANPAGES := mdbx_stat.1 mdbx_copy.1 mdbx_dump.1 mdbx_load.1 mdbx_chk.1 mdbx_drop.1
TIP := // TIP:
.PHONY: all help options lib tools clean install uninstall
.PHONY: all help options lib tools clean install uninstall check_buildflags_tag
.PHONY: install-strip install-no-strip strip libmdbx mdbx show-options
ifeq ("$(origin V)", "command line")
@@ -72,11 +74,11 @@ endif
ifeq ($(MDBX_BUILD_VERBOSE),1)
QUIET :=
HUSH :=
$(info ## TIP: Use `make V=0` for quiet.)
$(info $(TIP) Use `make V=0` for quiet.)
else
QUIET := @
HUSH := >/dev/null
$(info ## TIP: Use `make V=1` for verbose.)
$(info $(TIP) Use `make V=1` for verbose.)
endif
all: show-options $(LIBRARIES) $(TOOLS)
@@ -102,19 +104,22 @@ help:
@echo " make bench-clean - remove temp database(s) after benchmark"
#> dist-cutoff-begin
@echo ""
@echo " make smoke - fast smoke test"
@echo " make test - basic test"
@echo " make check - basic test"
@echo " make memcheck - build with Valgrind's and test with memcheck tool"
@echo " make test-valgrind - build with Valgrind's and test with memcheck tool"
@echo " make test-asan - build with AddressSanitizer and test"
@echo " make test-leak - build with LeakSanitizer and test"
@echo " make test-ubsan - build with UndefinedBehaviourSanitizer and test"
@echo " make cross-gcc - run cross-compilation testset"
@echo " make cross-qemu - run cross-compilation and execution testset with QEMU"
@echo " make check - smoke test with amalgamation and installation checking"
@echo " make long-test - execute long test which runs for several weeks, or until you interrupt it"
@echo " make memcheck - build with Valgrind's and smoke test with memcheck tool"
@echo " make test-valgrind - build with Valgrind's and basic test with memcheck tool"
@echo " make test-asan - build with AddressSanitizer and basic test"
@echo " make test-leak - build with LeakSanitizer and basic test"
@echo " make test-ubsan - build with UndefinedBehaviourSanitizer and basic test"
@echo " make cross-gcc - check cross-compilation without test execution"
@echo " make cross-qemu - run cross-compilation and execution basic test with QEMU"
@echo " make gcc-analyzer - run gcc-analyzer (mostly useless for now)"
@echo " make build-test - build test executable(s)"
@echo " make test-fault - run transaction owner failure testcase"
@echo " make test-singleprocess - run single-process test (used by make cross-qemu)"
@echo " make smoke-fault - execute transaction owner failure smoke testcase"
@echo " make smoke-singleprocess - execute single-process smoke test"
@echo " make test-singleprocess - execute single-process basic test (also used by make cross-qemu)"
@echo ""
@echo " make dist - build amalgamated source code"
@echo " make doxygen - build HTML documentation"
@@ -123,12 +128,14 @@ help:
#< dist-cutoff-end
show-options:
@echo " MDBX_BUILD_OPTIONS =$(MDBX_BUILD_OPTIONS)"
@echo '## TIP: Use `make options` to listing available build options.'
@echo " MDBX_BUILD_OPTIONS = $(MDBX_BUILD_OPTIONS)"
@echo " MDBX_BUILD_TIMESTAMP = $(MDBX_BUILD_TIMESTAMP)"
@echo '$(TIP) Use `make options` to listing available build options.'
@echo " CC =`which $(CC)` | `$(CC) --version | head -1`"
@echo " CFLAGS =$(CFLAGS)"
@echo " CXXFLAGS =$(CXXFLAGS)"
@echo " LDFLAGS =$(LDFLAGS) $(LIBS) $(EXE_LDFLAGS)"
@echo '## TIP: Use `make help` to listing available targets.'
@echo '$(TIP) Use `make help` to listing available targets.'
options:
@echo " INSTALL =$(INSTALL)"
@@ -149,7 +156,8 @@ options:
@echo " EXE_LDFLAGS =$(EXE_LDFLAGS)"
@echo " LIBS =$(LIBS)"
@echo ""
@echo " MDBX_BUILD_OPTIONS =$(MDBX_BUILD_OPTIONS)"
@echo " MDBX_BUILD_OPTIONS = $(MDBX_BUILD_OPTIONS)"
@echo " MDBX_BUILD_TIMESTAMP = $(MDBX_BUILD_TIMESTAMP)"
@echo ""
@echo "## Assortment items for MDBX_BUILD_OPTIONS:"
@echo "## Note that the defaults should already be correct for most platforms;"
@@ -178,7 +186,17 @@ clean:
@echo ' REMOVE ...'
$(QUIET)rm -rf $(TOOLS) mdbx_test @* *.[ao] *.[ls]o *.$(SO_SUFFIX) *.dSYM *~ tmp.db/* \
*.gcov *.log *.err src/*.o test/*.o mdbx_example dist \
config.h src/config.h src/version.c *.tar*
config.h src/config.h src/version.c *.tar* buildflags.tag
MDBX_BUILD_FLAGS =$(strip $(MDBX_BUILD_OPTIONS) $(CXXSTD) $(CFLAGS) $(LDFLAGS) $(LIBS))
check_buildflags_tag:
$(QUIET)if [ "$(MDBX_BUILD_FLAGS)" != "$$(cat buildflags.tag 2>&1)" ]; then \
echo -n " CLEAN for build with specified flags..." && \
$(MAKE) IOARENA=false CXXSTD= -s clean >/dev/null && echo " Ok" && \
echo '$(MDBX_BUILD_FLAGS)' > buildflags.tag; \
fi
buildflags.tag: check_buildflags_tag
libmdbx.a: mdbx-static.o mdbx++-static.o
@echo ' AR $@'
@@ -196,13 +214,13 @@ ifeq ($(wildcard mdbx.c),mdbx.c)
# Amalgamated source code, i.e. distributed after `make dist`
MAN_SRCDIR := man1/
config.h: mdbx.c $(lastword $(MAKEFILE_LIST))
config.h: buildflags.tag mdbx.c $(lastword $(MAKEFILE_LIST))
@echo ' MAKE $@'
$(QUIET)(echo '#define MDBX_BUILD_TIMESTAMP "$(shell date +%Y-%m-%dT%H:%M:%S%z)"' \
&& echo '#define MDBX_BUILD_FLAGS "$(CXXSTD) $(CFLAGS) $(LDFLAGS) $(LIBS)"' \
$(QUIET)(echo '#define MDBX_BUILD_TIMESTAMP "$(MDBX_BUILD_TIMESTAMP)"' \
&& echo "#define MDBX_BUILD_FLAGS \"$$(cat buildflags.tag)\"" \
&& 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 mdbx.h $(lastword $(MAKEFILE_LIST))
@echo ' CC $@'
@@ -229,9 +247,9 @@ else
################################################################################
# Plain (non-amalgamated) sources with test
.PHONY: build-test check cross-gcc cross-qemu dist doxygen gcc-analyzer
.PHONY: reformat release-assets tags test test-asan test-fault test-leak
.PHONY: test-singleprocess test-ubsan test-valgrind memcheck
.PHONY: build-test build-test-with-valgrind check cross-gcc cross-qemu dist doxygen gcc-analyzer long-test
.PHONY: reformat release-assets tags smoke test test-asan smoke-fault test-leak
.PHONY: smoke-singleprocess test-singleprocess test-ubsan test-valgrind memcheck
define uname2osal
case "$(UNAME)" in
@@ -247,7 +265,7 @@ define uname2titer
esac
endef
DIST_EXTRA := LICENSE README.md CMakeLists.txt GNUmakefile Makefile ChangeLog.md VERSION config.h.in ntdll.def \
DIST_EXTRA := LICENSE README.md CMakeLists.txt GNUmakefile Makefile ChangeLog.md VERSION.txt config.h.in ntdll.def \
$(addprefix man1/, $(MANPAGES)) cmake/compiler.cmake cmake/profile.cmake cmake/utils.cmake
DIST_SRC := mdbx.h mdbx.h++ mdbx.c mdbx.c++ $(addsuffix .c, $(TOOLS))
@@ -271,72 +289,90 @@ reformat:
fi
MAN_SRCDIR := src/man1/
ALLOY_DEPS := $(wildcard src/*)
ALLOY_DEPS := $(shell git ls-files src/)
git_DIR := $(shell if [ -d .git ]; then echo .git; elif [ -s .git -a -f .git ]; then grep '^gitdir: ' .git | cut -d ':' -f 2; else echo git_directory_is_absent; fi)
MDBX_GIT_VERSION = $(shell set -o pipefail; git describe --tags 2>&- | sed -n 's|^v*\([0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\)\(.*\)|\1|p' || echo 'Please fetch tags and/or use non-obsolete git version')
MDBX_GIT_REVISION = $(shell set -o pipefail; git rev-list --count HEAD ^`git tag --sort=-version:refname 2>&- | sed -n '/^\(v[0-9]\+\.[0-9]\+\.[0-9]\+\)*/p;q' || echo 'git_tag_with_sort_was_failed'` 2>&- || echo 'Please use non-obsolete git version')
MDBX_GIT_REVISION = $(shell set -o pipefail; git rev-list `git describe --tags --abbrev=0`..HEAD --count 2>&- || echo 'Please fetch tags and/or use non-obsolete git version')
MDBX_GIT_TIMESTAMP = $(shell git show --no-patch --format=%cI HEAD 2>&- || echo 'Please install latest get version')
MDBX_GIT_DESCRIBE = $(shell git describe --tags --long --dirty=-dirty 2>&- || echo 'Please fetch tags and/or install non-obsolete git version')
MDBX_VERSION_SUFFIX = $(shell set -o pipefail; echo -n '$(MDBX_GIT_DESCRIBE)' | tr -c -s '[a-zA-Z0-9]' _)
MDBX_BUILD_SOURCERY = $(shell set -o pipefail; $(MAKE) CXXSTD= -s src/version.c >/dev/null && (openssl dgst -r -sha256 src/version.c || sha256sum src/version.c || shasum -a 256 src/version.c) 2>/dev/null | cut -d ' ' -f 1 || (echo 'Please install openssl or sha256sum or shasum' >&2 && echo sha256sum_is_no_available))_$(MDBX_VERSION_SUFFIX)
MDBX_BUILD_SOURCERY = $(shell set -o pipefail; $(MAKE) IOARENA=false CXXSTD= -s src/version.c >/dev/null && (openssl dgst -r -sha256 src/version.c || sha256sum src/version.c || shasum -a 256 src/version.c) 2>/dev/null | cut -d ' ' -f 1 || (echo 'Please install openssl or sha256sum or shasum' >&2 && echo sha256sum_is_no_available))_$(MDBX_VERSION_SUFFIX)
MDBX_DIST_DIR = libmdbx-$(MDBX_VERSION_SUFFIX)
# Extra options mdbx_test utility
MDBX_TEST_EXTRA ?=
MDBX_SMOKE_EXTRA ?=
check: test dist
check: DESTDIR = $(shell pwd)/@check-install
check: test dist install
test: build-test
@echo ' RUNNING `mdbx_test basic`...'
smoke: build-test
@echo ' SMOKE `mdbx_test basic`...'
$(QUIET)rm -f $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; \
(./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=$(TEST_ITER) --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) basic && \
./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=12 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) basic) \
| tee >(gzip --stdout > $(TEST_LOG).gz) | tail -n 42) \
(./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=$(TEST_ITER) --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic && \
./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=12 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic) \
| tee >(gzip --stdout >$(TEST_LOG).gz) | tail -n 42) \
&& ./mdbx_chk -vvn $(TEST_DB) && ./mdbx_chk -vvn $(TEST_DB)-copy
test-singleprocess: all mdbx_test
@echo ' RUNNING `mdbx_test --nested`...'
smoke-singleprocess: build-test
@echo ' SMOKE `mdbx_test --nested`...'
$(QUIET)rm -f $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; \
(./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) --hill && \
(./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) --hill && \
./mdbx_test --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-before --dont-cleanup-after --copy && \
./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) --nested) \
| tee >(gzip --stdout > $(TEST_LOG).gz) | tail -n 42) \
./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=42 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) --nested) \
| tee >(gzip --stdout >$(TEST_LOG).gz) | tail -n 42) \
&& ./mdbx_chk -vvn $(TEST_DB) && ./mdbx_chk -vvn $(TEST_DB)-copy
test-fault: all mdbx_test
@echo ' RUNNING `mdbx_test --inject-writefault=42 basic`...'
$(QUIET)rm -f $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; ./mdbx_test --progress --console=no --pathname=$(TEST_DB) --inject-writefault=42 --dump-config --dont-cleanup-after $(MDBX_TEST_EXTRA) basic \
| tee >(gzip --stdout > $(TEST_LOG).gz) | tail -n 42) \
smoke-fault: build-test
@echo ' SMOKE `mdbx_test --inject-writefault=42 basic`...'
$(QUIET)rm -f $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; ./mdbx_test --progress --console=no --pathname=$(TEST_DB) --inject-writefault=42 --dump-config --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic \
| tee >(gzip --stdout >$(TEST_LOG).gz) | tail -n 42) \
; ./mdbx_chk -vvnw $(TEST_DB) && ([ ! -e $(TEST_DB)-copy ] || ./mdbx_chk -vvn $(TEST_DB)-copy)
VALGRIND=valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt
memcheck test-valgrind:
@echo " RUNNING \`mdbx_test basic\` under Valgrind's memcheck..."
$(QUIET)$(MAKE) CXXSTD= clean && $(MAKE) CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Ofast -DMDBX_USE_VALGRIND" build-test && \
rm -f valgrind-*.log $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; ( \
$(VALGRIND) ./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) basic && \
test: build-test
@echo ' RUNNING `test/long_stochastic.sh --loops 2`...'
$(QUIET)test/long_stochastic.sh --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG)
long-test: build-test
@echo ' RUNNING `test/long_stochastic.sh --loops 42`...'
$(QUIET)test/long_stochastic.sh --loops 42 --db-upto-mb 1024 --skip-make
test-singleprocess: build-test
@echo ' RUNNING `test/long_stochastic.sh --single --loops 2`...'
$(QUIET)test/long_stochastic.sh --single --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG)
test-valgrind: CFLAGS_EXTRA=-Ofast -DMDBX_USE_VALGRIND
test-valgrind: build-test
@echo ' RUNNING `test/long_stochastic.sh --with-valgrind --loops 2`...'
$(QUIET)test/long_stochastic.sh --with-valgrind --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG)
memcheck: VALGRIND=valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt
memcheck: CFLAGS_EXTRA=-Ofast -DMDBX_USE_VALGRIND
memcheck: build-test
@echo " SMOKE \`mdbx_test basic\` under Valgrind's memcheck..."
$(QUIET)rm -f valgrind-*.log $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; ( \
$(VALGRIND) ./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic && \
$(VALGRIND) ./mdbx_test --progress --console=no --pathname=$(TEST_DB) --dont-cleanup-before --dont-cleanup-after --copy && \
$(VALGRIND) ./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=4 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_TEST_EXTRA) basic && \
$(VALGRIND) ./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=4 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic && \
$(VALGRIND) ./mdbx_chk -vvn $(TEST_DB) && \
$(VALGRIND) ./mdbx_chk -vvn $(TEST_DB)-copy \
) | tee >(gzip --stdout > $(TEST_LOG).gz) | tail -n 42)
) | tee >(gzip --stdout >$(TEST_LOG).gz) | tail -n 42)
gcc-analyzer:
@echo ' RE-BUILD with `-fanalyzer` option...'
@echo "NOTE: There a lot of false-positive warnings at 2020-05-01 by pre-release GCC-10 (20200328, Red Hat 10.0.1-0.11)"
$(QUIET)$(MAKE) CXXSTD=$(CXXSTD) --always-make CFLAGS_EXTRA="-Og -fanalyzer -Wno-error" build-test
$(QUIET)$(MAKE) IOARENA=false CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Og -fanalyzer -Wno-error" build-test
test-ubsan:
@echo ' RE-CHECK with `-fsanitize=undefined` option...'
$(QUIET)$(MAKE) CXXSTD= clean && $(MAKE) CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Ofast -fsanitize=undefined -fsanitize-undefined-trap-on-error" check
@echo ' RE-TEST with `-fsanitize=undefined` option...'
$(QUIET)$(MAKE) IOARENA=false CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Ofast -fsanitize=undefined -fsanitize-undefined-trap-on-error" test
test-asan:
@echo ' RE-CHECK with `-fsanitize=address` option...'
$(QUIET)$(MAKE) CXXSTD= clean && $(MAKE) CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Os -fsanitize=address" check
@echo ' RE-TEST with `-fsanitize=address` option...'
$(QUIET)$(MAKE) IOARENA=false CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-Os -fsanitize=address" test
test-leak:
@echo ' RE-CHECK with `-fsanitize=leak` option...'
$(QUIET)$(MAKE) CXXSTD= clean && $(MAKE) CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-fsanitize=leak" check
@echo ' RE-TEST with `-fsanitize=leak` option...'
$(QUIET)$(MAKE) IOARENA=false CXXSTD=$(CXXSTD) CFLAGS_EXTRA="-fsanitize=leak" test
mdbx_example: mdbx.h example/example-mdbx.c libmdbx.$(SO_SUFFIX)
@echo ' CC+LD $@'
@@ -345,7 +381,7 @@ mdbx_example: mdbx.h example/example-mdbx.c libmdbx.$(SO_SUFFIX)
build-test: all mdbx_example mdbx_test
define test-rule
$(patsubst %.cc,%.o,$(1)): $(1) $(TEST_INC) mdbx.h $(lastword $(MAKEFILE_LIST))
$(patsubst %.cc,%.o,$(1)): $(1) $(TEST_INC) $(HEADERS) $(lastword $(MAKEFILE_LIST))
@echo ' CC $$@'
$(QUIET)$$(CXX) $$(CXXFLAGS) $$(MDBX_BUILD_OPTIONS) -c $(1) -o $$@
@@ -384,16 +420,16 @@ src/version.c: src/version.c.in $(lastword $(MAKEFILE_LIST)) $(git_DIR)/HEAD $(g
-e "s|\$${MDBX_VERSION_MINOR}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 2)|" \
-e "s|\$${MDBX_VERSION_RELEASE}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 3)|" \
-e "s|\$${MDBX_VERSION_REVISION}|$(MDBX_GIT_REVISION)|" \
src/version.c.in > $@
src/version.c.in >$@
src/config.h: src/version.c $(lastword $(MAKEFILE_LIST))
src/config.h: buildflags.tag src/version.c $(lastword $(MAKEFILE_LIST))
@echo ' MAKE $@'
$(QUIET)(echo '#define MDBX_BUILD_TIMESTAMP "$(shell date +%Y-%m-%dT%H:%M:%S%z)"' \
&& echo '#define MDBX_BUILD_FLAGS "$(CXXSTD) $(CFLAGS) $(LDFLAGS) $(LIBS)"' \
$(QUIET)(echo '#define MDBX_BUILD_TIMESTAMP "$(MDBX_BUILD_TIMESTAMP)"' \
&& echo "#define MDBX_BUILD_FLAGS \"$$(cat buildflags.tag)\"" \
&& 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)' \
) > $@
) >$@
mdbx-dylib.o: src/config.h src/version.c src/alloy.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
@echo ' CC $@'
@@ -414,32 +450,32 @@ docs/Doxyfile: docs/Doxyfile.in src/version.c
-e "s|\$${MDBX_VERSION_MINOR}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 2)|" \
-e "s|\$${MDBX_VERSION_RELEASE}|$(shell echo '$(MDBX_GIT_VERSION)' | cut -d . -f 3)|" \
-e "s|\$${MDBX_VERSION_REVISION}|$(MDBX_GIT_REVISION)|" \
docs/Doxyfile.in > $@
docs/Doxyfile.in >$@
define md-extract-section
docs/__$(1).md: $(2)
@echo ' EXTRACT $1'
$(QUIET)sed -n '/<!-- section-begin $(1) -->/,/<!-- section-end -->/p' $$< > $$@ && test -s $$@
$(QUIET)sed -n '/<!-- section-begin $(1) -->/,/<!-- section-end -->/p' $$< >$$@ && test -s $$@
endef
$(foreach section,overview mithril characteristics improvements history usage performance bindings,$(eval $(call md-extract-section,$(section),README.md)))
docs/overall.md: docs/__overview.md docs/_toc.md docs/__mithril.md docs/__history.md AUTHORS LICENSE
@echo ' MAKE $@'
$(QUIET)echo -e "\\mainpage Overall\n\\section brief Brief" | cat - $(filter %.md, $^) > $@ && echo -e "\n\n\nLicense\n=======\n" | cat AUTHORS - LICENSE >> $@
$(QUIET)echo -e "\\mainpage Overall\n\\section brief Brief" | cat - $(filter %.md, $^) >$@ && echo -e "\n\n\nLicense\n=======\n" | cat AUTHORS - LICENSE >>$@
docs/intro.md: docs/_preface.md docs/__characteristics.md docs/__improvements.md docs/_restrictions.md docs/__performance.md
@echo ' MAKE $@'
$(QUIET)cat $^ | sed 's/^Performance comparison$$/Performance comparison {#performance}/' > $@
$(QUIET)cat $^ | sed 's/^Performance comparison$$/Performance comparison {#performance}/' >$@
docs/usage.md: docs/__usage.md docs/_starting.md docs/__bindings.md
@echo ' MAKE $@'
$(QUIET)echo -e "\\page usage Usage\n\\section getting Building & Embedding" | cat - $^ | sed 's/^Bindings$$/Bindings {#bindings}/' > $@
$(QUIET)echo -e "\\page usage Usage\n\\section getting Building & Embedding" | cat - $^ | sed 's/^Bindings$$/Bindings {#bindings}/' >$@
doxygen: docs/Doxyfile docs/overall.md docs/intro.md docs/usage.md mdbx.h mdbx.h++ src/options.h ChangeLog.md AUTHORS LICENSE
@echo ' RUNNING doxygen...'
$(QUIET)rm -rf docs/html && \
cat mdbx.h | tr '\n' '\r' | sed -e 's/LIBMDBX_INLINE_API\s*(\s*\([^,]\+\),\s*\([^,]\+\),\s*(\s*\([^)]\+\)\s*)\s*)\s*{/inline \1 \2(\3) {/g' | tr '\r' '\n' > docs/mdbx.h && \
cat mdbx.h | tr '\n' '\r' | sed -e 's/LIBMDBX_INLINE_API\s*(\s*\([^,]\+\),\s*\([^,]\+\),\s*(\s*\([^)]\+\)\s*)\s*)\s*{/inline \1 \2(\3) {/g' | tr '\r' '\n' >docs/mdbx.h && \
cp mdbx.h++ src/options.h ChangeLog.md docs/ && (cd docs && doxygen Doxyfile $(HUSH)) && cp AUTHORS LICENSE docs/html/
mdbx++-dylib.o: src/config.h src/mdbx.c++ mdbx.h mdbx.h++ $(lastword $(MAKEFILE_LIST))
@@ -462,15 +498,14 @@ release-assets: libmdbx-sources-$(MDBX_VERSION_SUFFIX).tar.gz libmdbx-sources-$(
dist-checked.tag: $(addprefix dist/, $(DIST_SRC) $(DIST_EXTRA))
@echo -n ' VERIFY amalgamated sources...'
$(QUIET)rm -rf $@ \
$(QUIET)rm -rf $@ dist/@tmp-shared_internals.inc \
&& if grep -R "define xMDBX_ALLOY" dist | grep -q MDBX_BUILD_SOURCERY; then echo "sed output is WRONG!" >&2; exit 2; fi \
&& rm -rf dist-check && cp -r -p dist dist-check && ($(MAKE) -C dist-check > dist-check/build.log 2> dist-check/build.err || (cat dist-check/build.err && exit 1)) \
&& touch $@ || (echo " FAILED! See dist-check/build.err" >&2; exit 2) && echo " Ok" \
&& rm dist/@tmp-shared_internals.inc
&& rm -rf dist-check && cp -r -p dist dist-check && ($(MAKE) IOARENA=false CXXSTD=$(CXXSTD) -C dist-check >dist-check.log 2>dist-check.err || (cat dist-check.err && exit 1)) \
&& touch $@ || (echo " FAILED! See dist-check.log and dist-check.err" >&2; exit 2) && echo " Ok"
libmdbx-sources-$(MDBX_VERSION_SUFFIX).tar.gz: dist-checked.tag
@echo ' CREATE $@'
$(QUIET)$(TAR) -c $(shell LC_ALL=C $(TAR) --help | grep -q -- '--owner' && echo '--owner=0 --group=0') -f - -C dist $(DIST_SRC) $(DIST_EXTRA) | gzip -c -9 > $@
$(QUIET)$(TAR) -c $(shell LC_ALL=C $(TAR) --help | grep -q -- '--owner' && echo '--owner=0 --group=0') -f - -C dist $(DIST_SRC) $(DIST_EXTRA) | gzip -c -9 >$@
libmdbx-sources-$(MDBX_VERSION_SUFFIX).zip: dist-checked.tag
@echo ' CREATE $@'
@@ -487,26 +522,26 @@ dist/mdbx.h++: mdbx.h++ src/version.c $(lastword $(MAKEFILE_LIST))
dist/@tmp-shared_internals.inc: src/version.c $(ALLOY_DEPS) $(lastword $(MAKEFILE_LIST))
@echo ' ALLOYING...'
$(QUIET)mkdir -p dist \
&& echo '#define xMDBX_ALLOY 1' > dist/@tmp-sed.inc && echo '#define MDBX_BUILD_SOURCERY $(MDBX_BUILD_SOURCERY)' >> dist/@tmp-sed.inc \
&& echo '#define xMDBX_ALLOY 1' >dist/@tmp-sed.inc && echo '#define MDBX_BUILD_SOURCERY $(MDBX_BUILD_SOURCERY)' >>dist/@tmp-sed.inc \
&& sed \
-e '/#pragma once/r dist/@tmp-sed.inc' \
-e 's|#include "../mdbx.h"|@INCLUDE "mdbx.h"|' \
-e '/#include "defs.h"/r src/defs.h' \
-e '/#include "osal.h"/r src/osal.h' \
-e '/#include "options.h"/r src/options.h' \
src/internals.h > $@ \
src/internals.h >$@ \
&& rm -rf dist/@tmp-sed.inc
dist/mdbx.c: dist/@tmp-shared_internals.inc $(lastword $(MAKEFILE_LIST))
@echo ' MAKE $@'
$(QUIET)mkdir -p dist && (cat dist/@tmp-shared_internals.inc \
&& cat src/core.c src/osal.c src/version.c src/lck-windows.c src/lck-posix.c \
) | grep -v -e '#include "' -e '#pragma once' | sed 's|@INCLUDE|#include|' > $@
) | grep -v -e '#include "' -e '#pragma once' | sed 's|@INCLUDE|#include|' >$@
dist/mdbx.c++: dist/@tmp-shared_internals.inc src/mdbx.c++ $(lastword $(MAKEFILE_LIST))
@echo ' MAKE $@'
$(QUIET)mkdir -p dist && (cat dist/@tmp-shared_internals.inc && cat src/mdbx.c++) \
| grep -v -e '#include "' -e '#pragma once' | sed 's|@INCLUDE|#include|;s|"mdbx.h"|"mdbx.h++"|' > $@
| grep -v -e '#include "' -e '#pragma once' | sed 's|@INCLUDE|#include|;s|"mdbx.h"|"mdbx.h++"|' >$@
define dist-tool-rule
dist/$(1).c: src/$(1).c src/wingetopt.h src/wingetopt.c \
@@ -517,7 +552,7 @@ dist/$(1).c: src/$(1).c src/wingetopt.h src/wingetopt.c \
-e '/#include "wingetopt.h"/r src/wingetopt.c' \
src/$(1).c \
| grep -v -e '#include "' -e '#pragma once' -e '#define xMDBX_ALLOY' \
| sed 's|@INCLUDE|#include|' > $$@
| sed 's|@INCLUDE|#include|' >$$@
endef
$(foreach file,$(TOOLS),$(eval $(call dist-tool-rule,$(file))))
@@ -525,14 +560,14 @@ $(foreach file,$(TOOLS),$(eval $(call dist-tool-rule,$(file))))
define dist-extra-rule
dist/$(1): $(1)
@echo ' REFINE $$@'
$(QUIET)mkdir -p $$(dir $$@) && sed -e '/^#> dist-cutoff-begin/,/^#< dist-cutoff-end/d' $$< > $$@
$(QUIET)mkdir -p $$(dir $$@) && sed -e '/^#> dist-cutoff-begin/,/^#< dist-cutoff-end/d' $$< >$$@
endef
$(foreach file,$(filter-out man1/% VERSION %.in ntdll.def,$(DIST_EXTRA)),$(eval $(call dist-extra-rule,$(file))))
$(foreach file,$(filter-out man1/% VERSION.txt %.in ntdll.def,$(DIST_EXTRA)),$(eval $(call dist-extra-rule,$(file))))
dist/VERSION: src/version.c
dist/VERSION.txt: src/version.c
@echo ' MAKE $@'
$(QUIET)mkdir -p dist/ && echo "$(MDBX_GIT_VERSION).$(MDBX_GIT_REVISION)" > $@
$(QUIET)mkdir -p dist/ && echo "$(MDBX_GIT_VERSION).$(MDBX_GIT_REVISION)" >$@
dist/ntdll.def: src/ntdll.def
@echo ' COPY $@'
@@ -551,7 +586,7 @@ endif
################################################################################
# Cross-compilation simple test
CROSS_LIST = sparc64-linux-gnu-gcc alpha-linux-gnu-gcc mips-linux-gnu-gcc \
CROSS_LIST = mips-linux-gnu-gcc \
powerpc64-linux-gnu-gcc powerpc-linux-gnu-gcc \
arm-linux-gnueabihf-gcc aarch64-linux-gnu-gcc \
sh4-linux-gnu-gcc mips64-linux-gnuabi64-gcc \
@@ -562,7 +597,7 @@ CROSS_LIST = sparc64-linux-gnu-gcc alpha-linux-gnu-gcc mips-linux-gnu-gcc \
# s390x-linux-gnu-gcc - works (previously: qemu hang/abort)
# sparc64-linux-gnu-gcc - coredump (qemu mmap-troubles, previously: qemu fails fcntl for F_SETLK/F_GETLK)
# alpha-linux-gnu-gcc - coredump (qemu mmap-troubles)
CROSS_LIST_NOQEMU =
CROSS_LIST_NOQEMU = sparc64-linux-gnu-gcc alpha-linux-gnu-gcc
cross-gcc:
@echo ' Re-building by cross-compiler for: $(CROSS_LIST_NOQEMU) $(CROSS_LIST)'
@@ -570,7 +605,7 @@ cross-gcc:
@echo "FOR INSTANCE: apt install g++-aarch64-linux-gnu g++-alpha-linux-gnu g++-arm-linux-gnueabihf g++-hppa-linux-gnu g++-mips-linux-gnu g++-mips64-linux-gnuabi64 g++-powerpc-linux-gnu g++-powerpc64-linux-gnu g++-s390x-linux-gnu g++-sh4-linux-gnu g++-sparc64-linux-gnu"
$(QUIET)for CC in $(CROSS_LIST_NOQEMU) $(CROSS_LIST); do \
echo "===================== $$CC"; \
$(MAKE) CXXSTD= clean && CC=$$CC CXX=$$(echo $$CC | sed 's/-gcc/-g++/') EXE_LDFLAGS=-static $(MAKE) all || exit $$?; \
$(MAKE) IOARENA=false CXXSTD= clean && CC=$$CC CXX=$$(echo $$CC | sed 's/-gcc/-g++/') EXE_LDFLAGS=-static $(MAKE) IOARENA=false all || exit $$?; \
done
# Unfortunately qemu don't provide robust support for futexes.
@@ -583,21 +618,25 @@ cross-qemu:
@echo " 2) apt install binfmt-support qemu-user-static qemu-user qemu-system-arm qemu-system-mips qemu-system-misc qemu-system-ppc qemu-system-sparc"
$(QUIET)for CC in $(CROSS_LIST); do \
echo "===================== $$CC + qemu"; \
$(MAKE) CXXSTD= clean && \
MDBX_TEST_EXTRA="$(MDBX_TEST_EXTRA)$$(echo $$CC | grep -q -e alpha -e sparc && echo ' --size-upper=64M --size-lower=64M')" \
$(MAKE) IOARENA=false CXXSTD= clean && \
CC=$$CC CXX=$$(echo $$CC | sed 's/-gcc/-g++/') EXE_LDFLAGS=-static MDBX_BUILD_OPTIONS="-DMDBX_SAFE4QEMU $(MDBX_BUILD_OPTIONS)" \
$(MAKE) test-singleprocess || exit $$?; \
$(MAKE) IOARENA=false smoke-singleprocess test-singleprocess || exit $$?; \
done
#< dist-cutoff-end
install: $(LIBRARIES) $(TOOLS) $(HEADERS)
@echo ' INSTALLING...'
$(INSTALL) -D -p $(EXE_INSTALL_FLAGS) -t $(DESTDIR)$(prefix)/bin$(suffix) $(TOOLS) && \
$(INSTALL) -D -p $(EXE_INSTALL_FLAGS) -t $(DESTDIR)$(prefix)/lib$(suffix) $(filter-out libmdbx.a,$(LIBRARIES)) && \
$(INSTALL) -D -p -t $(DESTDIR)$(prefix)/lib$(suffix) libmdbx.a && \
$(INSTALL) -D -p -m 444 -t $(DESTDIR)$(prefix)/include $(HEADERS) && \
$(INSTALL) -D -p -m 444 -t $(DESTDIR)$(mandir)/man1 $(addprefix $(MAN_SRCDIR), $(MANPAGES))
$(QUIET)mkdir -p $(DESTDIR)$(prefix)/bin$(suffix) && \
$(INSTALL) -p $(EXE_INSTALL_FLAGS) $(TOOLS) $(DESTDIR)$(prefix)/bin$(suffix)/ && \
mkdir -p $(DESTDIR)$(prefix)/lib$(suffix)/ && \
$(INSTALL) -p $(EXE_INSTALL_FLAGS) $(filter-out libmdbx.a,$(LIBRARIES)) $(DESTDIR)$(prefix)/lib$(suffix)/ && \
mkdir -p $(DESTDIR)$(prefix)/lib$(suffix)/ && \
$(INSTALL) -p libmdbx.a $(DESTDIR)$(prefix)/lib$(suffix)/ && \
mkdir -p $(DESTDIR)$(prefix)/include/ && \
$(INSTALL) -p -m 444 $(HEADERS) $(DESTDIR)$(prefix)/include/ && \
mkdir -p $(DESTDIR)$(mandir)/man1/ && \
$(INSTALL) -p -m 444 $(addprefix $(MAN_SRCDIR), $(MANPAGES)) $(DESTDIR)$(mandir)/man1/
install-strip: EXE_INSTALL_FLAGS = -s
install-strip: install
@@ -615,24 +654,32 @@ uninstall:
################################################################################
# Benchmarking by ioarena
IOARENA ?= $(shell \
ifeq ($(origin IOARENA),undefined)
IOARENA := $(shell \
(test -x ../ioarena/@BUILD/src/ioarena && echo ../ioarena/@BUILD/src/ioarena) || \
(test -x ../../@BUILD/src/ioarena && echo ../../@BUILD/src/ioarena) || \
(test -x ../../src/ioarena && echo ../../src/ioarena) || which ioarena 2>&- || \
echo '\#\# TIP: Clone and build the https://github.com/pmwkaa/ioarena.git within a neighbouring directory for availability of benchmarking.' >&2)
(echo false && echo '$(TIP) Clone and build the https://github.com/pmwkaa/ioarena.git within a neighbouring directory for availability of benchmarking.' >&2))
endif
NN ?= 25000000
BENCH_CRUD_MODE ?= nosync
ifneq ($(wildcard $(IOARENA)),)
.PHONY: bench bench-clean bench-couple re-bench bench-quartet bench-triplet
bench-clean:
@echo ' REMOVE bench-*.txt _ioarena/*'
$(QUIET)rm -rf bench-*.txt _ioarena/*
re-bench: bench-clean bench
ifeq ($(or $(IOARENA),false),false)
bench bench-quartet bench-triplet bench-couple:
$(QUIET)echo 'The `ioarena` benchmark is required.' >&2 && \
echo 'Please clone and build the https://github.com/pmwkaa/ioarena.git within a neighbouring `ioarena` directory.' >&2 && \
false
else
.PHONY: bench bench-clean bench-couple re-bench bench-quartet bench-triplet
define bench-rule
bench-$(1)_$(2).txt: $(3) $(IOARENA) $(lastword $(MAKEFILE_LIST))
@echo ' RUNNING ioarena for $1/$2...'

View File

@@ -5,8 +5,8 @@ clean install install-no-strip install-strip strip tools uninstall \
bench bench-clean bench-couple bench-quartet bench-triplet re-bench \
lib libmdbx mdbx mdbx_chk mdbx_copy mdbx_drop mdbx_dump mdbx_load mdbx_stat \
check dist memcheck cross-gcc cross-qemu doxygen gcc-analyzer reformat \
release-assets tags test build-test mdbx_test \
test-asan test-fault test-leak test-singleprocess test-ubsan test-valgrind:
release-assets tags test build-test mdbx_test smoke smoke-fault smoke-singleprocess \
test-asan test-leak test-singleprocess test-ubsan test-valgrind:
@CC=$(CC) \
CXX=`if test -n "$(CXX)" && which "$(CXX)" > /dev/null; then echo "$(CXX)"; elif test -n "$(CCC)" && which "$(CCC)" > /dev/null; then echo "$(CCC)"; else echo "c++"; fi` \
`which gmake || which gnumake || echo 'echo "GNU Make 3.80 or above is required"; exit 2;'` \

View File

@@ -91,7 +91,7 @@ _MithrilDB_ is a rightly relevant name.
- [Improvements beyond LMDB](#improvements-beyond-lmdb)
- [History & Acknowledgments](#history)
- [Usage](#usage)
- [Building](#building)
- [Building and Testing](#building-and-testing)
- [API description](#api-description)
- [Bindings](#bindings)
- [Performance comparison](#performance-comparison)
@@ -167,9 +167,11 @@ transaction journal. No crash recovery needed. No maintenance is required.
2. _libmdbx_ 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. _libmdbx_ 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.
3. _libmdbx_ 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. _libmdbx_ 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
4. _libmdbx_ 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,
the database can grow quickly, and result in performance degradation.
Try to avoid long running read transactions.
@@ -369,7 +371,7 @@ 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.
## Building
## Building and Testing
Both amalgamated and original source code provides build through the use
[CMake](https://cmake.org/) or [GNU
@@ -383,10 +385,76 @@ target platform. Obviously you need building tools itself, i.e. `git`,
and `make options` are also available for listing existing targets
and build options respectively.
The only significant specificity is that git' tags are required
to build from complete (not amalgamated) source codes.
Executing **`git fetch --tags --force --prune`** is enough to get ones,
or `git fetch --unshallow --tags --prune --force` after the Github's
[`actions/checkout@v2`](https://github.com/actions/checkout) either set **`fetch-depth: 0`** for it.
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.
### Testing
The amalgamated source code does not contain any tests for or several reasons.
Please read [the explanation](https://github.com/erthink/libmdbx/issues/214#issuecomment-870717981) and don't ask to alter this.
So for testing _libmdbx_ itself you need a full source code, i.e. the clone of a git repository, there is no option.
The full source code of _libmdbx_ has a [`test` subdirectory](https://github.com/erthink/libmdbx/tree/master/test) with minimalistic test "framework".
Actually yonder is a source code of the `mdbx_test` console utility which has a set of command-line options that allow construct and run a reasonable enough test scenarios.
This test utility is intended for _libmdbx_'s developers for testing library itself, but not for use by users.
Therefore, only basic information is provided:
- There are few CRUD-based test cases (hill, TTL, nested, append, jitter, etc),
which can be combined to test the concurrent operations within shared database in a multi-processes environment.
This is the `basic` test scenario.
- The `Makefile` provide several self-described targets for testing: `smoke`, `test`, `check`, `memcheck`, `test-valgrind`,
`test-asan`, `test-leak`, `test-ubsan`, `cross-gcc`, `cross-qemu`, `gcc-analyzer`, `smoke-fault`, `smoke-singleprocess`,
`test-singleprocess`, 'long-test'. Please run `make --help` if doubt.
- In addition to the `mdbx_test` utility, there is the script [`long_stochastic.sh`](https://github.com/erthink/libmdbx/blob/master/test/long_stochastic.sh),
which calls `mdbx_test` by going through set of modes and options, with gradually increasing the number of operations and the size of transactions.
This script is used for mostly of all automatic testing, including `Makefile` targets and Continuous Integration.
- Brief information of available command-line options is available by `--help`.
However, you should dive into source code to get all, there is no option.
Anyway, no matter how thoroughly the _libmdbx_ is tested, you should rely only on your own tests for a few reasons:
1. Mostly of all use cases are unique.
So it is no warranty that your use case was properly tested, even the _libmdbx_'s tests engages stochastic approach.
2. If there are problems, then your test on the one hand will help to verify whether you are using _libmdbx_ correctly,
on the other hand it will allow to reproduce the problem and insure against regression in a future.
3. Actually you should rely on than you checked by yourself or take a risk.
### Common important details
#### Build reproducibility
By default _libmdbx_ track build time via `MDBX_BUILD_TIMESTAMP` build option and macro.
So for a [reproducible builds](https://en.wikipedia.org/wiki/Reproducible_builds) you should predefine/override it to known fixed string value.
For instance:
- for reproducible build with make: `make MDBX_BUILD_TIMESTAMP=unknown ` ...
- or during configure by CMake: `cmake -DMDBX_BUILD_TIMESTAMP:STRING=unknown ` ...
Of course, in addition to this, your toolchain must ensure the reproducibility of builds.
For more information please refer to [reproducible-builds.org](https://reproducible-builds.org/).
#### Containers
There are no special traits nor quirks if you use libmdbx ONLY inside the single container.
But in a cross-container cases or with a host-container(s) mix the two major things MUST be
guaranteed:
1. Coherence of memory mapping content and unified page cache inside OS kernel for host and all container(s) operated with a some DB.
Basically this means must be only a single physical copy of each memory mapped DB' page in the system memory.
2. Uniqueness of PID values and/or a common space for ones:
- for POSIX systems: PID uniqueness for all processes operated with a DB.
I.e. the `--pid=host` is required for run DB-aware processes inside Docker,
either without host interaction a `--pid=container:<name|id>` with the same name/id.
- for non-POSIX (i.e. Windows) systems: inter-visibility of processes handles.
I.e. the `OpenProcess(SYNCHRONIZE, ..., PID)` must return reasonable error,
including `ERROR_ACCESS_DENIED`,
but not the `ERROR_INVALID_PARAMETER` as for an invalid/non-existent PID.
#### DSO/DLL unloading and destructors of Thread-Local-Storage objects
When building _libmdbx_ as a shared library or use static _libmdbx_ as a
part of another dynamic library, it is advisable to make sure that your
@@ -479,7 +547,7 @@ Please refer to the [official guide](https://developer.android.com/studio/projec
### iOS
To build _libmdbx_ for iOS, we recommend using CMake with the
"[toolchain file](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html)"
["toolchain file"](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html)
from the [ios-cmake](https://github.com/leetal/ios-cmake) project.
<!-- section-end -->
@@ -496,6 +564,10 @@ Bindings
| Runtime | Repo | Author |
| ------- | ------ | ------ |
| Haskell | [libmdbx-hs](https://hackage.haskell.org/package/libmdbx) | [Francisco Vallarino](https://github.com/fjvallarino) |
| Python (draft) | [python-bindings](https://github.com/erthink/libmdbx/commits/python-bindings) branch | [Noel Kuntze](https://github.com/Thermi)
| NodeJS | [lmdbx-store](https://github.com/kriszyp/lmdbx-store) | [Kris Zyp](https://github.com/kriszyp/)
| NodeJS | [node-mdbx](https://www.npmjs.com/package/node-mdbx/) | [Сергей Федотов](mailto:sergey.fedotov@corp.mail.ru) |
| Ruby | [ruby-mdbx](https://rubygems.org/gems/mdbx/) | [Mahlon E. Smith](https://github.com/mahlonsmith) |
| Go | [mdbx-go](https://github.com/torquem-ch/mdbx-go) | [Alex Sharov](https://github.com/AskAlexSharov) |
| [Nim](https://en.wikipedia.org/wiki/Nim_(programming_language)) | [NimDBX](https://github.com/snej/nimdbx) | [Jens Alfke](https://github.com/snej)
@@ -610,7 +682,9 @@ 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 is consistent and conforms to the one of last successful 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.

View File

@@ -1,4 +1,4 @@
version: 0.10.1.{build}
version: 0.10.3.{build}
environment:
matrix:
@@ -52,8 +52,8 @@ matrix:
configuration: Release
# Enable RDP for troubleshooting
init:
- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
#init:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
before_build:
- git clean -x -f -d

View File

@@ -13,9 +13,14 @@
## limitations under the License.
##
cmake_minimum_required(VERSION 3.8.2)
if(CMAKE_VERSION VERSION_LESS 3.12)
cmake_minimum_required(VERSION 3.8.2)
else()
cmake_minimum_required(VERSION 3.12)
endif()
cmake_policy(PUSH)
cmake_policy(VERSION 3.8.2)
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
if(CMAKE_VERSION MATCHES ".*MSVC.*" AND CMAKE_VERSION VERSION_LESS 3.16)
message(FATAL_ERROR "CMake from MSVC kit is unfit! "
@@ -286,21 +291,21 @@ if(CMAKE_COMPILER_IS_GNU${CMAKE_PRIMARY_LANG})
set(gcc_suffix "")
if(gcc_collect_valid AND gcc_collect)
string(REGEX MATCH "^(.*cc)(-.+)$" gcc_suffix_valid ${gcc_collect})
string(REGEX MATCH "^(.*(cc|\\+\\+))(-.+)$" gcc_suffix_valid ${gcc_collect})
if(gcc_suffix_valid)
string(REGEX MATCH "^(.*cc)(-.+)$" "\\2" gcc_suffix ${gcc_collect})
string(REGEX REPLACE "^(.*(cc|\\+\\+))(-.+)$" "\\3" gcc_suffix ${gcc_collect})
endif()
endif()
get_filename_component(gcc_dir ${CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER} DIRECTORY)
if(NOT CMAKE_GCC_AR)
find_program(CMAKE_GCC_AR NAMES gcc${gcc_suffix}-ar gcc-ar${gcc_suffix} PATHS ${gcc_dir} NO_DEFAULT_PATH)
find_program(CMAKE_GCC_AR NAMES "gcc${gcc_suffix}-ar" "gcc-ar${gcc_suffix}" PATHS "${gcc_dir}" NO_DEFAULT_PATH)
endif()
if(NOT CMAKE_GCC_NM)
find_program(CMAKE_GCC_NM NAMES gcc${gcc_suffix}-nm gcc-nm${gcc_suffix} PATHS ${gcc_dir} NO_DEFAULT_PATH)
find_program(CMAKE_GCC_NM NAMES "gcc${gcc_suffix}-nm" "gcc-nm${gcc_suffix}" PATHS "${gcc_dir}" NO_DEFAULT_PATH)
endif()
if(NOT CMAKE_GCC_RANLIB)
find_program(CMAKE_GCC_RANLIB NAMES gcc${gcc_suffix}-ranlib gcc-ranlib${gcc_suffix} PATHS ${gcc_dir} NO_DEFAULT_PATH)
find_program(CMAKE_GCC_RANLIB NAMES "gcc${gcc_suffix}-ranlib" "gcc-ranlib${gcc_suffix}" PATHS "${gcc_dir}" NO_DEFAULT_PATH)
endif()
unset(gcc_dir)
@@ -342,30 +347,40 @@ if(CMAKE_COMPILER_IS_CLANG)
OUTPUT_VARIABLE clang_search_dirs RESULT_VARIABLE clang_probe_result ERROR_QUIET)
unset(clang_bindirs)
unset(clang_bindirs_x)
unset(clang_libdirs)
unset(clang_libdirs_x)
if(clang_probe_result EQUAL 0)
string(REGEX MATCH "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" regexp_valid ${clang_search_dirs})
if(regexp_valid)
string(REGEX REPLACE "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
string(REPLACE ":" ";" list "${list}")
#set(clang_bindirs "")
foreach(dir IN LISTS list)
get_filename_component(dir "${dir}" REALPATH)
string(REGEX MATCH "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" regexp_valid ${clang_search_dirs})
if(regexp_valid)
string(REGEX REPLACE "(^|\n.*)(.*programs: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
string(REPLACE ":" ";" list "${list}")
foreach(dir IN LISTS list)
get_filename_component(dir "${dir}" REALPATH)
if(dir MATCHES ".*llvm.*" OR dir MATCHES ".*clang.*")
list(APPEND clang_bindirs "${dir}")
endforeach()
list(REMOVE_DUPLICATES clang_bindirs)
endif()
string(REGEX MATCH "(^|\n.*)(.*libraries: =)([^\n]+)((\n.*)|$)" regexp_valid ${clang_search_dirs})
if(regexp_valid)
string(REGEX REPLACE "(^|\n.*)(.*libraries: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
string(REPLACE ":" ";" list "${list}")
#set(clang_libdirs "")
foreach(dir IN LISTS list)
get_filename_component(dir "${dir}" REALPATH)
else()
list(APPEND clang_bindirs_x "${dir}")
endif()
endforeach()
list(APPEND clang_bindirs "${clang_bindirs_x}")
list(REMOVE_DUPLICATES clang_bindirs)
endif()
string(REGEX MATCH "(^|\n.*)(.*libraries: =)([^\n]+)((\n.*)|$)" regexp_valid ${clang_search_dirs})
if(regexp_valid)
string(REGEX REPLACE "(^|\n.*)(.*libraries: =)([^\n]+)((\n.*)|$)" "\\3" list ${clang_search_dirs})
string(REPLACE ":" ";" list "${list}")
foreach(dir IN LISTS list)
get_filename_component(dir "${dir}" REALPATH)
if(dir MATCHES ".*llvm.*" OR dir MATCHES ".*clang.*")
list(APPEND clang_libdirs "${dir}")
endforeach()
list(REMOVE_DUPLICATES clang_libdirs)
endif()
else()
list(APPEND clang_libdirs_x "${dir}")
endif()
endforeach()
list(APPEND clang_libdirs "${clang_libdirs_x}")
list(REMOVE_DUPLICATES clang_libdirs)
endif()
else()
get_filename_component(clang_bindirs ${CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER} DIRECTORY)
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")

View File

@@ -13,9 +13,14 @@
## limitations under the License.
##
cmake_minimum_required(VERSION 3.8.2)
if(CMAKE_VERSION VERSION_LESS 3.12)
cmake_minimum_required(VERSION 3.8.2)
else()
cmake_minimum_required(VERSION 3.12)
endif()
cmake_policy(PUSH)
cmake_policy(VERSION 3.8.2)
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
include(CheckLibraryExists)
check_library_exists(gcov __gcov_flush "" HAVE_GCOV)

View File

@@ -13,9 +13,14 @@
## limitations under the License.
##
cmake_minimum_required(VERSION 3.8.2)
if(CMAKE_VERSION VERSION_LESS 3.12)
cmake_minimum_required(VERSION 3.8.2)
else()
cmake_minimum_required(VERSION 3.12)
endif()
cmake_policy(PUSH)
cmake_policy(VERSION 3.8.2)
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
macro(add_compile_flags languages)
foreach(_lang ${languages})
@@ -173,7 +178,10 @@ macro(fetch_version name source_root_directory parent_scope)
set(${name}_GIT_REVISION 0)
# Try to get version from VERSION file
set(version_file "${source_root_directory}/VERSION")
set(version_file "${source_root_directory}/VERSION.txt")
if(NOT EXISTS "${version_file}")
set(version_file "${source_root_directory}/VERSION")
endif()
if(EXISTS "${version_file}")
file(STRINGS "${version_file}" ${name}_VERSION LIMIT_COUNT 1 LIMIT_INPUT 42)
endif()

102
mdbx.h
View File

@@ -361,6 +361,12 @@ typedef mode_t mdbx_mode_t;
#define LIBMDBX_INLINE_API(TYPE, NAME, ARGS) static __inline TYPE NAME ARGS
#endif /* LIBMDBX_INLINE_API */
/** \brief Converts a macro argument into a string constant. */
#ifndef MDBX_STRINGIFY
#define MDBX_STRINGIFY_HELPER(x) #x
#define MDBX_STRINGIFY(x) MDBX_STRINGIFY_HELPER(x)
#endif /* MDBX_STRINGIFY */
/*----------------------------------------------------------------------------*/
#ifndef __cplusplus
@@ -454,8 +460,12 @@ typedef mode_t mdbx_mode_t;
#endif
#endif /* MDBX_PRINTF_ARGS */
#if defined(DOXYGEN) || (__has_cpp_attribute(maybe_unused) && \
(defined(__cplusplus) || __STDC_VERSION__ > 202005L))
#if defined(DOXYGEN) || \
(defined(__cplusplus) && __cplusplus >= 201603 && \
__has_cpp_attribute(maybe_unused) && \
__has_cpp_attribute(maybe_unused) >= 201603) || \
(!defined(__cplusplus) && defined(__STDC_VERSION__) && \
__STDC_VERSION__ > 202005L)
#define MDBX_MAYBE_UNUSED [[maybe_unused]]
#elif defined(__GNUC__) || __has_attribute(__unused__)
#define MDBX_MAYBE_UNUSED __attribute__((__unused__))
@@ -463,6 +473,12 @@ typedef mode_t mdbx_mode_t;
#define MDBX_MAYBE_UNUSED
#endif /* MDBX_MAYBE_UNUSED */
#if __has_attribute(no_sanitize)
#define MDBX_NOSANITIZE_ENUM __attribute((__no_sanitize__("enum")))
#else
#define MDBX_NOSANITIZE_ENUM
#endif /* MDBX_NOSANITIZE_ENUM */
/* Oh, below are some songs and dances since:
* - C++ requires explicit definition of the necessary operators.
* - the proper implementation of DEFINE_ENUM_FLAG_OPERATORS for C++ required
@@ -488,28 +504,40 @@ typedef mode_t mdbx_mode_t;
/// used to define flags (based on Microsoft's DEFINE_ENUM_FLAG_OPERATORS).
#define DEFINE_ENUM_FLAG_OPERATORS(ENUM) \
extern "C++" { \
MDBX_CXX01_CONSTEXPR ENUM operator|(ENUM a, ENUM b) { \
MDBX_NOSANITIZE_ENUM MDBX_CXX01_CONSTEXPR ENUM operator|(ENUM a, ENUM b) { \
return ENUM(unsigned(a) | unsigned(b)); \
} \
MDBX_CXX14_CONSTEXPR ENUM &operator|=(ENUM &a, ENUM b) { return a = a | b; } \
MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, ENUM b) { \
MDBX_NOSANITIZE_ENUM MDBX_CXX14_CONSTEXPR ENUM &operator|=(ENUM &a, \
ENUM b) { \
return a = a | b; \
} \
MDBX_NOSANITIZE_ENUM MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, ENUM b) { \
return ENUM(unsigned(a) & unsigned(b)); \
} \
MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, unsigned b) { \
MDBX_NOSANITIZE_ENUM MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, \
unsigned b) { \
return ENUM(unsigned(a) & b); \
} \
MDBX_CXX01_CONSTEXPR ENUM operator&(unsigned a, ENUM b) { \
MDBX_NOSANITIZE_ENUM MDBX_CXX01_CONSTEXPR ENUM operator&(unsigned a, \
ENUM b) { \
return ENUM(a & unsigned(b)); \
} \
MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, ENUM b) { return a = a & b; } \
MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, unsigned b) { \
MDBX_NOSANITIZE_ENUM MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, \
ENUM b) { \
return a = a & b; \
} \
MDBX_NOSANITIZE_ENUM MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, \
unsigned b) { \
return a = a & b; \
} \
MDBX_CXX01_CONSTEXPR unsigned operator~(ENUM a) { return ~unsigned(a); } \
MDBX_CXX01_CONSTEXPR ENUM operator^(ENUM a, ENUM b) { \
MDBX_NOSANITIZE_ENUM MDBX_CXX01_CONSTEXPR ENUM operator^(ENUM a, ENUM b) { \
return ENUM(unsigned(a) ^ unsigned(b)); \
} \
MDBX_CXX14_CONSTEXPR ENUM &operator^=(ENUM &a, ENUM b) { return a = a ^ b; } \
MDBX_NOSANITIZE_ENUM MDBX_CXX14_CONSTEXPR ENUM &operator^=(ENUM &a, \
ENUM b) { \
return a = a ^ b; \
} \
}
#else /* __cplusplus */
/* nope for C since it always allows these operators for enums */
@@ -781,6 +809,10 @@ enum MDBX_log_level_t {
and all other log-messages */
MDBX_LOG_EXTRA = 7,
#ifdef ENABLE_UBSAN
MDBX_LOG_MAX = 7 /* avoid UBSAN false-positive trap by a tests */,
#endif /* ENABLE_UBSAN */
/** for \ref mdbx_setup_debug() only: Don't change current settings */
MDBX_LOG_DONTCHANGE = -1
};
@@ -794,6 +826,8 @@ typedef enum MDBX_log_level_t MDBX_log_level_t;
* effect, but `MDBX_DBG_ASSERT`, `MDBX_DBG_AUDIT` and `MDBX_DBG_JITTER` only if
* libmdbx builded with \ref MDBX_DEBUG. */
enum MDBX_debug_flags_t {
MDBX_DBG_NONE = 0,
/** Enable assertion checks.
* Requires build with \ref MDBX_DEBUG > 0 */
MDBX_DBG_ASSERT = 1,
@@ -816,6 +850,11 @@ enum MDBX_debug_flags_t {
/** Allow read and write transactions overlapping for the same thread */
MDBX_DBG_LEGACY_OVERLAP = 32,
#ifdef ENABLE_UBSAN
MDBX_DBG_MAX = ((unsigned)MDBX_LOG_MAX) << 16 |
63 /* avoid UBSAN false-positive trap by a tests */,
#endif /* ENABLE_UBSAN */
/** for mdbx_setup_debug() only: Don't change current settings */
MDBX_DBG_DONTCHANGE = -1
};
@@ -3461,7 +3500,19 @@ LIBMDBX_API int mdbx_canary_get(const MDBX_txn *txn, MDBX_canary *canary);
/** \brief A callback function used to compare two keys in a database
* \ingroup c_crud
* \see mdbx_cmp() \see mdbx_get_keycmp()
* \see mdbx_get_datacmp \see mdbx_dcmp() */
* \see mdbx_get_datacmp \see mdbx_dcmp()
*
* \anchor avoid_custom_comparators
* It is recommend not using custom comparison functions, but instead
* converting the keys to one of the forms that are suitable for built-in
* comparators (for instance take look to the \ref value2key).
* The reasons to not using custom comparators are:
* - The order of records could not be validated without your code.
* So `mdbx_chk` utility will reports "wrong order" errors
* and the `-i` option is required to ignore ones.
* - A records could not be ordered or sorted without your code.
* So mdbx_load utility should be used with `-a` option to preserve
* input data order. */
typedef int(MDBX_cmp_func)(const MDBX_val *a,
const MDBX_val *b) MDBX_CXX17_NOEXCEPT;
@@ -3538,16 +3589,7 @@ typedef int(MDBX_cmp_func)(const MDBX_val *a,
*
* For \ref mdbx_dbi_open_ex() additional arguments allow you to set custom
* comparison functions for keys and values (for multimaps).
* However, I recommend not using custom comparison functions, but instead
* converting the keys to one of the forms that are suitable for built-in
* comparators (for instance take look to the \ref value2key).
* The reasons to not using custom comparators are:
* - The order of records could not be validated without your code.
* So `mdbx_chk` utility will reports "wrong order" errors
* and the `-i` option is required to ignore ones.
* - A records could not be ordered or sorted without your code.
* So mdbx_load utility should be used with `-a` option to preserve
* input data order.
* \see avoid_custom_comparators
*
* \returns A non-zero error value on failure and 0 on success,
* some possible errors are:
@@ -3564,8 +3606,10 @@ typedef int(MDBX_cmp_func)(const MDBX_val *a,
LIBMDBX_API int mdbx_dbi_open(MDBX_txn *txn, const char *name,
MDBX_db_flags_t flags, MDBX_dbi *dbi);
/** \deprecated Please avoid using custom comparators
* and use mdbx_dbi_open() instead.
/** \deprecated Please
* \ref avoid_custom_comparators "avoid using custom comparators" and use
* \ref mdbx_dbi_open() instead.
*
* \ingroup c_dbi
*
* \param [in] txn transaction handle returned by \ref mdbx_txn_begin().
@@ -3581,7 +3625,9 @@ MDBX_DEPRECATED LIBMDBX_API int
mdbx_dbi_open_ex(MDBX_txn *txn, const char *name, MDBX_db_flags_t flags,
MDBX_dbi *dbi, MDBX_cmp_func *keycmp, MDBX_cmp_func *datacmp);
/** \defgroup value2key Value-to-Key functions to avoid custom comparators
/** \defgroup value2key Value-to-Key functions
* \brief Value-to-Key functions to
* \ref avoid_custom_comparators "avoid using custom comparators"
* \see key2value
* @{
*
@@ -3616,7 +3662,9 @@ MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_INLINE_API(uint32_t, mdbx_key_from_int32,
}
/** @} */
/** \defgroup key2value Key-to-Value functions to avoid custom comparators
/** \defgroup key2value Key-to-Value functions
* \brief Key-to-Value functions to
* \ref avoid_custom_comparators "avoid using custom comparators"
* \see value2key
* @{ */
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int64_t
@@ -4552,6 +4600,7 @@ LIBMDBX_API int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result,
/** \brief Compare two keys according to a particular database.
* \ingroup c_crud
* \see MDBX_cmp_func
*
* This returns a comparison as if the two data items were keys in the
* specified database.
@@ -4576,6 +4625,7 @@ mdbx_get_keycmp(MDBX_db_flags_t flags);
/** \brief Compare two data items according to a particular database.
* \ingroup c_crud
* \see MDBX_cmp_func
*
* This returns a comparison as if the two items were data items of the
* specified database.

2617
mdbx.h++

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,9 @@
From 7834ae8fc834f5f7b98d45703079cd94e5402ed6 Mon Sep 17 00:00:00 2001
From 0c0df833879b3f815959c8bd9b6cc27cb9d71b9e Mon Sep 17 00:00:00 2001
From: Leonid Yuriev <leo@yuriev.ru>
Date: Fri, 27 Nov 2020 16:31:12 +0300
Cc: Heiko Thiery <heiko.thiery@gmail.com>, Thomas Petazzoni <thomas.petazzoni@bootlin.com>, Leonid Yuriev <leo@yuriev.ru
Subject: [PATCH v5 1/1] package/libmdbx: new package (library/database).
Date: Tue, 3 Aug 2021 00:55:27 +0300
Subject: [PATCH] package/libmdbx: new package (library/database).
This patch adds libmdbx v0.9.2:
This patch adds libmdbx v0.10.2:
- libmdbx is one of the fastest compact embeddable key-value ACID database.
- libmdbx has a specific set of properties and capabilities,
focused on creating unique lightweight solutions.
@@ -13,44 +12,14 @@ This patch adds libmdbx v0.9.2:
- https://github.com/erthink/libmdbx
Signed-off-by: Leonid Yuriev <leo@yuriev.ru>
---
Changes v1 -> v2:
- libmdbx version v0.8.2 -> v0.9.1 (released 2020-09-30)
Changes v2 -> v3:
- removed outcommented stuff (suggested by Heiko Thiery).
- cleaned up and simplified the makefile (suggested by Heiko Thiery).
- added patch for C++ header installation.
- added patch with fix minor copy&paste typo.
- added patch with pthread workaround for buggy toolchain/cmake/buildroot.
- added patch for `pthread_yield()`.
- passed `utils/check-package package/libmdbx/*` (suggested by Heiko Thiery)
- passed `utils/test-pkg -a -p libmdbx`,
except w/o threads & w/o MMU (suggested by Heiko Thiery).
Changes v3 -> v4:
- fix passing BR2_PACKAGE_LIBMDBX_TOOLS option to cmake.
- fix using `depend on` instead of `select`,
and add `comment` for C++ toolchain (suggested by Heiko Thiery).
- fix minor help typo.
Changes v4 -> v5:
- libmdbx version v0.9.1 -> v0.9.2 (released 2020-11-27)
- dropped all patch since not needed.
- cosmetic changes (suggested by Thomas Petazzoni <thomas.petazzoni@bootlin.com>).
- added dependence of BR2_TOOLCHAIN_HAS_SYNC_4 (suggested by Thomas Petazzoni <thomas.petazzoni@bootlin.com>).
- added dependence of !BR2_TOOLCHAIN_HAS_GCC_BUG_64735 (suggested by Thomas Petazzoni <thomas.petazzoni@bootlin.com>).
- take in account the BR2_SHARED_STATIC_LIBS (suggested by Thomas Petazzoni <thomas.petazzoni@bootlin.com>).
Signed-off-by: Leonid Yuriev <leo@yuriev.ru>
Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
---
DEVELOPERS | 3 +++
package/Config.in | 1 +
package/libmdbx/Config.in | 45 ++++++++++++++++++++++++++++++++++++
package/libmdbx/libmdbx.hash | 5 ++++
package/libmdbx/libmdbx.mk | 33 ++++++++++++++++++++++++++
5 files changed, 87 insertions(+)
package/libmdbx/libmdbx.mk | 42 +++++++++++++++++++++++++++++++++
5 files changed, 96 insertions(+)
create mode 100644 package/libmdbx/Config.in
create mode 100644 package/libmdbx/libmdbx.hash
create mode 100644 package/libmdbx/libmdbx.mk
@@ -134,28 +103,28 @@ index 0000000000..d13f73938f
+ !BR2_TOOLCHAIN_GCC_AT_LEAST_4_4
diff --git a/package/libmdbx/libmdbx.hash b/package/libmdbx/libmdbx.hash
new file mode 100644
index 0000000000..0d3501f1d9
index 0000000000..c8a28ada34
--- /dev/null
+++ b/package/libmdbx/libmdbx.hash
@@ -0,0 +1,5 @@
+# Hashes from: https://github.com/erthink/libmdbx/releases/
+sha256 c35cc53d66d74ebfc86e39441ba26276541ac7892bf91dba1e70c83665a02767 libmdbx-amalgamated-0.9.2.tar.gz
+sha256 745555704df76626a6612ad0c6bc6b1a66bfab98b9245b07dfb82640aa46d6fa libmdbx-amalgamated-0.10.2.tar.gz
+
+# Locally calculated
+sha256 310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569 LICENSE
diff --git a/package/libmdbx/libmdbx.mk b/package/libmdbx/libmdbx.mk
new file mode 100644
index 0000000000..f3720130ec
index 0000000000..60c5148625
--- /dev/null
+++ b/package/libmdbx/libmdbx.mk
@@ -0,0 +1,33 @@
@@ -0,0 +1,42 @@
+################################################################################
+#
+# libmdbx
+#
+################################################################################
+
+LIBMDBX_VERSION = 0.9.2
+LIBMDBX_VERSION = 0.10.2
+LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.gz
+LIBMDBX_SITE = https://github.com/erthink/libmdbx/releases/download/v$(LIBMDBX_VERSION)
+LIBMDBX_SUPPORTS_IN_SOURCE_BUILD = NO
@@ -165,7 +134,12 @@ index 0000000000..f3720130ec
+LIBMDBX_STRIP_COMPONENTS = 0
+LIBMDBX_INSTALL_STAGING = YES
+
+LIBMDBX_CONF_OPTS = -DMDBX_INSTALL_MANPAGES=OFF -DBUILD_FOR_NATIVE_CPU=OFF \
+# Set CMAKE_BUILD_TYPE to Release to remove -Werror and avoid a build failure
+# with glibc < 2.12
+LIBMDBX_CONF_OPTS = \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DMDBX_INSTALL_MANPAGES=OFF \
+ -DBUILD_FOR_NATIVE_CPU=OFF \
+ -DMDBX_BUILD_CXX=$(if $(BR2_PACKAGE_LIBMDBX_CXX),ON,OFF) \
+ -DMDBX_BUILD_TOOLS=$(if $(BR2_PACKAGE_LIBMDBX_TOOLS),ON,OFF)
+
@@ -176,12 +150,16 @@ index 0000000000..f3720130ec
+endif
+
+ifeq ($(BR2_SHARED_LIBS)$(BR2_SHARED_STATIC_LIBS),y)
+LIBMDBX_CONF_OPTS += -DMDBX_BUILD_SHARED_LIBRARY=ON -DMDBX_LINK_TOOLS_NONSTATIC=ON
+LIBMDBX_CONF_OPTS += \
+ -DMDBX_BUILD_SHARED_LIBRARY=ON \
+ -DMDBX_LINK_TOOLS_NONSTATIC=ON
+else
+LIBMDBX_CONF_OPTS += -DMDBX_BUILD_SHARED_LIBRARY=OFF -DMDBX_LINK_TOOLS_NONSTATIC=OFF
+LIBMDBX_CONF_OPTS += \
+ -DMDBX_BUILD_SHARED_LIBRARY=OFF \
+ -DMDBX_LINK_TOOLS_NONSTATIC=OFF
+endif
+
+$(eval $(cmake-package))
--
2.29.2
2.32.0

View File

@@ -1,219 +0,0 @@
From 0bf9d06e8b090e2d9783d03074f3752ed708f6cf Mon Sep 17 00:00:00 2001
From: Leonid Yuriev <leo@yuriev.ru>
Date: Fri, 27 Nov 2020 16:31:12 +0300
Cc: Heiko Thiery <heiko.thiery@gmail.com>
Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Subject: [PATCH v5 0/1] cover letter for package/libmdbx: new package (library/database)
This patch adds libmdbx v0.9.2 and below is a brief overview of libmdbx.
Please merge.
Regards,
Leonid.
--
libmdbx is an extremely fast, compact, powerful, embedded, transactional
key-value database, with permissive license. libmdbx has a specific set
of properties and capabilities, focused on creating unique lightweight
solutions.
Historically, libmdbx (MDBX) is a deeply revised and extended descendant
of the legendary LMDB (Lightning Memory-Mapped Database). libmdbx
inherits all benefits from LMDB, but resolves some issues and adds a set
of improvements.
According to developers, for now libmdbx surpasses the LMDB in terms of
reliability, features and performance.
The most important differences MDBX from LMDB:
==============================================
1. More attention is paid to the quality of the code, to an
"unbreakability" of the API, to testing and automatic checks (i.e.
sanitizers, etc). So there:
- more control during operation;
- more checking parameters, internal audit of database structures;
- no warnings from compiler;
- no issues from ASAN, UBSAN, Valgrind, Coverity;
- etc.
2. Keys could be more than 2 times longer than LMDB.
3. Up to 20% faster than LMDB in CRUD benchmarks.
4. Automatic on-the-fly database size adjustment,
both increment and reduction.
5. Automatic continuous zero-overhead database compactification.
6. The same database format for 32- and 64-bit builds.
7. LIFO policy for Garbage Collection recycling (this can significantly
increase write performance due write-back disk cache up to several times
in a best case scenario).
8. Range query estimation.
9. Utility for checking the integrity of the database structure with
some recovery capabilities.
For more info please refer:
- https://github.com/erthink/libmdbx for source code and README.
- https://erthink.github.io/libmdbx for API description.
--
MDBX is a Btree-based database management library modeled loosely on the
BerkeleyDB API, but much simplified. The entire database (aka
"environment") is exposed in a memory map, and all data fetches return
data directly from the mapped memory, so no malloc's or memcpy's occur
during data fetches. As such, the library is extremely simple because it
requires no page caching layer of its own, and it is extremely high
performance and memory-efficient. It is also fully transactional with
full ACID semantics, and when the memory map is read-only, the database
integrity cannot be corrupted by stray pointer writes from application
code.
The library is fully thread-aware and supports concurrent read/write
access from multiple processes and threads. Data pages use a
copy-on-write strategy so no active data pages are ever overwritten,
which also provides resistance to corruption and eliminates the need of
any special recovery procedures after a system crash. Writes are fully
serialized; only one write transaction may be active at a time, which
guarantees that writers can never deadlock. The database structure is
multi-versioned so readers run with no locks; writers cannot block
readers, and readers don't block writers.
Unlike other well-known database mechanisms which use either write-ahead
transaction logs or append-only data writes, MDBX requires no
maintenance during operation. Both write-ahead loggers and append-only
databases require periodic checkpointing and/or compaction of their log
or database files otherwise they grow without bound. MDBX tracks
retired/freed pages within the database and re-uses them for new write
operations, so the database size does not grow without bound in normal
use.
The memory map can be used as a read-only or read-write map. It is
read-only by default as this provides total immunity to corruption.
Using read-write mode offers much higher write performance, but adds the
possibility for stray application writes thru pointers to silently
corrupt the database.
Features
========
- Key-value data model, keys are always sorted.
- Fully ACID-compliant, through to MVCC and CoW.
- Multiple key-value sub-databases within a single datafile.
- Range lookups, including range query estimation.
- Efficient support for short fixed length keys, including native
32/64-bit integers.
- Ultra-efficient support for multimaps. Multi-values sorted, searchable
and iterable. Keys stored without duplication.
- Data is memory-mapped and accessible directly/zero-copy. Traversal of
database records is extremely-fast.
- Transactions for readers and writers, ones do not block others.
- Writes are strongly serialized. No transaction conflicts nor
deadlocks.
- Readers are non-blocking, notwithstanding snapshot isolation.
- Nested write transactions.
- Reads scale linearly across CPUs.
- Continuous zero-overhead database compactification.
- Automatic on-the-fly database size adjustment.
- Customizable database page size.
- Olog(N) cost of lookup, insert, update, and delete operations by
virtue of B+ tree characteristics.
- Online hot backup.
- Append operation for efficient bulk insertion of pre-sorted data.
- No WAL nor any transaction journal. No crash recovery needed. No
maintenance is required.
- No internal cache and/or memory management, all done by basic OS
services.
Limitations
===========
- Page size: a power of 2, maximum 65536 bytes, default 4096 bytes.
- Key size: minimum 0, maximum ≈¼ pagesize (1300 bytes for default 4K
pagesize, 21780 bytes for 64K pagesize).
- Value size: minimum 0, maximum 2146435072 (0x7FF00000) bytes for maps,
≈¼ pagesize for multimaps (1348 bytes default 4K pagesize, 21828 bytes
for 64K pagesize).
- Write transaction size: up to 4194301 (0x3FFFFD) pages (16 GiB for
default 4K pagesize, 256 GiB for 64K pagesize).
- Database size: up to 2147483648 pages (8 TiB for default 4K pagesize,
128 TiB for 64K pagesize).
- Maximum sub-databases: 32765.
Gotchas
=======
- There cannot be more than one writer at a time, i.e. no more than one
write transaction at a time.
- libmdbx is based on B+ tree, so access to database pages is mostly
random. Thus SSDs provide a significant performance boost over
spinning disks for large databases.
- libmdbx uses shadow paging instead of WAL. Thus syncing data to disk
might be a bottleneck for write intensive workload.
- libmdbx uses copy-on-write for 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, the database can grow quickly, and result in
performance degradation. Try to avoid long running read transactions.
- libmdbx is extraordinarily fast and provides minimal overhead for data
access, so you should reconsider using brute force techniques and
double check your code. On the one hand, in the case of libmdbx, a
simple linear search may be more profitable than complex indexes. On
the other hand, if you make something suboptimally, you can notice
detrimentally only on sufficiently large data.
--
Leonid Yuriev (1):
package/libmdbx: new package (library/database).
DEVELOPERS | 3 +++
package/Config.in | 1 +
package/libmdbx/Config.in | 45 ++++++++++++++++++++++++++++++++++++
package/libmdbx/libmdbx.hash | 5 ++++
package/libmdbx/libmdbx.mk | 33 ++++++++++++++++++++++++++
5 files changed, 87 insertions(+)
create mode 100644 package/libmdbx/Config.in
create mode 100644 package/libmdbx/libmdbx.hash
create mode 100644 package/libmdbx/libmdbx.mk
--
2.29.2

View File

@@ -1,34 +1,34 @@
N | MASK | ENV | TXN | DB | PUT | DBI | NODE | PAGE |
--|---------|-----------|--------------|----------|-----------|------------|---------|----------|
0 |0000 0001|ALLOC_CACHE|TXN_FINISHED | | |DBI_DIRTY |F_BIGDATA|P_BRANCH
1 |0000 0002|ALLOC_GC |TXN_ERROR |REVERSEKEY|F_SUBDATA |DBI_STALE |F_SUBDATA|P_LEAF
2 |0000 0004|ALLOC_NEW |TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW
3 |0000 0008|ALLOC_SLOT |TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META
4 |0000 0010| |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD
5 |0000 0020| | |INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP
7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | |
8 |0000 0100| | | | | | |
9 |0000 0200| | | | | | |
10|0000 0400| | | | | | |
11|0000 0800| | | | | | |
12|0000 1000| | | | | | |
13|0000 2000| | | | | | |P_SPILLED
14|0000 4000|NOSUBDIR | | | | | |P_LOOSE
15|0000 8000| | |DB_VALID |NOSPILL | | |P_FROZEN
16|0001 0000|SAFE_NOSYNC|TXN_NOSYNC | |RESERVE | |RESERVE |
17|0002 0000|RDONLY |TXN_RDONLY | |APPEND | |APPEND |
18|0004 0000|NOMETASYNC |TXN_NOMETASYNC|CREATE |APPENDDUP
19|0008 0000|WRITEMAP |<= | |MULTIPLE
20|0010 0000|UTTERLY | |
21|0020 0000|NOTLS |<= |
22|0040 0000|EXCLUSIVE | |
23|0080 0000|NORDAHEAD | |
24|0100 0000|NOMEMINIT |TXN_PREPARE |
25|0200 0000|COALESCE | |
26|0400 0000|LIFORECLAIM| |
27|0800 0000|PAGEPERTURB| |
28|1000 0000|ENV_TXKEY |TXN_TRY |
29|2000 0000|ENV_ACTIVE | |
30|4000 0000|ACCEDE |SHRINK_ALLOWED|DB_ACCEDE
31|8000 0000|FATAL_ERROR| |
N | MASK | ENV | TXN | DB | PUT | DBI | NODE | PAGE | MRESIZE |
--|---------|-----------|--------------|----------|-----------|------------|---------|----------|---------|
0 |0000 0001|ALLOC_CACHE|TXN_FINISHED | | |DBI_DIRTY |F_BIGDATA|P_BRANCH | |
1 |0000 0002|ALLOC_GC |TXN_ERROR |REVERSEKEY|F_SUBDATA |DBI_STALE |F_SUBDATA|P_LEAF | |
2 |0000 0004|ALLOC_NEW |TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW| |
3 |0000 0008|ALLOC_SLOT |TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | |
4 |0000 0010| |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | |
5 |0000 0020| | |INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2 | |
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP | |
7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | | | |
8 |0000 0100| _MAY_MOVE | | | | | | | <= |
9 |0000 0200| _MAY_UNMAP| | | | | | | <= |
10|0000 0400| | | | | | | | |
11|0000 0800| | | | | | | | |
12|0000 1000| | | | | | | | |
13|0000 2000| | | | | | |P_SPILLED | |
14|0000 4000|NOSUBDIR | | | | | |P_LOOSE | |
15|0000 8000| | |DB_VALID |NOSPILL | | |P_FROZEN | |
16|0001 0000|SAFE_NOSYNC|TXN_NOSYNC | |RESERVE | |RESERVE | | |
17|0002 0000|RDONLY |TXN_RDONLY | |APPEND | |APPEND | | <= |
18|0004 0000|NOMETASYNC |TXN_NOMETASYNC|CREATE |APPENDDUP | | | | |
19|0008 0000|WRITEMAP |<= | |MULTIPLE | | | | <= |
20|0010 0000|UTTERLY | | | | | | | <= |
21|0020 0000|NOTLS |<= | | | | | | |
22|0040 0000|EXCLUSIVE | | | | | | | |
23|0080 0000|NORDAHEAD | | | | | | | |
24|0100 0000|NOMEMINIT |TXN_PREPARE | | | | | | |
25|0200 0000|COALESCE | | | | | | | |
26|0400 0000|LIFORECLAIM| | | | | | | |
27|0800 0000|PAGEPERTURB| | | | | | | |
28|1000 0000|ENV_TXKEY |TXN_TRY | | | | | | |
29|2000 0000|ENV_ACTIVE | | | | | | | |
30|4000 0000|ACCEDE |SHRINK_ALLOWED|DB_ACCEDE | | | | | |
31|8000 0000|FATAL_ERROR| | | | | | | |

1050
src/core.c

File diff suppressed because it is too large Load Diff

View File

@@ -190,7 +190,7 @@
# elif defined(__GNUC__) || __has_attribute(__hot__)
# define __hot __attribute__((__hot__)) __optimize("O3")
# else
# define __hot __optimize("O3")
# define __hot __optimize("O3")
# endif
# else
# define __hot
@@ -304,11 +304,6 @@
# define ARRAY_END(array) (&array[ARRAY_LENGTH(array)])
#endif /* ARRAY_END */
#ifndef STRINGIFY
# define STRINGIFY_HELPER(x) #x
# define STRINGIFY(x) STRINGIFY_HELPER(x)
#endif /* STRINGIFY */
#define CONCAT(a,b) a##b
#define XCONCAT(a,b) CONCAT(a,b)
@@ -326,7 +321,7 @@
#define MDBX_STRING_TETRAD(str) MDBX_TETRAD(str[0], str[1], str[2], str[3])
#define FIXME "FIXME: " __FILE__ ", " STRINGIFY(__LINE__)
#define FIXME "FIXME: " __FILE__ ", " MDBX_STRINGIFY(__LINE__)
#ifndef STATIC_ASSERT_MSG
# if defined(static_assert)

View File

@@ -818,15 +818,13 @@ typedef struct MDBX_lockinfo {
#if MDBX_WORDBITS >= 64
#define MAX_MAPSIZE MAX_MAPSIZE64
#define MDBX_READERS_LIMIT \
((MAX_PAGESIZE - sizeof(MDBX_lockinfo)) / sizeof(MDBX_reader))
#define MDBX_PGL_LIMIT ((size_t)MAX_PAGENO)
#else
#define MDBX_READERS_LIMIT 1024
#define MAX_MAPSIZE MAX_MAPSIZE32
#define MDBX_PGL_LIMIT (MAX_MAPSIZE32 / MIN_PAGESIZE)
#endif /* MDBX_WORDBITS */
#define MDBX_READERS_LIMIT 32767
#define MDBX_RADIXSORT_THRESHOLD 333
/*----------------------------------------------------------------------------*/
@@ -1148,7 +1146,7 @@ struct MDBX_env {
mdbx_thread_key_t me_txkey; /* thread-key for readers */
char *me_pathname; /* path to the DB files */
void *me_pbuf; /* scratch area for DUPSORT put() */
MDBX_txn *me_txn0; /* prealloc'd write transaction */
MDBX_txn *me_txn0; /* preallocated write transaction */
MDBX_dbx *me_dbxs; /* array of static DB info */
uint16_t *me_dbflags; /* array of flags from MDBX_db.md_flags */
@@ -1648,3 +1646,17 @@ MDBX_MAYBE_UNUSED static void static_checks(void) {
#ifdef __cplusplus
}
#endif
#define MDBX_ASAN_POISON_MEMORY_REGION(addr, size) \
do { \
mdbx_trace("POISON_MEMORY_REGION(%p, %zu) at %u", (void *)(addr), \
(size_t)(size), __LINE__); \
ASAN_POISON_MEMORY_REGION(addr, size); \
} while (0)
#define MDBX_ASAN_UNPOISON_MEMORY_REGION(addr, size) \
do { \
mdbx_trace("UNPOISON_MEMORY_REGION(%p, %zu) at %u", (void *)(addr), \
(size_t)(size), __LINE__); \
ASAN_UNPOISON_MEMORY_REGION(addr, size); \
} while (0)

View File

@@ -29,7 +29,7 @@ uint32_t mdbx_linux_kernel_version;
bool mdbx_RunningOnWSL1;
#endif /* xMDBX_ALLOY */
static __cold uint8_t probe_for_WSL(const char *tag) {
__cold static uint8_t probe_for_WSL(const char *tag) {
const char *const WSL = strstr(tag, "WSL");
if (WSL && WSL[3] >= '2' && WSL[3] <= '9')
return WSL[3] - '0';
@@ -45,7 +45,7 @@ static __cold uint8_t probe_for_WSL(const char *tag) {
#endif /* Linux */
static __cold __attribute__((__constructor__)) void
__cold static __attribute__((__constructor__)) void
mdbx_global_constructor(void) {
#if defined(__linux__) || defined(__gnu_linux__)
struct utsname buffer;
@@ -81,7 +81,7 @@ mdbx_global_constructor(void) {
mdbx_rthc_global_init();
}
static __cold __attribute__((__destructor__)) void
__cold static __attribute__((__destructor__)) void
mdbx_global_destructor(void) {
mdbx_rthc_global_dtor();
}
@@ -145,7 +145,7 @@ mdbx_global_destructor(void) {
#if MDBX_USE_OFDLOCKS
static int op_setlk, op_setlkw, op_getlk;
static void __cold choice_fcntl() {
__cold static void choice_fcntl() {
assert(!op_setlk && !op_setlkw && !op_getlk);
if ((mdbx_runtime_flags & MDBX_DBG_LEGACY_MULTIOPEN) == 0
#if defined(__linux__) || defined(__gnu_linux__)
@@ -334,7 +334,7 @@ static int check_fstat(MDBX_env *env) {
return rc;
}
MDBX_INTERNAL_FUNC int __cold mdbx_lck_seize(MDBX_env *env) {
__cold MDBX_INTERNAL_FUNC int mdbx_lck_seize(MDBX_env *env) {
assert(env->me_lazy_fd != INVALID_HANDLE_VALUE);
if (unlikely(mdbx_getpid() != env->me_pid))
return MDBX_PANIC;
@@ -487,7 +487,7 @@ MDBX_INTERNAL_FUNC int mdbx_lck_downgrade(MDBX_env *env) {
return rc;
}
MDBX_INTERNAL_FUNC int __cold mdbx_lck_destroy(MDBX_env *env,
__cold MDBX_INTERNAL_FUNC int mdbx_lck_destroy(MDBX_env *env,
MDBX_env *inprocess_neighbor) {
if (unlikely(mdbx_getpid() != env->me_pid))
return MDBX_PANIC;
@@ -574,7 +574,7 @@ MDBX_INTERNAL_FUNC int __cold mdbx_lck_destroy(MDBX_env *env,
/*---------------------------------------------------------------------------*/
MDBX_INTERNAL_FUNC int __cold mdbx_lck_init(MDBX_env *env,
__cold MDBX_INTERNAL_FUNC int mdbx_lck_init(MDBX_env *env,
MDBX_env *inprocess_neighbor,
int global_uniqueness_flag) {
#if MDBX_LOCKING == MDBX_LOCKING_SYSV
@@ -721,7 +721,7 @@ bailout:
#endif /* MDBX_LOCKING > 0 */
}
static int __cold mdbx_ipclock_failed(MDBX_env *env, mdbx_ipclock_t *ipc,
__cold static int mdbx_ipclock_failed(MDBX_env *env, mdbx_ipclock_t *ipc,
const int err) {
int rc = err;
#if MDBX_LOCKING == MDBX_LOCKING_POSIX2008 || MDBX_LOCKING == MDBX_LOCKING_SYSV

View File

@@ -260,6 +260,7 @@ static int suspend_and_append(mdbx_handle_array_t **array,
MDBX_INTERNAL_FUNC int
mdbx_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
mdbx_assert(env, (env->me_flags & MDBX_NOTLS) == 0);
const uintptr_t CurrentTid = GetCurrentThreadId();
int rc;
if (env->me_lck_mmap.lck) {
@@ -277,12 +278,6 @@ mdbx_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
if (reader->mr_tid.weak == CurrentTid ||
reader->mr_tid.weak == WriteTxnOwner)
goto skip_lck;
if (env->me_flags & MDBX_NOTLS) {
/* Skip duplicates in no-tls mode */
for (const MDBX_reader *scan = reader; --scan >= begin;)
if (scan->mr_tid.weak == reader->mr_tid.weak)
goto skip_lck;
}
rc = suspend_and_append(array, (mdbx_tid_t)reader->mr_tid.weak);
if (rc != MDBX_SUCCESS) {

View File

@@ -124,7 +124,7 @@ public:
//------------------------------------------------------------------------------
__cold std::string format_va(const char *fmt, va_list ap) {
__cold std::string format_va(const char *fmt, va_list ap) {
va_list ones;
va_copy(ones, ap);
#ifdef _MSC_VER
@@ -145,7 +145,7 @@ __cold std::string format_va(const char *fmt, va_list ap) {
return result;
}
__cold std::string format(const char *fmt, ...) {
__cold std::string format(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
std::string result = format_va(fmt, ap);
@@ -166,15 +166,15 @@ public:
virtual ~bug() noexcept;
};
__cold bug::bug(const trouble_location &location) noexcept
__cold bug::bug(const trouble_location &location) noexcept
: std::runtime_error(format("mdbx.bug: %s.%s at %s:%u", location.function(),
location.condition(), location.filename(),
location.line())),
location_(location) {}
__cold bug::~bug() noexcept {}
__cold bug::~bug() noexcept {}
[[noreturn]] __cold void raise_bug(const trouble_location &what_and_where) {
[[noreturn]] __cold void raise_bug(const trouble_location &what_and_where) {
throw bug(what_and_where);
}
@@ -188,7 +188,7 @@ __cold bug::~bug() noexcept {}
#define ENSURE(condition) \
do \
if (MDBX_UNLIKELY(!(condition))) \
RAISE_BUG(__LINE__, #condition, __func__, __FILE__); \
MDBX_CXX20_UNLIKELY RAISE_BUG(__LINE__, #condition, __func__, __FILE__); \
while (0)
#define NOT_IMPLEMENTED() \
@@ -268,16 +268,22 @@ namespace mdbx {
[[noreturn]] __cold void throw_max_length_exceeded() {
throw std::length_error(
"mdbx:: exceeded the maximal length of data/slice/buffer");
"mdbx:: Exceeded the maximal length of data/slice/buffer.");
}
[[noreturn]] __cold void throw_too_small_target_buffer() {
throw std::length_error("mdbx:: the target buffer is too small");
throw std::length_error("mdbx:: The target buffer is too small.");
}
[[noreturn]] __cold void throw_out_range() {
throw std::out_of_range("mdbx:: slice or buffer method was called with "
"an argument that exceeds the length");
throw std::out_of_range("mdbx:: Slice or buffer method was called with "
"an argument that exceeds the length.");
}
[[noreturn]] __cold void throw_allocators_mismatch() {
throw std::logic_error(
"mdbx:: An allocators mismatch, so an object could not be transferred "
"into an incompatible memory allocation scheme.");
}
__cold exception::exception(const ::mdbx::error &error) noexcept
@@ -338,7 +344,7 @@ __cold const char *error::what() const noexcept {
switch (code()) {
#define ERROR_CASE(CODE) \
case CODE: \
return STRINGIFY(CODE)
return MDBX_STRINGIFY(CODE)
ERROR_CASE(MDBX_ENODATA);
ERROR_CASE(MDBX_EINVAL);
ERROR_CASE(MDBX_EACCESS);
@@ -474,15 +480,15 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
F0, F1, F1, F1, F4, P_, P_, P_, P_, P_, P_, P_, P_, P_, P_, P_ // f0
};
if (length() < 1)
return false;
if (MDBX_UNLIKELY(length() < 1))
MDBX_CXX20_UNLIKELY return false;
auto src = byte_ptr();
const auto end = src + length();
if (MDBX_UNLIKELY(disable_utf8)) {
do
if (MDBX_UNLIKELY((P_ & map[*src]) == 0))
return false;
MDBX_CXX20_UNLIKELY return false;
while (++src < end);
return true;
}
@@ -493,35 +499,35 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
const auto second_to = range_to[bits & second_range_mask];
switch (bits >> LS) {
default:
return false;
MDBX_CXX20_UNLIKELY return false;
case 1:
src += 1;
continue;
case 2:
if (unlikely(src + 1 >= end))
return false;
if (unlikely(src[1] < second_from || src[1] > second_to))
return false;
if (MDBX_UNLIKELY(src + 1 >= end))
MDBX_CXX20_UNLIKELY return false;
if (MDBX_UNLIKELY(src[1] < second_from || src[1] > second_to))
MDBX_CXX20_UNLIKELY return false;
src += 2;
continue;
case 3:
if (unlikely(src + 3 >= end))
return false;
if (unlikely(src[1] < second_from || src[1] > second_to))
return false;
if (unlikely(src[2] < 0x80 || src[2] > 0xBF))
return false;
if (MDBX_UNLIKELY(src + 3 >= end))
MDBX_CXX20_UNLIKELY return false;
if (MDBX_UNLIKELY(src[1] < second_from || src[1] > second_to))
MDBX_CXX20_UNLIKELY return false;
if (MDBX_UNLIKELY(src[2] < 0x80 || src[2] > 0xBF))
MDBX_CXX20_UNLIKELY return false;
src += 3;
continue;
case 4:
if (unlikely(src + 4 >= end))
return false;
if (unlikely(src[1] < second_from || src[1] > second_to))
return false;
if (unlikely(src[2] < 0x80 || src[2] > 0xBF))
return false;
if (unlikely(src[3] < 0x80 || src[3] > 0xBF))
return false;
if (MDBX_UNLIKELY(src + 4 >= end))
MDBX_CXX20_UNLIKELY return false;
if (MDBX_UNLIKELY(src[1] < second_from || src[1] > second_to))
MDBX_CXX20_UNLIKELY return false;
if (MDBX_UNLIKELY(src[2] < 0x80 || src[2] > 0xBF))
MDBX_CXX20_UNLIKELY return false;
if (MDBX_UNLIKELY(src[3] < 0x80 || src[3] > 0xBF))
MDBX_CXX20_UNLIKELY return false;
src += 4;
continue;
}
@@ -532,15 +538,14 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
//------------------------------------------------------------------------------
char *slice::to_hex(char *__restrict dest, size_t dest_size, bool uppercase,
unsigned wrap_width) const {
if (MDBX_UNLIKELY(to_hex_bytes(wrap_width) > dest_size))
throw_too_small_target_buffer();
char *to_hex::write_bytes(char *__restrict dest, size_t dest_size) const {
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
auto src = byte_ptr();
auto src = source.byte_ptr();
const char alphabase = (uppercase ? 'A' : 'a') - 10;
auto line = dest;
for (const auto end = src + length(); src != end; ++src) {
for (const auto end = source.end_byte_ptr(); src != end; ++src) {
const int8_t hi = *src >> 4;
const int8_t lo = *src & 15;
dest[0] = char(alphabase + hi + (((hi - 10) >> 7) & -7));
@@ -554,16 +559,15 @@ char *slice::to_hex(char *__restrict dest, size_t dest_size, bool uppercase,
return dest;
}
byte *slice::from_hex(byte *__restrict dest, size_t dest_size,
bool ignore_spaces) const {
if (MDBX_UNLIKELY(length() % 2 && !ignore_spaces))
throw std::domain_error(
char *from_hex::write_bytes(char *__restrict dest, size_t dest_size) const {
if (MDBX_UNLIKELY(source.length() % 2 && !ignore_spaces))
MDBX_CXX20_UNLIKELY throw std::domain_error(
"mdbx::from_hex:: odd length of hexadecimal string");
if (MDBX_UNLIKELY(from_hex_bytes() > dest_size))
throw_too_small_target_buffer();
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
auto src = byte_ptr();
for (auto left = length(); left > 0;) {
auto src = source.byte_ptr();
for (auto left = source.length(); left > 0;) {
if (MDBX_UNLIKELY(*src <= ' ') &&
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
++src;
@@ -572,7 +576,8 @@ byte *slice::from_hex(byte *__restrict dest, size_t dest_size,
}
if (MDBX_UNLIKELY(left < 1 || !isxdigit(src[0]) || !isxdigit(src[1])))
throw std::domain_error("mdbx::from_hex:: invalid hexadecimal string");
MDBX_CXX20_UNLIKELY throw std::domain_error(
"mdbx::from_hex:: invalid hexadecimal string");
int8_t hi = src[0];
hi = (hi | 0x20) - 'a';
@@ -589,13 +594,13 @@ byte *slice::from_hex(byte *__restrict dest, size_t dest_size,
return dest;
}
bool slice::is_hex(bool ignore_spaces) const noexcept {
if (MDBX_UNLIKELY(length() % 2 && !ignore_spaces))
return false;
bool from_hex::is_erroneous() const noexcept {
if (MDBX_UNLIKELY(source.length() % 2 && !ignore_spaces))
MDBX_CXX20_UNLIKELY return true;
bool got = false;
auto src = byte_ptr();
for (auto left = length(); left > 0;) {
auto src = source.byte_ptr();
for (auto left = source.length(); left > 0;) {
if (MDBX_UNLIKELY(*src <= ' ') &&
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
++src;
@@ -604,13 +609,13 @@ bool slice::is_hex(bool ignore_spaces) const noexcept {
}
if (MDBX_UNLIKELY(left < 1 || !isxdigit(src[0]) || !isxdigit(src[1])))
return false;
MDBX_CXX20_UNLIKELY return true;
got = true;
src += 2;
left -= 2;
}
return got;
return !got;
}
//------------------------------------------------------------------------------
@@ -658,13 +663,12 @@ static inline char b58_8to11(uint64_t &v) noexcept {
return b58_alphabet[i];
}
char *slice::to_base58(char *__restrict dest, size_t dest_size,
unsigned wrap_width) const {
if (MDBX_UNLIKELY(to_base58_bytes(wrap_width) > dest_size))
throw_too_small_target_buffer();
char *to_base58::write_bytes(char *__restrict dest, size_t dest_size) const {
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
auto src = byte_ptr();
size_t left = length();
auto src = source.byte_ptr();
size_t left = source.length();
auto line = dest;
while (MDBX_LIKELY(left > 7)) {
left -= 8;
@@ -741,13 +745,12 @@ static inline signed char b58_11to8(uint64_t &v, const byte c) noexcept {
return m;
}
byte *slice::from_base58(byte *__restrict dest, size_t dest_size,
bool ignore_spaces) const {
if (MDBX_UNLIKELY(from_base58_bytes() > dest_size))
throw_too_small_target_buffer();
char *from_base58::write_bytes(char *__restrict dest, size_t dest_size) const {
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
auto src = byte_ptr();
for (auto left = length(); left > 0;) {
auto src = source.byte_ptr();
for (auto left = source.length(); left > 0;) {
if (MDBX_UNLIKELY(isspace(*src)) && ignore_spaces) {
++src;
--left;
@@ -762,7 +765,7 @@ byte *slice::from_base58(byte *__restrict dest, size_t dest_size,
b58_11to8(v, src[6]) | b58_11to8(v, src[7]) |
b58_11to8(v, src[8]) | b58_11to8(v, src[9]) |
b58_11to8(v, src[10])) < 0))
goto bailout;
MDBX_CXX20_UNLIKELY goto bailout;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
v = bswap64(v);
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
@@ -777,14 +780,14 @@ byte *slice::from_base58(byte *__restrict dest, size_t dest_size,
}
constexpr unsigned invalid_length_mask = 1 << 1 | 1 << 4 | 1 << 8;
if (invalid_length_mask & (1 << left))
goto bailout;
if (MDBX_UNLIKELY(invalid_length_mask & (1 << left)))
MDBX_CXX20_UNLIKELY goto bailout;
uint64_t v = 1;
unsigned parrots = 0;
do {
if (MDBX_UNLIKELY(b58_11to8(v, *src++) < 0))
goto bailout;
MDBX_CXX20_UNLIKELY goto bailout;
parrots += 32;
} while (--left);
@@ -801,10 +804,10 @@ bailout:
throw std::domain_error("mdbx::from_base58:: invalid base58 string");
}
bool slice::is_base58(bool ignore_spaces) const noexcept {
bool from_base58::is_erroneous() const noexcept {
bool got = false;
auto src = byte_ptr();
for (auto left = length(); left > 0;) {
auto src = source.byte_ptr();
for (auto left = source.length(); left > 0;) {
if (MDBX_UNLIKELY(*src <= ' ') &&
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
++src;
@@ -817,7 +820,7 @@ bool slice::is_base58(bool ignore_spaces) const noexcept {
b58_map[src[3]] | b58_map[src[4]] | b58_map[src[5]] |
b58_map[src[6]] | b58_map[src[7]] | b58_map[src[8]] |
b58_map[src[9]] | b58_map[src[10]]) < 0))
return false;
MDBX_CXX20_UNLIKELY return true;
src += 11;
left -= 11;
got = true;
@@ -830,12 +833,12 @@ bool slice::is_base58(bool ignore_spaces) const noexcept {
do
if (MDBX_UNLIKELY(b58_map[*src++] < 0))
return false;
MDBX_CXX20_UNLIKELY return true;
while (--left);
got = true;
break;
}
return got;
return !got;
}
//------------------------------------------------------------------------------
@@ -854,13 +857,12 @@ static inline void b64_3to4(const byte x, const byte y, const byte z,
dest[3] = alphabet[z & 0x3f];
}
char *slice::to_base64(char *__restrict dest, size_t dest_size,
unsigned wrap_width) const {
if (MDBX_UNLIKELY(to_base64_bytes(wrap_width) > dest_size))
throw_too_small_target_buffer();
char *to_base64::write_bytes(char *__restrict dest, size_t dest_size) const {
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
auto src = byte_ptr();
size_t left = length();
auto src = source.byte_ptr();
size_t left = source.length();
auto line = dest;
while (true) {
switch (left) {
@@ -910,22 +912,22 @@ static const signed char b64_map[256] = {
static inline signed char b64_4to3(signed char a, signed char b, signed char c,
signed char d,
byte *__restrict dest) noexcept {
char *__restrict dest) noexcept {
dest[0] = byte((a << 2) + ((b & 0x30) >> 4));
dest[1] = byte(((b & 0xf) << 4) + ((c & 0x3c) >> 2));
dest[2] = byte(((c & 0x3) << 6) + d);
return a | b | c | d;
}
byte *slice::from_base64(byte *__restrict dest, size_t dest_size,
bool ignore_spaces) const {
if (MDBX_UNLIKELY(length() % 4 && !ignore_spaces))
throw std::domain_error("mdbx::from_base64:: odd length of base64 string");
if (MDBX_UNLIKELY(from_base64_bytes() > dest_size))
throw_too_small_target_buffer();
char *from_base64::write_bytes(char *__restrict dest, size_t dest_size) const {
if (MDBX_UNLIKELY(source.length() % 4 && !ignore_spaces))
MDBX_CXX20_UNLIKELY throw std::domain_error(
"mdbx::from_base64:: odd length of base64 string");
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
auto src = byte_ptr();
for (auto left = length(); left > 0;) {
auto src = source.byte_ptr();
for (auto left = source.length(); left > 0;) {
if (MDBX_UNLIKELY(*src <= ' ') &&
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
++src;
@@ -933,10 +935,11 @@ byte *slice::from_base64(byte *__restrict dest, size_t dest_size,
continue;
}
if (MDBX_UNLIKELY(left < 3)) {
bailout:
throw std::domain_error("mdbx::from_base64:: invalid base64 string");
}
if (MDBX_UNLIKELY(left < 3))
MDBX_CXX20_UNLIKELY {
bailout:
throw std::domain_error("mdbx::from_base64:: invalid base64 string");
}
const signed char a = b64_map[src[0]], b = b64_map[src[1]],
c = b64_map[src[2]], d = b64_map[src[3]];
if (MDBX_UNLIKELY(b64_4to3(a, b, c, d, dest) < 0)) {
@@ -946,7 +949,7 @@ byte *slice::from_base64(byte *__restrict dest, size_t dest_size,
if (c == d)
return dest + 1;
}
goto bailout;
MDBX_CXX20_UNLIKELY goto bailout;
}
src += 4;
left -= 4;
@@ -954,13 +957,13 @@ byte *slice::from_base64(byte *__restrict dest, size_t dest_size,
return dest;
}
bool slice::is_base64(bool ignore_spaces) const noexcept {
if (MDBX_UNLIKELY(length() % 4 && !ignore_spaces))
return false;
bool from_base64::is_erroneous() const noexcept {
if (MDBX_UNLIKELY(source.length() % 4 && !ignore_spaces))
MDBX_CXX20_UNLIKELY return true;
bool got = false;
auto src = byte_ptr();
for (auto left = length(); left > 0;) {
auto src = source.byte_ptr();
for (auto left = source.length(); left > 0;) {
if (MDBX_UNLIKELY(*src <= ' ') &&
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
++src;
@@ -969,19 +972,20 @@ bool slice::is_base64(bool ignore_spaces) const noexcept {
}
if (MDBX_UNLIKELY(left < 3))
return false;
MDBX_CXX20_UNLIKELY return false;
const signed char a = b64_map[src[0]], b = b64_map[src[1]],
c = b64_map[src[2]], d = b64_map[src[3]];
if (MDBX_UNLIKELY((a | b | c | d) < 0)) {
if (left == 4 && (a | b) >= 0 && d == EQ && (c >= 0 || c == d))
if (MDBX_UNLIKELY((a | b | c | d) < 0))
MDBX_CXX20_UNLIKELY {
if (left == 4 && (a | b) >= 0 && d == EQ && (c >= 0 || c == d))
return false;
return true;
return false;
}
}
got = true;
src += 4;
left -= 4;
}
return got;
return !got;
}
//------------------------------------------------------------------------------
@@ -1185,9 +1189,9 @@ static inline MDBX_env *create_env() {
}
env_managed::~env_managed() noexcept {
if (handle_)
error::success_or_panic(::mdbx_env_close(handle_), "mdbx::~env()",
"mdbx_env_close");
if (MDBX_UNLIKELY(handle_))
MDBX_CXX20_UNLIKELY error::success_or_panic(
::mdbx_env_close(handle_), "mdbx::~env()", "mdbx_env_close");
}
void env_managed::close(bool dont_sync) {
@@ -1195,12 +1199,12 @@ void env_managed::close(bool dont_sync) {
static_cast<MDBX_error_t>(::mdbx_env_close_ex(handle_, dont_sync));
switch (rc.code()) {
case MDBX_EBADSIGN:
handle_ = nullptr;
MDBX_CXX20_UNLIKELY handle_ = nullptr;
__fallthrough /* fall through */;
default:
rc.throw_exception();
MDBX_CXX20_UNLIKELY rc.throw_exception();
case MDBX_SUCCESS:
handle_ = nullptr;
MDBX_CXX20_LIKELY handle_ = nullptr;
}
}
@@ -1222,7 +1226,7 @@ __cold env_managed::env_managed(const ::std::filesystem::path &pathname,
if (op.options.nested_write_transactions &&
!get_options().nested_write_transactions)
error::throw_exception(MDBX_INCOMPATIBLE);
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
}
__cold env_managed::env_managed(const ::std::filesystem::path &pathname,
@@ -1238,7 +1242,7 @@ __cold env_managed::env_managed(const ::std::filesystem::path &pathname,
if (op.options.nested_write_transactions &&
!get_options().nested_write_transactions)
error::throw_exception(MDBX_INCOMPATIBLE);
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
}
#endif /* MDBX_STD_FILESYSTEM_PATH */
@@ -1253,7 +1257,7 @@ __cold env_managed::env_managed(const ::std::wstring &pathname,
if (op.options.nested_write_transactions &&
!get_options().nested_write_transactions)
error::throw_exception(MDBX_INCOMPATIBLE);
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
}
__cold env_managed::env_managed(const ::std::wstring &pathname,
@@ -1269,7 +1273,7 @@ __cold env_managed::env_managed(const ::std::wstring &pathname,
if (op.options.nested_write_transactions &&
!get_options().nested_write_transactions)
error::throw_exception(MDBX_INCOMPATIBLE);
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
}
#endif /* Windows */
@@ -1283,7 +1287,7 @@ __cold env_managed::env_managed(const ::std::string &pathname,
if (op.options.nested_write_transactions &&
!get_options().nested_write_transactions)
error::throw_exception(MDBX_INCOMPATIBLE);
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
}
__cold env_managed::env_managed(const ::std::string &pathname,
@@ -1299,7 +1303,7 @@ __cold env_managed::env_managed(const ::std::string &pathname,
if (op.options.nested_write_transactions &&
!get_options().nested_write_transactions)
error::throw_exception(MDBX_INCOMPATIBLE);
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_INCOMPATIBLE);
}
//------------------------------------------------------------------------------
@@ -1314,25 +1318,25 @@ txn_managed txn::start_nested() {
}
txn_managed::~txn_managed() noexcept {
if (handle_)
error::success_or_panic(::mdbx_txn_abort(handle_), "mdbx::~txn",
"mdbx_txn_abort");
if (MDBX_UNLIKELY(handle_))
MDBX_CXX20_UNLIKELY error::success_or_panic(::mdbx_txn_abort(handle_),
"mdbx::~txn", "mdbx_txn_abort");
}
void txn_managed::abort() {
const error err = static_cast<MDBX_error_t>(::mdbx_txn_abort(handle_));
if (MDBX_LIKELY(err.code() != MDBX_THREAD_MISMATCH))
handle_ = nullptr;
MDBX_CXX20_LIKELY handle_ = nullptr;
if (MDBX_UNLIKELY(err.code() != MDBX_SUCCESS))
err.throw_exception();
MDBX_CXX20_UNLIKELY err.throw_exception();
}
void txn_managed::commit() {
const error err = static_cast<MDBX_error_t>(::mdbx_txn_commit(handle_));
if (MDBX_LIKELY(err.code() != MDBX_THREAD_MISMATCH))
handle_ = nullptr;
MDBX_CXX20_LIKELY handle_ = nullptr;
if (MDBX_UNLIKELY(err.code() != MDBX_SUCCESS))
err.throw_exception();
MDBX_CXX20_UNLIKELY err.throw_exception();
}
//------------------------------------------------------------------------------
@@ -1396,7 +1400,7 @@ __cold ::std::ostream &operator<<(::std::ostream &out, const slice &it) {
if (root.is_printable())
(out << "\"").write(root.char_ptr(), root.length()) << "\"";
else
out << root.base58_encode();
out << root.encode_base58();
if (root.length() < it.length())
out << "...";
}

View File

@@ -112,7 +112,7 @@ struct problem {
};
struct problem *problems_list;
uint64_t total_problems;
unsigned total_problems, data_tree_problems, gc_tree_problems;
static void MDBX_PRINTF_ARGS(1, 2) print(const char *msg, ...) {
if (!quiet) {
@@ -299,14 +299,20 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
const size_t nentries, const size_t payload_bytes,
const size_t header_bytes, const size_t unused_bytes) {
(void)ctx;
const bool is_gc_tree = dbi_name_or_tag == MDBX_PGWALK_GC;
if (deep > 42) {
problem_add("deep", deep, "too large", nullptr);
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
return MDBX_CORRUPTED /* avoid infinite loop/recursion */;
}
walk_dbi_t *dbi = pagemap_lookup_dbi(dbi_name_or_tag, false);
if (!dbi)
if (!dbi) {
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
return MDBX_ENOMEM;
}
const size_t page_bytes = payload_bytes + header_bytes + unused_bytes;
walk.pgcount += pgnumber;
@@ -319,13 +325,19 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
(unsigned)pagetype, deep);
pagetype_caption = "unknown";
dbi->pages.other += pgnumber;
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
break;
case MDBX_page_broken:
pagetype_caption = "broken";
dbi->pages.other += pgnumber;
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
break;
case MDBX_subpage_broken:
pagetype_caption = "broken-subpage";
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
break;
case MDBX_page_meta:
pagetype_caption = "meta";
@@ -375,17 +387,21 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
bool already_used = false;
for (unsigned n = 0; n < pgnumber; ++n) {
uint64_t spanpgno = pgno + n;
if (spanpgno >= alloc_pages)
if (spanpgno >= alloc_pages) {
problem_add("page", spanpgno, "wrong page-no",
"%s-page: %" PRIu64 " > %" PRIu64 ", deep %i",
pagetype_caption, spanpgno, alloc_pages, deep);
else if (walk.pagemap[spanpgno]) {
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
} else if (walk.pagemap[spanpgno]) {
walk_dbi_t *coll_dbi = &walk.dbi[walk.pagemap[spanpgno] - 1];
problem_add("page", spanpgno,
(branch && coll_dbi == dbi) ? "loop" : "already used",
"%s-page: by %s, deep %i", pagetype_caption, coll_dbi->name,
deep);
already_used = true;
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
} else {
walk.pagemap[spanpgno] = (short)(dbi - walk.dbi + 1);
dbi->pages.total += 1;
@@ -399,18 +415,26 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
if (MDBX_IS_ERROR(err)) {
problem_add("page", pgno, "invalid/corrupted", "%s-page", pagetype_caption);
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
} else {
if (unused_bytes > page_size)
if (unused_bytes > page_size) {
problem_add("page", pgno, "illegal unused-bytes",
"%s-page: %u < %" PRIuPTR " < %u", pagetype_caption, 0,
unused_bytes, envinfo.mi_dxb_pagesize);
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
}
if (header_bytes < (int)sizeof(long) ||
(size_t)header_bytes >= envinfo.mi_dxb_pagesize - sizeof(long))
(size_t)header_bytes >= envinfo.mi_dxb_pagesize - sizeof(long)) {
problem_add("page", pgno, "illegal header-length",
"%s-page: %" PRIuPTR " < %" PRIuPTR " < %" PRIuPTR,
pagetype_caption, sizeof(long), header_bytes,
envinfo.mi_dxb_pagesize - sizeof(long));
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
}
if (payload_bytes < 1) {
if (nentries > 1) {
problem_add("page", pgno, "zero size-of-entry",
@@ -420,12 +444,16 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
// LY: hush a misuse error
page_bytes = page_size;
} */
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
} else {
problem_add("page", pgno, "empty",
"%s-page: payload %" PRIuPTR " bytes, %" PRIuPTR
" entries, deep %i",
pagetype_caption, payload_bytes, nentries, deep);
dbi->pages.empty += 1;
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
}
}
@@ -438,6 +466,8 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
payload_bytes, unused_bytes, deep);
if (page_size > page_bytes)
dbi->lost_bytes += page_size - page_bytes;
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
} else {
dbi->payload_bytes += payload_bytes + header_bytes;
walk.total_payload_bytes += payload_bytes + header_bytes;
@@ -807,8 +837,9 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
bad_data = true;
}
if (prev_key.iov_base && !bad_data) {
if ((flags & MDBX_DUPFIXED) && prev_data.iov_len != data.iov_len) {
if (prev_key.iov_base) {
if (prev_data.iov_base && !bad_data && (flags & MDBX_DUPFIXED) &&
prev_data.iov_len != data.iov_len) {
problem_add("entry", record_count, "different data length",
"%" PRIuPTR " != %" PRIuPTR, prev_data.iov_len,
data.iov_len);
@@ -821,11 +852,11 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
++dups;
if ((flags & MDBX_DUPSORT) == 0) {
problem_add("entry", record_count, "duplicated entries", nullptr);
if (data.iov_len == prev_data.iov_len &&
if (prev_data.iov_base && data.iov_len == prev_data.iov_len &&
memcmp(data.iov_base, prev_data.iov_base, data.iov_len) == 0) {
problem_add("entry", record_count, "complete duplicate", nullptr);
}
} else if (!bad_data) {
} else if (!bad_data && prev_data.iov_base) {
cmp = mdbx_dcmp(txn, dbi_handle, &data, &prev_data);
if (cmp == 0) {
problem_add("entry", record_count, "complete duplicate", nullptr);
@@ -838,11 +869,6 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
problem_add("entry", record_count, "wrong order of entries", nullptr);
}
}
} else if (verbose) {
if (flags & MDBX_INTEGERKEY)
print(" - fixed key-size %" PRIuPTR "\n", key.iov_len);
if (flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED))
print(" - fixed data-size %" PRIuPTR "\n", data.iov_len);
}
if (handler) {
@@ -855,10 +881,17 @@ static int process_db(MDBX_dbi dbi_handle, char *dbi_name, visitor *handler,
key_bytes += key.iov_len;
data_bytes += data.iov_len;
if (!bad_key)
if (!bad_key) {
if (verbose && (flags & MDBX_INTEGERKEY) && !prev_key.iov_base)
print(" - fixed key-size %" PRIuPTR "\n", key.iov_len);
prev_key = key;
if (!bad_data)
}
if (!bad_data) {
if (verbose && (flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) &&
!prev_data.iov_base)
print(" - fixed data-size %" PRIuPTR "\n", data.iov_len);
prev_data = data;
}
rc = mdbx_cursor_get(mc, &key, &data, MDBX_NEXT);
}
if (rc != MDBX_NOTFOUND)
@@ -915,7 +948,7 @@ static __inline bool meta_ot(txnid_t txn_a, uint64_t sign_a, txnid_t txn_b,
static __inline bool meta_eq(txnid_t txn_a, uint64_t sign_a, txnid_t txn_b,
uint64_t sign_b) {
if (txn_a != txn_b)
if (!txn_a || txn_a != txn_b)
return false;
if (SIGN_IS_STEADY(sign_a) != SIGN_IS_STEADY(sign_b))
@@ -1033,7 +1066,7 @@ int main(int argc, char *argv[]) {
int rc;
char *prog = argv[0];
char *envname;
int problems_maindb = 0, problems_freedb = 0, problems_meta = 0;
unsigned problems_maindb = 0, problems_freedb = 0, problems_meta = 0;
bool write_locked = false;
bool turn_meta = false;
bool force_turn_meta = false;
@@ -1376,11 +1409,6 @@ int main(int argc, char *argv[]) {
"of may by large than the database itself,\n "
"until it will be closed or reopened in read-write mode.\n");
#endif
print(" - transactions: recent %" PRIu64 ", latter reader %" PRIu64
", lag %" PRIi64 "\n",
envinfo.mi_recent_txnid, envinfo.mi_latter_reader_txnid,
envinfo.mi_recent_txnid - envinfo.mi_latter_reader_txnid);
verbose_meta(0, envinfo.mi_meta0_txnid, envinfo.mi_meta0_sign,
envinfo.mi_bootid.meta0.x, envinfo.mi_bootid.meta0.y);
verbose_meta(1, envinfo.mi_meta1_txnid, envinfo.mi_meta1_sign,
@@ -1389,52 +1417,70 @@ int main(int argc, char *argv[]) {
envinfo.mi_bootid.meta2.x, envinfo.mi_bootid.meta2.y);
}
if (verbose > 1)
print(" - performs check for meta-pages clashes\n");
if (meta_eq(envinfo.mi_meta0_txnid, envinfo.mi_meta0_sign,
envinfo.mi_meta1_txnid, envinfo.mi_meta1_sign)) {
print(" ! meta-%d and meta-%d are clashed\n", 0, 1);
++problems_meta;
}
if (meta_eq(envinfo.mi_meta1_txnid, envinfo.mi_meta1_sign,
envinfo.mi_meta2_txnid, envinfo.mi_meta2_sign)) {
print(" ! meta-%d and meta-%d are clashed\n", 1, 2);
++problems_meta;
}
if (meta_eq(envinfo.mi_meta2_txnid, envinfo.mi_meta2_sign,
envinfo.mi_meta0_txnid, envinfo.mi_meta0_sign)) {
print(" ! meta-%d and meta-%d are clashed\n", 2, 0);
++problems_meta;
}
if (stuck_meta >= 0) {
if (verbose) {
print(" - skip checking meta-pages since the %u"
" is selected for verification\n",
stuck_meta);
print(" - transactions: recent %" PRIu64
", selected for verification %" PRIu64 ", lag %" PRIi64 "\n",
envinfo.mi_recent_txnid, get_meta_txnid(stuck_meta),
envinfo.mi_recent_txnid - get_meta_txnid(stuck_meta));
}
} else {
if (verbose > 1)
print(" - performs check for meta-pages clashes\n");
if (meta_eq(envinfo.mi_meta0_txnid, envinfo.mi_meta0_sign,
envinfo.mi_meta1_txnid, envinfo.mi_meta1_sign)) {
print(" ! meta-%d and meta-%d are clashed\n", 0, 1);
++problems_meta;
}
if (meta_eq(envinfo.mi_meta1_txnid, envinfo.mi_meta1_sign,
envinfo.mi_meta2_txnid, envinfo.mi_meta2_sign)) {
print(" ! meta-%d and meta-%d are clashed\n", 1, 2);
++problems_meta;
}
if (meta_eq(envinfo.mi_meta2_txnid, envinfo.mi_meta2_sign,
envinfo.mi_meta0_txnid, envinfo.mi_meta0_sign)) {
print(" ! meta-%d and meta-%d are clashed\n", 2, 0);
++problems_meta;
}
const unsigned steady_meta_id = meta_recent(true);
const uint64_t steady_meta_txnid = get_meta_txnid(steady_meta_id);
const unsigned weak_meta_id = meta_recent(false);
const uint64_t weak_meta_txnid = get_meta_txnid(weak_meta_id);
if (envflags & MDBX_EXCLUSIVE) {
if (verbose > 1)
print(" - performs full check recent-txn-id with meta-pages\n");
if (steady_meta_txnid != envinfo.mi_recent_txnid) {
print(" ! steady meta-%d txn-id mismatch recent-txn-id (%" PRIi64
" != %" PRIi64 ")\n",
steady_meta_id, steady_meta_txnid, envinfo.mi_recent_txnid);
++problems_meta;
const unsigned steady_meta_id = meta_recent(true);
const uint64_t steady_meta_txnid = get_meta_txnid(steady_meta_id);
const unsigned weak_meta_id = meta_recent(false);
const uint64_t weak_meta_txnid = get_meta_txnid(weak_meta_id);
if (envflags & MDBX_EXCLUSIVE) {
if (verbose > 1)
print(" - performs full check recent-txn-id with meta-pages\n");
if (steady_meta_txnid != envinfo.mi_recent_txnid) {
print(" ! steady meta-%d txn-id mismatch recent-txn-id (%" PRIi64
" != %" PRIi64 ")\n",
steady_meta_id, steady_meta_txnid, envinfo.mi_recent_txnid);
++problems_meta;
}
} else if (write_locked) {
if (verbose > 1)
print(" - performs lite check recent-txn-id with meta-pages (not a "
"monopolistic mode)\n");
if (weak_meta_txnid != envinfo.mi_recent_txnid) {
print(" ! weak meta-%d txn-id mismatch recent-txn-id (%" PRIi64
" != %" PRIi64 ")\n",
weak_meta_id, weak_meta_txnid, envinfo.mi_recent_txnid);
++problems_meta;
}
} else if (verbose) {
print(" - skip check recent-txn-id with meta-pages (monopolistic or "
"read-write mode only)\n");
}
} else if (write_locked) {
if (verbose > 1)
print(" - performs lite check recent-txn-id with meta-pages (not a "
"monopolistic mode)\n");
if (weak_meta_txnid != envinfo.mi_recent_txnid) {
print(" ! weak meta-%d txn-id mismatch recent-txn-id (%" PRIi64
" != %" PRIi64 ")\n",
weak_meta_id, weak_meta_txnid, envinfo.mi_recent_txnid);
++problems_meta;
}
} else if (verbose) {
print(" - skip check recent-txn-id with meta-pages (monopolistic or "
"read-write mode only)\n");
total_problems += problems_meta;
if (verbose)
print(" - transactions: recent %" PRIu64 ", latter reader %" PRIu64
", lag %" PRIi64 "\n",
envinfo.mi_recent_txnid, envinfo.mi_latter_reader_txnid,
envinfo.mi_recent_txnid - envinfo.mi_latter_reader_txnid);
}
total_problems += problems_meta;
if (!dont_traversal) {
struct problem *saved_list;
@@ -1545,8 +1591,19 @@ int main(int argc, char *argv[]) {
if (!verbose)
print("Iterating DBIs...\n");
problems_maindb = process_db(~0u, /* MAIN_DBI */ nullptr, nullptr, false);
problems_freedb = process_db(FREE_DBI, "@GC", handle_freedb, false);
if (data_tree_problems) {
print("Skip processing %s since tree is corrupted (%u problems)\n", "@MAIN",
data_tree_problems);
problems_maindb = data_tree_problems;
} else
problems_maindb = process_db(~0u, /* MAIN_DBI */ nullptr, nullptr, false);
if (gc_tree_problems) {
print("Skip processing %s since tree is corrupted (%u problems)\n", "@GC",
gc_tree_problems);
problems_freedb = gc_tree_problems;
} else
problems_freedb = process_db(FREE_DBI, "@GC", handle_freedb, false);
if (verbose) {
uint64_t value = envinfo.mi_mapsize / envinfo.mi_dxb_pagesize;
@@ -1603,7 +1660,7 @@ int main(int argc, char *argv[]) {
if (rc == 0 && total_problems == 1 && problems_meta == 1 && !dont_traversal &&
(envflags & MDBX_RDONLY) == 0 && !only_subdb && stuck_meta < 0 &&
steady_meta_txnid < envinfo.mi_recent_txnid) {
get_meta_txnid(meta_recent(true)) < envinfo.mi_recent_txnid) {
print("Perform sync-to-disk for make steady checkpoint at txn-id #%" PRIi64
"\n",
envinfo.mi_recent_txnid);
@@ -1673,8 +1730,8 @@ bailout:
#endif /* !WINDOWS */
if (total_problems) {
print("Total %" PRIu64 " error%s detected, elapsed %.3f seconds.\n",
total_problems, (total_problems > 1) ? "s are" : " is", elapsed);
print("Total %u error%s detected, elapsed %.3f seconds.\n", total_problems,
(total_problems > 1) ? "s are" : " is", elapsed);
if (problems_meta || problems_maindb || problems_freedb)
return EXIT_FAILURE_CHECK_MAJOR;
return EXIT_FAILURE_CHECK_MINOR;

View File

@@ -39,18 +39,18 @@
#else
#define MDBX_ENV_CHECKPID 1
#endif
#define MDBX_ENV_CHECKPID_CONFIG "AUTO=" STRINGIFY(MDBX_ENV_CHECKPID)
#define MDBX_ENV_CHECKPID_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_ENV_CHECKPID)
#else
#define MDBX_ENV_CHECKPID_CONFIG STRINGIFY(MDBX_ENV_CHECKPID)
#define MDBX_ENV_CHECKPID_CONFIG MDBX_STRINGIFY(MDBX_ENV_CHECKPID)
#endif /* MDBX_ENV_CHECKPID */
/** Controls checking transaction owner thread against misuse transactions from
* other threads. */
#ifndef MDBX_TXN_CHECKOWNER
#define MDBX_TXN_CHECKOWNER 1
#define MDBX_TXN_CHECKOWNER_CONFIG "AUTO=" STRINGIFY(MDBX_TXN_CHECKOWNER)
#define MDBX_TXN_CHECKOWNER_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_TXN_CHECKOWNER)
#else
#define MDBX_TXN_CHECKOWNER_CONFIG STRINGIFY(MDBX_TXN_CHECKOWNER)
#define MDBX_TXN_CHECKOWNER_CONFIG MDBX_STRINGIFY(MDBX_TXN_CHECKOWNER)
#endif /* MDBX_TXN_CHECKOWNER */
/** Does a system have battery-backed Real-Time Clock or just a fake. */
@@ -61,9 +61,9 @@
#else
#define MDBX_TRUST_RTC 1
#endif
#define MDBX_TRUST_RTC_CONFIG "AUTO=" STRINGIFY(MDBX_TRUST_RTC)
#define MDBX_TRUST_RTC_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_TRUST_RTC)
#else
#define MDBX_TRUST_RTC_CONFIG STRINGIFY(MDBX_TRUST_RTC)
#define MDBX_TRUST_RTC_CONFIG MDBX_STRINGIFY(MDBX_TRUST_RTC)
#endif /* MDBX_TRUST_RTC */
/** Controls online database auto-compactification during write-transactions. */
@@ -238,9 +238,9 @@
#else
#define MDBX_LOCKING MDBX_LOCKING_SYSV
#endif
#define MDBX_LOCKING_CONFIG "AUTO=" STRINGIFY(MDBX_LOCKING)
#define MDBX_LOCKING_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_LOCKING)
#else
#define MDBX_LOCKING_CONFIG STRINGIFY(MDBX_LOCKING)
#define MDBX_LOCKING_CONFIG MDBX_STRINGIFY(MDBX_LOCKING)
#endif /* MDBX_LOCKING */
#endif /* !Windows */
@@ -253,9 +253,9 @@
#else
#define MDBX_USE_OFDLOCKS 0
#endif
#define MDBX_USE_OFDLOCKS_CONFIG "AUTO=" STRINGIFY(MDBX_USE_OFDLOCKS)
#define MDBX_USE_OFDLOCKS_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_USE_OFDLOCKS)
#else
#define MDBX_USE_OFDLOCKS_CONFIG STRINGIFY(MDBX_USE_OFDLOCKS)
#define MDBX_USE_OFDLOCKS_CONFIG MDBX_STRINGIFY(MDBX_USE_OFDLOCKS)
#endif /* MDBX_USE_OFDLOCKS */
/** Advanced: Using sendfile() syscall (autodetection by default). */
@@ -326,9 +326,9 @@
#else
#define MDBX_64BIT_ATOMIC 0
#endif
#define MDBX_64BIT_ATOMIC_CONFIG "AUTO=" STRINGIFY(MDBX_64BIT_ATOMIC)
#define MDBX_64BIT_ATOMIC_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_64BIT_ATOMIC)
#else
#define MDBX_64BIT_ATOMIC_CONFIG STRINGIFY(MDBX_64BIT_ATOMIC)
#define MDBX_64BIT_ATOMIC_CONFIG MDBX_STRINGIFY(MDBX_64BIT_ATOMIC)
#endif /* MDBX_64BIT_ATOMIC */
#ifndef MDBX_64BIT_CAS
@@ -355,9 +355,9 @@
#else
#define MDBX_64BIT_CAS MDBX_64BIT_ATOMIC
#endif
#define MDBX_64BIT_CAS_CONFIG "AUTO=" STRINGIFY(MDBX_64BIT_CAS)
#define MDBX_64BIT_CAS_CONFIG "AUTO=" MDBX_STRINGIFY(MDBX_64BIT_CAS)
#else
#define MDBX_64BIT_CAS_CONFIG STRINGIFY(MDBX_64BIT_CAS)
#define MDBX_64BIT_CAS_CONFIG MDBX_STRINGIFY(MDBX_64BIT_CAS)
#endif /* MDBX_64BIT_CAS */
#ifndef MDBX_UNALIGNED_OK

View File

@@ -217,7 +217,7 @@ __extern_C void __assert(const char *function, const char *file, int line,
#if !defined(__ANDROID_API__) || MDBX_DEBUG
void __cold mdbx_assert_fail(const MDBX_env *env, const char *msg,
__cold void mdbx_assert_fail(const MDBX_env *env, const char *msg,
const char *func, int line) {
#if MDBX_DEBUG
if (env && env->me_assert_func) {
@@ -1377,6 +1377,36 @@ static int mdbx_check_fs_local(mdbx_filehandle_t handle, int flags) {
return MDBX_SUCCESS;
}
static int check_mmap_limit(const size_t limit) {
const bool should_check =
#if defined(__SANITIZE_ADDRESS__)
true;
#else
RUNNING_ON_VALGRIND;
#endif /* __SANITIZE_ADDRESS__ */
if (should_check) {
intptr_t pagesize, total_ram_pages, avail_ram_pages;
int err =
mdbx_get_sysraminfo(&pagesize, &total_ram_pages, &avail_ram_pages);
if (unlikely(err != MDBX_SUCCESS))
return err;
const int log2page = log2n_powerof2(pagesize);
if ((limit >> (log2page + 7)) > (size_t)total_ram_pages ||
(limit >> (log2page + 6)) > (size_t)avail_ram_pages) {
mdbx_error(
"%s (%zu pages) is too large for available (%zu pages) or total "
"(%zu pages) system RAM",
"database upper size limit", limit >> log2page, avail_ram_pages,
total_ram_pages);
return MDBX_TOO_LARGE;
}
}
return MDBX_SUCCESS;
}
MDBX_INTERNAL_FUNC int mdbx_mmap(const int flags, mdbx_mmap_t *map,
const size_t size, const size_t limit,
const unsigned options) {
@@ -1384,34 +1414,34 @@ MDBX_INTERNAL_FUNC int mdbx_mmap(const int flags, mdbx_mmap_t *map,
map->limit = 0;
map->current = 0;
map->address = nullptr;
map->filesize = 0;
#if defined(_WIN32) || defined(_WIN64)
map->section = NULL;
map->filesize = 0;
#endif /* Windows */
int err = mdbx_check_fs_local(map->fd, flags);
if (unlikely(err != MDBX_SUCCESS))
return err;
err = check_mmap_limit(limit);
if (unlikely(err != MDBX_SUCCESS))
return err;
if ((flags & MDBX_RDONLY) == 0 && (options & MMAP_OPTION_TRUNCATE) != 0) {
err = mdbx_ftruncate(map->fd, size);
if (err != MDBX_SUCCESS)
return err;
#if defined(_WIN32) || defined(_WIN64)
map->filesize = size;
#else
#if !(defined(_WIN32) || defined(_WIN64))
map->current = size;
#endif /* ! Windows */
#endif /* !Windows */
} else {
uint64_t filesize = 0;
err = mdbx_filesize(map->fd, &filesize);
err = mdbx_filesize(map->fd, &map->filesize);
if (err != MDBX_SUCCESS)
return err;
#if defined(_WIN32) || defined(_WIN64)
map->filesize = filesize;
#else
map->current = (filesize > limit) ? limit : (size_t)filesize;
#endif /* ! Windows */
#if !(defined(_WIN32) || defined(_WIN64))
map->current = (map->filesize > limit) ? limit : (size_t)map->filesize;
#endif /* !Windows */
}
#if defined(_WIN32) || defined(_WIN64)
@@ -1508,7 +1538,7 @@ MDBX_INTERNAL_FUNC int mdbx_mmap(const int flags, mdbx_mmap_t *map,
#endif /* ! Windows */
VALGRIND_MAKE_MEM_DEFINED(map->address, map->current);
ASAN_UNPOISON_MEMORY_REGION(map->address, map->current);
MDBX_ASAN_UNPOISON_MEMORY_REGION(map->address, map->current);
return MDBX_SUCCESS;
}
@@ -1517,7 +1547,10 @@ MDBX_INTERNAL_FUNC int mdbx_munmap(mdbx_mmap_t *map) {
/* Unpoisoning is required for ASAN to avoid false-positive diagnostic
* when this memory will re-used by malloc or another mmapping.
* See https://github.com/erthink/libmdbx/pull/93#issuecomment-613687203 */
ASAN_UNPOISON_MEMORY_REGION(map->address, map->limit);
MDBX_ASAN_UNPOISON_MEMORY_REGION(map->address,
(map->filesize && map->filesize < map->limit)
? map->filesize
: map->limit);
#if defined(_WIN32) || defined(_WIN64)
if (map->section)
NtClose(map->section);
@@ -1535,8 +1568,8 @@ MDBX_INTERNAL_FUNC int mdbx_munmap(mdbx_mmap_t *map) {
return MDBX_SUCCESS;
}
MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t size,
size_t limit, const bool may_move) {
MDBX_INTERNAL_FUNC int mdbx_mresize(const int flags, mdbx_mmap_t *map,
size_t size, size_t limit) {
assert(size <= limit);
#if defined(_WIN32) || defined(_WIN64)
assert(size != map->current || limit != map->limit || size < map->filesize);
@@ -1559,6 +1592,10 @@ MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t size,
}
if (limit > map->limit) {
err = check_mmap_limit(limit);
if (unlikely(err != MDBX_SUCCESS))
return err;
/* check ability of address space for growth before unmap */
PVOID BaseAddress = (PBYTE)map->address + map->limit;
SIZE_T RegionSize = limit - map->limit;
@@ -1580,6 +1617,13 @@ MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t size,
* - change size of mapped view;
* - extend read-only mapping;
* Therefore we should unmap/map entire section. */
if ((flags & MDBX_MRESIZE_MAY_UNMAP) == 0)
return MDBX_RESULT_TRUE;
/* Unpoisoning is required for ASAN to avoid false-positive diagnostic
* when this memory will re-used by malloc or another mmapping.
* See https://github.com/erthink/libmdbx/pull/93#issuecomment-613687203 */
MDBX_ASAN_UNPOISON_MEMORY_REGION(map->address, map->limit);
status = NtUnmapViewOfSection(GetCurrentProcess(), map->address);
if (!NT_SUCCESS(status))
return ntstatus2errcode(status);
@@ -1615,7 +1659,7 @@ retry_file_and_section:
if (status != (NTSTATUS) /* STATUS_CONFLICTING_ADDRESSES */ 0xC0000018)
goto bailout_ntstatus /* no way to recovery */;
if (may_move)
if (flags & MDBX_MRESIZE_MAY_MOVE)
/* the base address could be changed */
map->address = NULL;
}
@@ -1673,7 +1717,7 @@ retry_mapview:;
if (!NT_SUCCESS(status)) {
if (status == (NTSTATUS) /* STATUS_CONFLICTING_ADDRESSES */ 0xC0000018 &&
map->address && may_move) {
map->address && (flags & MDBX_MRESIZE_MAY_MOVE) != 0) {
/* try remap at another base address */
map->address = NULL;
goto retry_mapview;
@@ -1684,7 +1728,7 @@ retry_mapview:;
if (map->address && (size != map->current || limit != map->limit)) {
/* try remap with previously size and limit,
* but will return MDBX_UNABLE_EXTEND_MAPSIZE on success */
rc = MDBX_UNABLE_EXTEND_MAPSIZE;
rc = (limit > map->limit) ? MDBX_UNABLE_EXTEND_MAPSIZE : MDBX_RESULT_TRUE;
size = map->current;
ReservedSize = limit = map->limit;
goto retry_file_and_section;
@@ -1700,40 +1744,62 @@ retry_mapview:;
#else /* Windows */
uint64_t filesize = 0;
int rc = mdbx_filesize(map->fd, &filesize);
map->filesize = 0;
int rc = mdbx_filesize(map->fd, &map->filesize);
if (rc != MDBX_SUCCESS)
return rc;
if (flags & MDBX_RDONLY) {
map->current = (filesize > limit) ? limit : (size_t)filesize;
map->current = (map->filesize > limit) ? limit : (size_t)map->filesize;
if (map->current != size)
rc = MDBX_UNABLE_EXTEND_MAPSIZE;
} else if (filesize != size) {
rc = mdbx_ftruncate(map->fd, size);
if (rc != MDBX_SUCCESS)
return rc;
rc =
(size > map->current) ? MDBX_UNABLE_EXTEND_MAPSIZE : MDBX_RESULT_TRUE;
} else {
if (map->filesize != size) {
rc = mdbx_ftruncate(map->fd, size);
if (rc != MDBX_SUCCESS)
return rc;
map->filesize = size;
}
if (map->current > size) {
/* Clearing asan's bitmask for the region which released in shrinking,
* since:
* - after the shrinking we will get an exception when accessing
* this region and (therefore) do not need the help of ASAN.
* - this allows us to clear the mask only within the file size
* when closing the mapping. */
MDBX_ASAN_UNPOISON_MEMORY_REGION(
(char *)map->address + size,
((map->current < map->limit) ? map->current : map->limit) - size);
}
map->current = size;
}
if (limit == map->limit)
return MDBX_SUCCESS;
return rc;
if (limit < map->limit) {
/* unmap an excess at end of mapping. */
// coverity[offset_free : FALSE]
if (unlikely(munmap(map->dxb + limit, map->limit - limit)))
return errno;
map->limit = limit;
return MDBX_SUCCESS;
return rc;
}
int err = check_mmap_limit(limit);
if (unlikely(err != MDBX_SUCCESS))
return err;
assert(limit > map->limit);
uint8_t *ptr = MAP_FAILED;
#if defined(MREMAP_MAYMOVE)
ptr = mremap(map->address, map->limit, limit, may_move ? MREMAP_MAYMOVE : 0);
ptr = mremap(map->address, map->limit, limit,
(flags & MDBX_MRESIZE_MAY_MOVE) ? MREMAP_MAYMOVE : 0);
if (ptr == MAP_FAILED) {
const int err = errno;
err = errno;
switch (err) {
default:
return err;
@@ -1764,7 +1830,7 @@ retry_mapview:;
return errno;
ptr = MAP_FAILED;
} else {
const int err = errno;
err = errno;
switch (err) {
default:
return err;
@@ -1780,7 +1846,7 @@ retry_mapview:;
if (ptr == MAP_FAILED) {
/* unmap and map again whole region */
if (!may_move) {
if ((flags & MDBX_MRESIZE_MAY_UNMAP) == 0) {
/* TODO: Perhaps here it is worth to implement suspend/resume threads
* and perform unmap/map as like for Windows. */
return MDBX_UNABLE_EXTEND_MAPSIZE;
@@ -1789,16 +1855,44 @@ retry_mapview:;
if (unlikely(munmap(map->address, map->limit)))
return errno;
ptr = mmap(map->address, limit, mmap_prot, mmap_flags, map->fd, 0);
// coverity[pass_freed_arg : FALSE]
ptr = mmap(map->address, limit, mmap_prot,
(flags & MDBX_MRESIZE_MAY_MOVE)
? mmap_flags
: mmap_flags | (MAP_FIXED_NOREPLACE ? MAP_FIXED_NOREPLACE
: MAP_FIXED),
map->fd, 0);
if (MAP_FIXED_NOREPLACE != 0 && MAP_FIXED_NOREPLACE != MAP_FIXED &&
unlikely(ptr == MAP_FAILED) && !(flags & MDBX_MRESIZE_MAY_MOVE) &&
errno == /* kernel don't support MAP_FIXED_NOREPLACE */ EINVAL)
// coverity[pass_freed_arg : FALSE]
ptr = mmap(map->address, limit, mmap_prot, mmap_flags | MAP_FIXED,
map->fd, 0);
if (unlikely(ptr == MAP_FAILED)) {
ptr = mmap(map->address, map->limit, mmap_prot, mmap_flags, map->fd, 0);
/* try to restore prev mapping */
// coverity[pass_freed_arg : FALSE]
ptr = mmap(map->address, map->limit, mmap_prot,
(flags & MDBX_MRESIZE_MAY_MOVE)
? mmap_flags
: mmap_flags | (MAP_FIXED_NOREPLACE ? MAP_FIXED_NOREPLACE
: MAP_FIXED),
map->fd, 0);
if (MAP_FIXED_NOREPLACE != 0 && MAP_FIXED_NOREPLACE != MAP_FIXED &&
unlikely(ptr == MAP_FAILED) && !(flags & MDBX_MRESIZE_MAY_MOVE) &&
errno == /* kernel don't support MAP_FIXED_NOREPLACE */ EINVAL)
// coverity[pass_freed_arg : FALSE]
ptr = mmap(map->address, map->limit, mmap_prot, mmap_flags | MAP_FIXED,
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 mmapping.
* See https://github.com/erthink/libmdbx/pull/93#issuecomment-613687203
*/
ASAN_UNPOISON_MEMORY_REGION(map->address, map->limit);
MDBX_ASAN_UNPOISON_MEMORY_REGION(
map->address,
(map->current < map->limit) ? map->current : map->limit);
map->limit = 0;
map->current = 0;
map->address = nullptr;
@@ -1814,12 +1908,12 @@ retry_mapview:;
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 mmapping.
* See https://github.com/erthink/libmdbx/pull/93#issuecomment-613687203
*/
ASAN_UNPOISON_MEMORY_REGION(map->address, map->limit);
* See https://github.com/erthink/libmdbx/pull/93#issuecomment-613687203 */
MDBX_ASAN_UNPOISON_MEMORY_REGION(
map->address, (map->current < map->limit) ? map->current : map->limit);
VALGRIND_MAKE_MEM_DEFINED(ptr, map->current);
ASAN_UNPOISON_MEMORY_REGION(ptr, map->current);
MDBX_ASAN_UNPOISON_MEMORY_REGION(ptr, map->current);
map->address = ptr;
}
map->limit = limit;
@@ -1841,7 +1935,7 @@ retry_mapview:;
/*----------------------------------------------------------------------------*/
MDBX_INTERNAL_FUNC __cold void mdbx_osal_jitter(bool tiny) {
__cold MDBX_INTERNAL_FUNC void mdbx_osal_jitter(bool tiny) {
for (;;) {
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \
defined(__x86_64__)
@@ -1869,7 +1963,7 @@ MDBX_INTERNAL_FUNC __cold void mdbx_osal_jitter(bool tiny) {
#elif defined(__APPLE__) || defined(__MACH__)
#include <mach/mach_time.h>
#elif defined(__linux__) || defined(__gnu_linux__)
static __cold clockid_t choice_monoclock(void) {
__cold static clockid_t choice_monoclock(void) {
struct timespec probe;
#if defined(CLOCK_BOOTTIME)
if (clock_gettime(CLOCK_BOOTTIME, &probe) == 0)
@@ -2061,7 +2155,7 @@ static LSTATUS mdbx_RegGetValue(HKEY hKey, LPCSTR lpSubKey, LPCSTR lpValue,
}
#endif
MDBX_MAYBE_UNUSED static __cold bool
__cold MDBX_MAYBE_UNUSED static bool
bootid_parse_uuid(bin128_t *s, const void *p, const size_t n) {
if (n > 31) {
unsigned bits = 0;
@@ -2177,7 +2271,7 @@ __cold MDBX_INTERNAL_FUNC bin128_t mdbx_osal_bootid(void) {
if (mdbx_RegGetValue(HKEY_LOCAL_MACHINE, HKLM_MicrosoftCryptography,
"MachineGuid", &buf.MachineGuid,
&len) == ERROR_SUCCESS &&
len > 42 && len < sizeof(buf))
len < sizeof(buf))
got_machineid = bootid_parse_uuid(&bin, &buf.MachineGuid, len);
if (!got_machineid) {
@@ -2241,12 +2335,16 @@ __cold MDBX_INTERNAL_FUNC bin128_t mdbx_osal_bootid(void) {
0x03 /* SystemTmeOfDayInformation */, &buf.SysTimeOfDayInfo,
sizeof(buf.SysTimeOfDayInfo), &len);
if (NT_SUCCESS(status) &&
len >= offsetof(union buf, SysTimeOfDayInfoHacked.BootTime) +
sizeof(buf.SysTimeOfDayInfoHacked.BootTime) &&
len >= offsetof(union buf, SysTimeOfDayInfoHacked.BootTimeBias) +
sizeof(buf.SysTimeOfDayInfoHacked.BootTimeBias) &&
buf.SysTimeOfDayInfoHacked.BootTime.QuadPart) {
bootid_collect(&bin, &buf.SysTimeOfDayInfoHacked.BootTime,
sizeof(buf.SysTimeOfDayInfoHacked.BootTime));
got_boottime = true;
const uint64_t UnbiasedBootTime =
buf.SysTimeOfDayInfoHacked.BootTime.QuadPart -
buf.SysTimeOfDayInfoHacked.BootTimeBias;
if (UnbiasedBootTime) {
bootid_collect(&bin, &UnbiasedBootTime, sizeof(UnbiasedBootTime));
got_boottime = true;
}
}
if (!got_boottime) {

View File

@@ -460,8 +460,8 @@ typedef struct mdbx_mmap_param {
mdbx_filehandle_t fd;
size_t limit; /* mapping length, but NOT a size of file nor DB */
size_t current; /* mapped region size, i.e. the size of file and DB */
#if defined(_WIN32) || defined(_WIN64)
uint64_t filesize /* in-process cache of a file size */;
#if defined(_WIN32) || defined(_WIN64)
HANDLE section; /* memory-mapped section handle */
#endif
} mdbx_mmap_t;
@@ -695,8 +695,10 @@ MDBX_INTERNAL_FUNC int mdbx_mmap(const int flags, mdbx_mmap_t *map,
const size_t must, const size_t limit,
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, const bool may_move);
#define MDBX_MRESIZE_MAY_MOVE 0x00000100
#define MDBX_MRESIZE_MAY_UNMAP 0x00000200
MDBX_INTERNAL_FUNC int mdbx_mresize(const int flags, mdbx_mmap_t *map,
size_t size, size_t limit);
#if defined(_WIN32) || defined(_WIN64)
typedef struct {
unsigned limit, count;

View File

@@ -8,7 +8,7 @@
#error "API version mismatch! Had `git fetch --tags` done?"
#endif
static const char sourcery[] = STRINGIFY(MDBX_BUILD_SOURCERY);
static const char sourcery[] = MDBX_STRINGIFY(MDBX_BUILD_SOURCERY);
__dll_export
#ifdef __attribute_used__

View File

@@ -1,7 +1,7 @@
enable_language(CXX)
include(../cmake/compiler.cmake)
add_executable(mdbx_test
set(LIBMDBX_TEST_SOURCES
base.h
cases.cc
chrono.cc
@@ -30,6 +30,12 @@ add_executable(mdbx_test
nested.cc
)
if(NOT MDBX_BUILD_CXX)
list(APPEND LIBMDBX_TEST_SOURCES "${MDBX_SOURCE_DIR}/mdbx.c++" ../mdbx.h++)
endif()
add_executable(mdbx_test ${LIBMDBX_TEST_SOURCES})
if(MDBX_CXX_STANDARD)
set_target_properties(mdbx_test PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)

View File

@@ -86,7 +86,7 @@
#define MDBX_INTERNAL_FUNC
#define MDBX_INTERNAL_VAR extern
#define xMDBX_TOOLS /* Avoid using internal mdbx_assert() */
#include "../mdbx.h"
#include "../mdbx.h++"
#include "../src/defs.h"
#include "../src/osal.h"

View File

@@ -310,6 +310,7 @@ const struct option_verb mode_bits[] = {
{"lifo", unsigned(MDBX_LIFORECLAIM)},
{"perturb", unsigned(MDBX_PAGEPERTURB)},
{"accede", unsigned(MDBX_ACCEDE)},
{"exclusive", unsigned(MDBX_EXCLUSIVE)},
{nullptr, 0}};
const struct option_verb table_bits[] = {

View File

@@ -243,23 +243,25 @@ void maker::seek2end(serial_t &serial) const {
serial = actor_params::serial_mask(mapping.width) - 1;
}
bool maker::increment(serial_t &serial, int delta) const {
bool maker::increment(serial_t &serial, int64_t delta) const {
if (serial > actor_params::serial_mask(mapping.width)) {
log_extra("keygen-increment: %" PRIu64 " > %" PRIu64 ", overflow", serial,
actor_params::serial_mask(mapping.width));
return false;
}
serial_t target = serial + (int64_t)delta;
serial_t target = serial + delta;
if (target > actor_params::serial_mask(mapping.width) ||
((delta > 0) ? target < serial : target > serial)) {
log_extra("keygen-increment: %" PRIu64 "%-d => %" PRIu64 ", overflow",
log_extra("keygen-increment: %" PRIu64 "%-" PRId64 " => %" PRIu64
", overflow",
serial, delta, target);
return false;
}
log_extra("keygen-increment: %" PRIu64 "%-d => %" PRIu64 ", continue", serial,
delta, target);
log_extra("keygen-increment: %" PRIu64 "%-" PRId64 " => %" PRIu64
", continue",
serial, delta, target);
serial = target;
return true;
}

View File

@@ -130,8 +130,8 @@ public:
bool is_unordered() const;
void seek2end(serial_t &serial) const;
bool increment(serial_t &serial, int delta) const;
bool increment_key_part(serial_t &serial, int delta,
bool increment(serial_t &serial, int64_t delta) const;
bool increment_key_part(serial_t &serial, int64_t delta,
bool reset_value_part = true) const {
if (reset_value_part) {
serial_t value_part_bits = ((serial_t(1) << mapping.split) - 1);
@@ -139,7 +139,7 @@ public:
if (delta >= 0)
serial &= ~value_part_bits;
}
return increment(serial, delta << mapping.split);
return increment(serial, int64_t(uint64_t(delta) << mapping.split));
}
};

View File

@@ -1,36 +1,121 @@
#!/usr/bin/env bash
if ! which make cc c++ tee lz4 >/dev/null; then
echo "Please install the following prerequisites: make cc c++ tee lz4 banner" >&2
if ! which make cc c++ tee >/dev/null; then
echo "Please install the following prerequisites: make cc c++ tee banner" >&2
exit 1
fi
LIST=basic
FROM=1
UPTO=9999999
MONITOR=
LOOPS=
SKIP_MAKE=no
BANNER="$(which banner 2>/dev/null | echo echo)"
UNAME="$(uname -s 2>/dev/null || echo Unknown)"
set -euo pipefail
DB_UPTO_MB=17408
## NOTE: Valgrind could produce some false-positive warnings
## in multi-process environment with shared memory.
## For instance, when the process "A" explicitly marks a memory
## region as "undefined", the process "B" fill it,
## and after this process "A" read such region, etc.
#VALGRIND="valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt"
while [ -n "$1" ]
do
case "$1" in
--help)
echo "--multi Engage multi-process test scenario (default)"
echo "--single Execute series of single-process tests (for QEMU, etc)"
echo "--with-valgrind Run tests under Valgrind's memcheck tool"
echo "--skip-make Don't (re)build libmdbx and test's executable"
echo "--from NN Start iterating from the NN ops per test case"
echo "--upto NN Don't run tests with more than NN ops per test case"
echo "--loops NN Stop after the NN loops"
echo "--dir PATH Specifies directory for test DB and other files (it will be cleared)"
echo "--db-upto-mb NN Limits upper size of test DB to the NN megabytes"
echo "--help Print this usage help and exit"
exit -2
;;
--multi)
LIST=basic
;;
--single)
LIST="--nested --hill --append --ttl --copy"
;;
--with-valgrind)
echo " NOTE: Valgrind could produce some false-positive warnings"
echo " in multi-process environment with shared memory."
echo " For instance, when the process 'A' explicitly marks a memory"
echo " region as 'undefined', the process 'B' fill it,"
echo " and after this process 'A' read such region, etc."
MONITOR="valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt"
rm -f valgrind-*.log
;;
--skip-make)
SKIP_MAKE=yes
;;
--from)
FROM=$(($2))
if [ -z "$FROM" -o "$FROM" -lt 1 ]; then
echo "Invalid value '$FROM' for --from option"
exit -2
fi
shift
;;
--upto)
UPTO=$(($2))
if [ -z "$UPTO" -o "$UPTO" -lt 1 ]; then
echo "Invalid value '$UPTO' for --upto option"
exit -2
fi
shift
;;
--loops)
LOOPS=$(($2))
if [ -z "$LOOPS" -o "$LOOPS" -lt 1 -o "$LOOPS" -gt 99 ]; then
echo "Invalid value '$LOOPS' for --loops option"
exit -2
fi
shift
;;
--dir)
TESTDB_DIR="$2"
if [ -z "$TESTDB_DIR" ]; then
echo "Invalid value '$TESTDB_DIR' for --dir option"
exit -2
fi
shift
;;
--db-upto-mb)
DB_UPTO_MB=$(($2))
if [ -z "$DB_UPTO_MB" -o "$DB_UPTO_MB" -lt 1 -o "$DB_UPTO_MB" -gt 4194304 ]; then
echo "Invalid value '$DB_UPTO_MB' for --db-upto-mb option"
exit -2
fi
shift
;;
*)
echo "Unknown option '$1'"
exit -2
;;
esac
shift
done
set -euo pipefail
if [ -z "$MONITOR" ]; then
if which time >/dev/null 2>/dev/null; then
MONITOR=$(which time)
if $MONITOR -o /dev/stdout true >/dev/null 2>/dev/null; then
MONITOR="$MONITOR -o /dev/stdout"
fi
fi
export MALLOC_CHECK_=7 MALLOC_PERTURB_=42
fi
###############################################################################
# 1. clean data from prev runs and examine available RAM
if [[ -v VALGRIND && ! -z "$VALGRIND" ]]; then
rm -f valgrind-*.log
else
VALGRIND=time
export MALLOC_CHECK_=7 MALLOC_PERTURB_=42
fi
WANNA_MOUNT=0
case ${UNAME} in
Linux)
MAKE=make
if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
for old_test_dir in $(ls -d /dev/shm/mdbx-test.[0-9]*); do
if [ -z "${TESTDB_DIR:-}" ]; then
for old_test_dir in $(ls -d /dev/shm/mdbx-test.[0-9]* 2>/dev/null); do
rm -rf $old_test_dir
done
TESTDB_DIR="/dev/shm/mdbx-test.$$"
@@ -46,8 +131,8 @@ case ${UNAME} in
FreeBSD)
MAKE=gmake
if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
for old_test_dir in $(ls -d /tmp/mdbx-test.[0-9]*); do
if [ -z "${TESTDB_DIR:-}" ]; then
for old_test_dir in $(ls -d /tmp/mdbx-test.[0-9]* 2>/dev/null); do
umount $old_test_dir && rm -r $old_test_dir
done
TESTDB_DIR="/tmp/mdbx-test.$$"
@@ -56,14 +141,13 @@ case ${UNAME} in
else
mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
fi
ram_avail_mb=$(($(LC_ALL=C vmstat -s | grep -ie '[0-9] pages free$' | cut -d p -f 1) * ($(LC_ALL=C vmstat -s | grep -ie '[0-9] bytes per page$' | cut -d b -f 1) / 1024) / 1024))
;;
Darwin)
MAKE=make
if [[ ! -v TESTDB_DIR || -z "$TESTDB_DIR" ]]; then
for vol in $(ls -d /Volumes/mdx[0-9]*[0-9]tst); do
if [ -z "${TESTDB_DIR:-}" ]; then
for vol in $(ls -d /Volumes/mdx[0-9]*[0-9]tst 2>/dev/null); do
disk=$(mount | grep $vol | cut -d ' ' -f 1)
echo "umount: volume $vol disk $disk"
hdiutil unmount $vol -force
@@ -74,12 +158,10 @@ case ${UNAME} in
else
mkdir -p $TESTDB_DIR && rm -f $TESTDB_DIR/*
fi
pagesize=$(($(LC_ALL=C vm_stat | grep -o 'page size of [0-9]\+ bytes' | cut -d' ' -f 4) / 1024))
freepages=$(LC_ALL=C vm_stat | grep '^Pages free:' | grep -o '[0-9]\+\.$' | cut -d'.' -f 1)
ram_avail_mb=$((pagesize * freepages / 1024))
echo "pagesize ${pagesize}K, freepages ${freepages}, ram_avail_mb ${ram_avail_mb}"
;;
*)
@@ -88,6 +170,8 @@ case ${UNAME} in
;;
esac
rm -f ${TESTDB_DIR}/*
###############################################################################
# 2. estimate reasonable RAM space for test-db
@@ -117,8 +201,8 @@ fi
# system immediately, as well some space is required for logs.
#
db_size_mb=$(((ram_avail_mb - ram_reserve4logs_mb) / 4))
if [ $db_size_mb -gt 17408 ]; then
db_size_mb=17408
if [ $db_size_mb -gt $DB_UPTO_MB ]; then
db_size_mb=$DB_UPTO_MB
fi
echo "=== use ${db_size_mb}M for DB"
@@ -150,48 +234,80 @@ case ${UNAME} in
esac
###############################################################################
# 4. Run basic test, i.e. `make check`
# 4. build the test executables
${MAKE} -j2 all mdbx_test
#${MAKE} TEST_DB=${TESTDB_DIR}/smoke.db TEST_LOG=${TESTDB_DIR}/smoke.log check
rm -f ${TESTDB_DIR}/*
if [ "$SKIP_MAKE" != "yes" ]; then
${MAKE} -j$(which nproc >/dev/null 2>/dev/null && nproc || echo 2) build-test
fi
###############################################################################
# 5. run stochastic iterations
function join { local IFS="$1"; shift; echo "$*"; }
function bit2option { local -n arr=$1; (( ($2&(1<<$3)) != 0 )) && echo -n '+' || echo -n '-'; echo "${arr[$3]}"; }
if which lz4 >/dev/null; then
function logger {
lz4 > ${TESTDB_DIR}/long.log.lz4
}
elif which gzip >/dev/null; then
function logger {
gzip > ${TESTDB_DIR}/long.log.gz
}
else
function logger {
cat > ${TESTDB_DIR}/long.log
}
fi
syncmodes=("" ,+nosync-safe ,+nosync-utterly)
options=(writemap coalesce lifo notls perturb)
function bits2list {
local -n arr=$1
function join { local IFS="$1"; shift; echo "$*"; }
function bits2options {
local bits=$1
local i
local list=()
for ((i=0; i<${#arr[@]}; ++i)) do
list[$i]=$(bit2option $1 $2 $i)
for ((i = 0; i < ${#options[@]}; ++i)); do
list[$i]=$( (( (bits & (1 << i)) != 0 )) && echo -n '+' || echo -n '-'; echo ${options[$i]})
done
join , "${list[@]}"
join , ${list[@]}
}
function failed {
echo "FAILED" >&2
exit 1
}
function check_deep {
if [ "$case" = "basic" -o "$case" = "--hill" ]; then
tee >(logger) | grep -e reach -e achieve
else
logger
fi
}
function probe {
echo "----------------------------------------------- $(date)"
echo "${caption}: $*"
rm -f ${TESTDB_DIR}/* \
&& ${VALGRIND} ./mdbx_test ${speculum} --random-writemap=no --ignore-dbfull --repeat=11 --pathname=${TESTDB_DIR}/long.db --cleanup-after=no "$@" \
| tee >(lz4 > ${TESTDB_DIR}/long.log.lz4) | grep -e reach -e achieve \
&& ${VALGRIND} ./mdbx_chk ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
&& ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${VALGRIND} ./mdbx_chk ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
|| (echo "FAILED"; exit 1)
echo "${caption}"
rm -f ${TESTDB_DIR}/* || failed
for case in $LIST
do
echo "Run ./mdbx_test ${speculum} --random-writemap=no --ignore-dbfull --repeat=11 --pathname=${TESTDB_DIR}/long.db --cleanup-after=no $@ $case"
${MONITOR} ./mdbx_test ${speculum} --random-writemap=no --ignore-dbfull --repeat=11 --pathname=${TESTDB_DIR}/long.db --cleanup-after=no "$@" $case | check_deep \
&& ${MONITOR} ./mdbx_chk ${TESTDB_DIR}/long.db | tee ${TESTDB_DIR}/long-chk.log \
&& ([ ! -e ${TESTDB_DIR}/long.db-copy ] || ${MONITOR} ./mdbx_chk ${TESTDB_DIR}/long.db-copy | tee ${TESTDB_DIR}/long-chk-copy.log) \
|| failed
done
}
#------------------------------------------------------------------------------
count=0
loop=0
cases='?'
for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10000000 33333333 100000000 333333333 1000000000; do
if [ $nops -lt $FROM ]; then continue; fi
if [ $nops -gt $UPTO ]; then echo "The '--upto $UPTO' limit reached"; break; fi
if [ -n "$LOOPS" ] && [ $loop -ge "$LOOPS" ]; then echo "The '--loops $LOOPS' limit reached"; break; fi
echo "======================================================================="
wbatch=$((nops / 7 + 1))
speculum=$([ $nops -le 1000 ] && echo '--speculum' || true)
@@ -204,68 +320,70 @@ for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10
split=30
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
split=24
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
split=16
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
split=4
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.integer --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
--pagesize=min --size-upper=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2list options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed} basic
--pagesize=min --size-upper-upto=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%3]} \
--keygen.seed=${seed}
done # options
loop=$((loop + 1))
if [ -n "$LOOPS" ] && [ $loop -ge "$LOOPS" ]; then break; fi
cases="${subcase}"
wbatch=$(((wbatch > 7) ? wbatch / 7 : 1))
if [ $wbatch -eq 1 -o $((nops / wbatch)) -gt 1000 ]; then break; fi

View File

@@ -92,6 +92,8 @@ MDBX_NORETURN void usage(void) {
" --mode={[+-]FLAG}[,[+-]FLAG]...\n"
" nosubdir == MDBX_NOSUBDIR\n"
" rdonly == MDBX_RDONLY\n"
" exclusive == MDBX_EXCLUSIVE\n"
" accede == MDBX_ACCEDE\n"
" nometasync == MDBX_NOMETASYNC\n"
" lifo == MDBX_LIFORECLAIM\n"
" coalesce == MDBX_COALESCE\n"
@@ -222,6 +224,43 @@ void cleanup() {
log_trace("<< cleanup");
}
static void fixup4qemu(actor_params &params) {
#ifdef MDBX_SAFE4QEMU
#if MDBX_WORDBITS == 32
intptr_t safe4qemu_limit = size_t(512) << 20 /* 512 megabytes */;
#if defined(__SANITIZE_ADDRESS__)
safe4qemu_limit >>= 1;
#else
if (RUNNING_ON_VALGRIND)
safe4qemu_limit >>= 1;
#endif /* __SANITIZE_ADDRESS__ */
if (params.size_lower > safe4qemu_limit ||
params.size_now > safe4qemu_limit ||
params.size_upper > safe4qemu_limit) {
params.size_upper = std::min(params.size_upper, safe4qemu_limit);
params.size_now = std::min(params.size_now, params.size_upper);
params.size_lower = std::min(params.size_lower, params.size_now);
log_notice("workaround: for conformance 32-bit build with "
"QEMU/ASAN/Valgrind database size reduced to %zu megabytes",
safe4qemu_limit >> 20);
}
#endif /* MDBX_WORDBITS == 32 */
#if defined(__alpha__) || defined(__alpha) || defined(__sparc__) || \
defined(__sparc) || defined(__sparc64__) || defined(__sparc64)
if (params.size_lower != params.size_upper) {
log_notice(
"workaround: for conformance Alpha/Sparc build with QEMU/ASAN/Valgrind "
"enforce fixed database size %zu megabytes",
params.size_upper >> 20);
params.size_lower = params.size_now = params.size_upper;
}
#endif /* Alpha || Sparc */
#endif /* MDBX_SAFE4QEMU */
(void)params;
}
int main(int argc, char *const argv[]) {
#ifdef _DEBUG
@@ -254,6 +293,7 @@ int main(int argc, char *const argv[]) {
const char *value = nullptr;
if (config::parse_option(argc, argv, narg, "case", &value)) {
fixup4qemu(params);
testcase_setup(value, params, last_space_id);
continue;
}
@@ -327,6 +367,15 @@ int main(int argc, char *const argv[]) {
mdbx_limits_dbsize_min(params.pagesize),
mdbx_limits_dbsize_max(params.pagesize)))
continue;
int64_t i64 = params.size_upper;
if (config::parse_option(argc, argv, narg, "size-upper-upto", i64,
int64_t(mdbx_limits_dbsize_min(params.pagesize)),
INT64_MAX, -1)) {
if (i64 > mdbx_limits_dbsize_max(params.pagesize))
i64 = mdbx_limits_dbsize_max(params.pagesize);
params.size_upper = intptr_t(i64);
continue;
}
if (config::parse_option_intptr(argc, argv, narg, "size-upper",
params.size_upper,
mdbx_limits_dbsize_min(params.pagesize),
@@ -477,38 +526,47 @@ int main(int argc, char *const argv[]) {
continue;
}
if (config::parse_option(argc, argv, narg, "hill", &value, "auto")) {
fixup4qemu(params);
configure_actor(last_space_id, ac_hill, value, params);
continue;
}
if (config::parse_option(argc, argv, narg, "jitter", nullptr)) {
fixup4qemu(params);
configure_actor(last_space_id, ac_jitter, value, params);
continue;
}
if (config::parse_option(argc, argv, narg, "dead.reader", nullptr)) {
fixup4qemu(params);
configure_actor(last_space_id, ac_deadread, value, params);
continue;
}
if (config::parse_option(argc, argv, narg, "dead.writer", nullptr)) {
fixup4qemu(params);
configure_actor(last_space_id, ac_deadwrite, value, params);
continue;
}
if (config::parse_option(argc, argv, narg, "try", nullptr)) {
fixup4qemu(params);
configure_actor(last_space_id, ac_try, value, params);
continue;
}
if (config::parse_option(argc, argv, narg, "copy", nullptr)) {
fixup4qemu(params);
configure_actor(last_space_id, ac_copy, value, params);
continue;
}
if (config::parse_option(argc, argv, narg, "append", nullptr)) {
fixup4qemu(params);
configure_actor(last_space_id, ac_append, value, params);
continue;
}
if (config::parse_option(argc, argv, narg, "ttl", nullptr)) {
fixup4qemu(params);
configure_actor(last_space_id, ac_ttl, value, params);
continue;
}
if (config::parse_option(argc, argv, narg, "nested", nullptr)) {
fixup4qemu(params);
configure_actor(last_space_id, ac_nested, value, params);
continue;
}
@@ -522,9 +580,10 @@ int main(int argc, char *const argv[]) {
global::config::console_mode))
continue;
if (*argv[narg] != '-')
if (*argv[narg] != '-') {
fixup4qemu(params);
testcase_setup(argv[narg], params, last_space_id);
else
} else
failure("Unknown option '%s'. Try --help\n", argv[narg]);
}
@@ -550,10 +609,6 @@ int main(int argc, char *const argv[]) {
if (global::config::cleanup_before)
cleanup();
log_trace(">> probe entropy_ticks()");
entropy_ticks();
log_trace("<< probe entropy_ticks()");
if (global::actors.size() == 1) {
logging::setup("main");
global::singlemode = true;

View File

@@ -110,16 +110,24 @@ bool testcase_nested::teardown() {
}
void testcase_nested::push_txn() {
MDBX_txn *txn;
MDBX_txn *nested_txn;
MDBX_txn_flags_t flags = MDBX_txn_flags_t(
prng32() & uint32_t(MDBX_TXN_NOSYNC | MDBX_TXN_NOMETASYNC));
int err = mdbx_txn_begin(db_guard.get(), txn_guard.get(), flags, &txn);
int err = mdbx_txn_begin(db_guard.get(), txn_guard.get(), flags, &nested_txn);
if (unlikely(err != MDBX_SUCCESS))
failure_perror("mdbx_txn_begin(nested)", err);
stack.emplace(scoped_txn_guard(txn), serial, fifo, speculum);
std::swap(txn_guard, std::get<0>(stack.top()));
/* CLANG/LLVM C++ library could stupidly copy std::set<> item-by-item,
* i.e. with insertion(s) & comparison(s), which will cause null dereference
* during call mdbx_cmp() with zero txn. So it is the workaround for this:
* - explicitly set txn_guard with the new nested txn;
* - explicitly copy the `speculum` (an instance of std::set<>). */
scoped_txn_guard nested_txn_guard(nested_txn);
txn_guard.swap(nested_txn_guard);
SET speculum_snapshot(speculum);
stack.emplace(std::move(nested_txn_guard), serial, fifo,
std::move(speculum_snapshot));
log_verbose("begin level#%zu txn #%" PRIu64 ", flags 0x%x, serial %" PRIu64,
stack.size(), mdbx_txn_id(txn), flags, serial);
stack.size(), mdbx_txn_id(nested_txn), flags, serial);
}
bool testcase_nested::pop_txn(bool abort) {
@@ -192,7 +200,7 @@ bool testcase_nested::trim_tail(unsigned window_width) {
while (fifo.size() > window_width) {
uint64_t tail_serial = fifo.back().first;
const unsigned tail_count = fifo.back().second;
log_verbose("nested: pop-tail (serial %" PRIu64 ", count %u)",
log_verbose("nested: trim-tail (serial %" PRIu64 ", count %u)",
tail_serial, tail_count);
fifo.pop_back();
for (unsigned n = 0; n < tail_count; ++n) {

View File

@@ -197,6 +197,18 @@ int testcase::breakable_commit() {
log_trace(">> txn_commit");
assert(txn_guard);
/* CLANG/LLVM C++ library could stupidly copy std::set<> item-by-item,
* i.e. with insertion(s) & comparison(s), which will cause null dereference
* during call mdbx_cmp() with zero txn. So it is the workaround for this:
* - explicitly make copies of the `speculums`;
* - explicitly move relevant copy after transaction commit. */
SET speculum_committed_copy(ItemCompare(this)),
speculum_copy(ItemCompare(this));
if (need_speculum_assign) {
speculum_committed_copy = speculum_committed;
speculum_copy = speculum;
}
MDBX_txn *txn = txn_guard.release();
txn_inject_writefault(txn);
int rc = mdbx_txn_commit(txn);
@@ -207,9 +219,9 @@ int testcase::breakable_commit() {
if (need_speculum_assign) {
need_speculum_assign = false;
if (unlikely(rc != MDBX_SUCCESS))
speculum = speculum_committed;
speculum = std::move(speculum_committed_copy);
else
speculum_committed = speculum;
speculum_committed = std::move(speculum_copy);
}
log_trace("<< txn_commit: %s", rc ? "failed" : "Ok");
@@ -714,8 +726,8 @@ void testcase::speculum_check_cursor(const char *where, const char *stage,
const testcase::SET::const_iterator &it,
MDBX_cursor *cursor,
const MDBX_cursor_op op) const {
MDBX_val cursor_key = {};
MDBX_val cursor_data = {};
MDBX_val cursor_key = {0, 0};
MDBX_val cursor_data = {0, 0};
int err;
if (it != speculum.end() && std::next(it) == speculum.end() &&
op == MDBX_PREV && (config.params.table_flags & MDBX_DUPSORT)) {
@@ -1018,7 +1030,7 @@ bool testcase::speculum_verify() {
char dump_mkey[128], dump_mvalue[128];
MDBX_cursor *cursor;
int err = mdbx_cursor_open(txn_guard.get(), dbi, &cursor);
int eof, err = mdbx_cursor_open(txn_guard.get(), dbi, &cursor);
if (err != MDBX_SUCCESS)
failure_perror("mdbx_cursor_open()", err);
@@ -1034,14 +1046,20 @@ bool testcase::speculum_verify() {
if (err != MDBX_SUCCESS) {
akey.iov_len = avalue.iov_len = 0;
akey.iov_base = avalue.iov_base = nullptr;
} else {
eof = mdbx_cursor_eof(cursor);
if (eof != MDBX_RESULT_FALSE) {
log_error("false-positive cursor-eof %u/%u: db{%s, %s}, rc %i", n,
extra, mdbx_dump_val(&akey, dump_key, sizeof(dump_key)),
mdbx_dump_val(&avalue, dump_value, sizeof(dump_value)), eof);
rc = false;
}
}
const auto S_key = iov2dataview(akey);
const auto S_data = iov2dataview(avalue);
if (it != speculum.cend()) {
mkey.iov_base = (void *)it->first.c_str();
mkey.iov_len = it->first.size();
mvalue.iov_base = (void *)it->second.c_str();
mvalue.iov_len = it->second.size();
mkey = it->first;
mvalue = it->second;
}
if (err == MDBX_SUCCESS && it != speculum.cend() && S_key == it->first &&
S_data == it->second) {
@@ -1091,6 +1109,14 @@ bool testcase::speculum_verify() {
n += 1;
}
if (err == MDBX_NOTFOUND) {
eof = mdbx_cursor_eof(cursor);
if (eof != MDBX_RESULT_TRUE) {
eof = mdbx_cursor_eof(cursor);
log_error("false-negative cursor-eof: %u, rc %i", n, eof);
rc = false;
}
}
mdbx_cursor_close(cursor);
return rc;
}

View File

@@ -130,16 +130,12 @@ public:
};
#define REGISTER_TESTCASE(NAME) \
static registry::factory<testcase_##NAME> gRegister_##NAME(ac_##NAME, \
STRINGIFY(NAME))
static registry::factory<testcase_##NAME> gRegister_##NAME( \
ac_##NAME, MDBX_STRINGIFY(NAME))
class testcase {
protected:
#if HAVE_cxx17_std_string_view
using data_view = std::string_view;
#else
using data_view = std::string;
#endif
using data_view = mdbx::slice;
static inline data_view iov2dataview(const MDBX_val &v) {
return (v.iov_base && v.iov_len)
? data_view(static_cast<const char *>(v.iov_base), v.iov_len)
@@ -149,7 +145,8 @@ protected:
return iov2dataview(b->value);
}
using Item = std::pair<std::string, std::string>;
using Item = std::pair<::mdbx::buffer<>, ::mdbx::buffer<>>;
static MDBX_val dataview2iov(const data_view &v) {
MDBX_val r;
r.iov_base = (void *)v.data();
@@ -158,10 +155,13 @@ protected:
}
struct ItemCompare {
const testcase *context;
ItemCompare(const testcase *owner) : context(owner) {}
ItemCompare(const testcase *owner) : context(owner) {
/* The context->txn_guard may be empty/null here */
}
bool operator()(const Item &a, const Item &b) const {
MDBX_val va = dataview2iov(a.first), vb = dataview2iov(b.first);
assert(context->txn_guard.get() != nullptr);
int cmp = mdbx_cmp(context->txn_guard.get(), context->dbi, &va, &vb);
if (cmp == 0 &&
(context->config.params.table_flags & MDBX_DUPSORT) != 0) {

View File

@@ -101,164 +101,6 @@ bool is_samedata(const MDBX_val *a, const MDBX_val *b) {
//-----------------------------------------------------------------------------
/* TODO: replace my 'libmera' from t1ha. */
uint64_t entropy_ticks(void) {
#if defined(EMSCRIPTEN)
return (uint64_t)emscripten_get_now();
#endif /* EMSCRIPTEN */
#if defined(__APPLE__) || defined(__MACH__)
return mach_absolute_time();
#endif /* defined(__APPLE__) || defined(__MACH__) */
#if defined(__sun__) || defined(__sun)
return gethrtime();
#endif /* __sun__ */
#if defined(__GNUC__) || defined(__clang__)
#if defined(__ia64__)
uint64_t ticks;
__asm __volatile("mov %0=ar.itc" : "=r"(ticks));
return ticks;
#elif defined(__hppa__)
uint64_t ticks;
__asm __volatile("mfctl 16, %0" : "=r"(ticks));
return ticks;
#elif defined(__s390__)
uint64_t ticks;
__asm __volatile("stck 0(%0)" : : "a"(&(ticks)) : "memory", "cc");
return ticks;
#elif defined(__alpha__)
uint64_t ticks;
__asm __volatile("rpcc %0" : "=r"(ticks));
return ticks;
#elif defined(__sparc__) || defined(__sparc) || defined(__sparc64__) || \
defined(__sparc64) || defined(__sparc_v8plus__) || \
defined(__sparc_v8plus) || defined(__sparc_v8plusa__) || \
defined(__sparc_v8plusa) || defined(__sparc_v9__) || defined(__sparc_v9)
union {
uint64_t u64;
struct {
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
uint32_t h, l;
#else
uint32_t l, h;
#endif
} u32;
} cycles;
#if defined(__sparc_v8plus__) || defined(__sparc_v8plusa__) || \
defined(__sparc_v9__) || defined(__sparc_v8plus) || \
defined(__sparc_v8plusa) || defined(__sparc_v9)
#if UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul || \
defined(__sparc64__) || defined(__sparc64)
__asm __volatile("rd %%tick, %0" : "=r"(cycles.u64));
#else
__asm __volatile("rd %%tick, %1; srlx %1, 32, %0"
: "=r"(cycles.u32.h), "=r"(cycles.u32.l));
#endif /* __sparc64__ */
#else
__asm __volatile(".byte 0x83, 0x41, 0x00, 0x00; mov %%g1, %0"
: "=r"(cycles.u64)
:
: "%g1");
#endif /* __sparc8plus__ || __sparc_v9__ */
return cycles.u64;
#elif (defined(__powerpc64__) || defined(__ppc64__) || defined(__ppc64) || \
defined(__powerpc64))
uint64_t ticks;
__asm __volatile("mfspr %0, 268" : "=r"(ticks));
return ticks;
#elif (defined(__powerpc__) || defined(__ppc__) || defined(__powerpc) || \
defined(__ppc))
#if UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul
uint64_t ticks;
__asm __volatile("mftb %0" : "=r"(ticks));
*now = ticks;
#else
uint64_t ticks;
uint32_t low, high_before, high_after;
__asm __volatile("mftbu %0; mftb %1; mftbu %2"
: "=r"(high_before), "=r"(low), "=r"(high_after));
ticks = (uint64_t)high_after << 32;
ticks |= low & /* zeroes if high part has changed */
~(high_before - high_after);
#endif
#elif (defined(__aarch64__) || (defined(__ARM_ARCH) && __ARM_ARCH > 7)) && \
!defined(MDBX_SAFE4QEMU)
uint64_t virtual_timer;
__asm __volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer));
return virtual_timer;
#elif (defined(__ARM_ARCH) && __ARM_ARCH > 5 && __ARM_ARCH < 8) || \
defined(_M_ARM)
static uint32_t pmcntenset = 0x00425B00;
if (unlikely(pmcntenset == 0x00425B00)) {
uint32_t pmuseren;
#ifdef _M_ARM
pmuseren = _MoveFromCoprocessor(15, 0, 9, 14, 0);
#else
__asm("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
#endif
if (1 & pmuseren /* Is it allowed for user mode code? */) {
#ifdef _M_ARM
pmcntenset = _MoveFromCoprocessor(15, 0, 9, 12, 1);
#else
__asm("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
#endif
} else
pmcntenset = 0;
}
if (pmcntenset & 0x80000000ul /* Is it counting? */) {
#ifdef _M_ARM
return __rdpmccntr64();
#else
uint32_t pmccntr;
__asm __volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
return pmccntr;
#endif
}
#elif ((defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS2) && \
_MIPS_ISA >= _MIPS_ISA_MIPS2) || \
(defined(__mips) && __mips >= 2) || defined(_R4000)) && \
!defined(MDBX_SAFE4QEMU) /* QEMU may not emulate the CC register \
(High-resolution cycle counter) */
unsigned count;
__asm __volatile("rdhwr %0, $2" : "=r"(count));
return count;
#endif /* arch selector */
#endif /* __GNUC__ || __clang__ */
#if defined(__e2k__) || defined(__ia32__)
return __rdtsc();
#elif defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS)
LARGE_INTEGER PerformanceCount;
if (QueryPerformanceCounter(&PerformanceCount))
return PerformanceCount.QuadPart;
return GetTickCount64();
#else
struct timespec ts;
#if defined(CLOCK_MONOTONIC_COARSE)
clockid_t clk_id = CLOCK_MONOTONIC_COARSE;
#elif defined(CLOCK_MONOTONIC_RAW)
clockid_t clk_id = CLOCK_MONOTONIC_RAW;
#else
clockid_t clk_id = CLOCK_MONOTONIC;
#endif
int rc = clock_gettime(clk_id, &ts);
if (unlikely(rc))
failure_perror("clock_gettime()", rc);
return (((uint64_t)ts.tv_sec) << 32) + ts.tv_nsec;
#endif
}
//-----------------------------------------------------------------------------
uint64_t prng64_white(uint64_t &state) {
state = prng64_map2_careless(state);
return bleach64(state);
@@ -303,8 +145,6 @@ uint64_t prng64(void) { return prng64_white(prng_state); }
void prng_fill(void *ptr, size_t bytes) { prng_fill(prng_state, ptr, bytes); }
uint64_t entropy_white() { return bleach64(entropy_ticks()); }
double double_from_lower(uint64_t salt) {
#ifdef IEEE754_DOUBLE_BIAS
ieee754_double r;
@@ -336,25 +176,25 @@ double double_from_upper(uint64_t salt) {
#endif
}
bool flipcoin() { return bleach32((uint32_t)entropy_ticks()) & 1; }
bool flipcoin_x2() { return (bleach32((uint32_t)entropy_ticks()) & 3) == 0; }
bool flipcoin_x3() { return (bleach32((uint32_t)entropy_ticks()) & 7) == 0; }
bool flipcoin_x4() { return (bleach32((uint32_t)entropy_ticks()) & 15) == 0; }
bool flipcoin() { return prng32() & 1; }
bool flipcoin_x2() { return (prng32() & 3) == 0; }
bool flipcoin_x3() { return (prng32() & 7) == 0; }
bool flipcoin_x4() { return (prng32() & 15) == 0; }
bool flipcoin_n(unsigned n) {
return (bleach64(entropy_ticks()) & ((UINT64_C(1) << n) - 1)) == 0;
return (prng64() & ((UINT64_C(1) << n) - 1)) == 0;
}
bool jitter(unsigned probability_percent) {
const uint32_t top = UINT32_MAX - UINT32_MAX % 100;
uint32_t dice, edge = (top) / 100 * probability_percent;
do
dice = bleach32((uint32_t)entropy_ticks());
dice = prng32();
while (dice >= top);
return dice < edge;
}
void jitter_delay(bool extra) {
unsigned dice = entropy_white() & 3;
unsigned dice = prng32() & 3;
if (dice == 0) {
log_trace("== jitter.no-delay");
} else {
@@ -367,8 +207,8 @@ void jitter_delay(bool extra) {
osal_yield();
cpu_relax();
if (dice > 2) {
unsigned us = entropy_white() &
(extra ? 0xffff /* 656 ms */ : 0x3ff /* 1 ms */);
unsigned us =
prng32() & (extra ? 0xffff /* 656 ms */ : 0x3ff /* 1 ms */);
log_trace("== jitter.delay: %0.6f", us / 1000000.0);
osal_udelay(us);
}

View File

@@ -292,8 +292,6 @@ inline bool is_samedata(const MDBX_val &a, const MDBX_val &b) {
}
std::string format(const char *fmt, ...);
uint64_t entropy_ticks(void);
uint64_t entropy_white(void);
static inline uint64_t bleach64(uint64_t v) {
// Tommy Ettinger, https://www.blogger.com/profile/04953541827437796598
// http://mostlymangling.blogspot.com/2019/01/better-stronger-mixer-and-test-procedure.html

View File

@@ -22,6 +22,14 @@
...
fun:mdbx_mapresize
}
{
msync-wipe-steady
Memcheck:Param
msync(start)
fun:msync
...
fun:mdbx_wipe_steady
}
# single-page flush by pwrite()
{
@@ -42,16 +50,15 @@
}
# modern Valgrind don't support the `vector[...]` pattern
#{
# pwritev-page-flush
# Memcheck:Param
# pwritev(vector[...])
# fun:pwritev
# ...
# fun:mdbx_iov_write*
#}
# for((i=0;i<64;++i)); do echo -e "{\n pwritev-page-flush-$i\n Memcheck:Param\n pwritev(vector[$i])\n fun:pwritev\n ...\n fun:mdbx_iov_write*\n}"; done >> valgrind_suppress.txt
{
pwritev-page-flush
Memcheck:Param
pwritev(vector[...])
fun:pwritev
...
fun:mdbx_iov_write*
}
{
pwritev-page-flush-0
Memcheck:Param