mirror of
https://github.com/isar/libmdbx.git
synced 2024-10-29 23:19:20 +08:00
mdbx: вливание ветки devel
в master
.
56 files changed, 8997 insertions(+), 4507 deletions(-)
This commit is contained in:
commit
abca22e32d
@ -305,7 +305,7 @@ else()
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/test/valgrind_suppress.txt"
|
||||
CACHE FILEPATH "Suppressions file for Valgrind" FORCE)
|
||||
set(MEMORYCHECK_COMMAND_OPTIONS
|
||||
"--trace-children=yes --leak-check=full --track-origins=yes --error-exitcode=42 --error-markers=@ --errors-for-leak-kinds=definite --fair-sched=yes --suppressions=${MEMORYCHECK_SUPPRESSIONS_FILE}"
|
||||
"--trace-children=yes --leak-check=full --track-origins=yes --track-origins=yes --error-exitcode=42 --error-markers=@ --errors-for-leak-kinds=definite --fair-sched=yes --suppressions=${MEMORYCHECK_SUPPRESSIONS_FILE}"
|
||||
CACHE STRING "Valgrind options" FORCE)
|
||||
set(VALGRIND_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS}" CACHE STRING "Valgrind options" FORCE)
|
||||
endif()
|
||||
@ -467,7 +467,7 @@ endif()
|
||||
# #### # # # #### # # ####
|
||||
#
|
||||
|
||||
set(MDBX_BUILD_OPTIONS ENABLE_UBSAN ENABLE_ASAN MDBX_USE_VALGRIND ENABLE_GPROF ENABLE_GCOV)
|
||||
set(MDBX_BUILD_OPTIONS ENABLE_UBSAN ENABLE_ASAN ENABLE_MEMCHECK ENABLE_GPROF ENABLE_GCOV)
|
||||
macro(add_mdbx_option NAME DESCRIPTION DEFAULT)
|
||||
list(APPEND MDBX_BUILD_OPTIONS ${NAME})
|
||||
if(NOT ${DEFAULT} STREQUAL "AUTO")
|
||||
@ -531,6 +531,8 @@ add_mdbx_option(MDBX_ENABLE_BIGFOOT "Chunking long list of retired pages during
|
||||
add_mdbx_option(MDBX_ENABLE_PGOP_STAT "Gathering statistics for page operations" ON)
|
||||
add_mdbx_option(MDBX_ENABLE_PROFGC "Profiling of GC search and updates" OFF)
|
||||
mark_as_advanced(MDBX_ENABLE_PROFGC)
|
||||
add_mdbx_option(MDBX_ENABLE_DBI_SPARSE "FIXME" ON)
|
||||
add_mdbx_option(MDBX_ENABLE_DBI_LOCKFREE "FIXME" ON)
|
||||
|
||||
if(NOT MDBX_AMALGAMATED_SOURCE)
|
||||
if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE_UPPERCASE STREQUAL "DEBUG")
|
||||
|
62
ChangeLog.md
62
ChangeLog.md
@ -4,6 +4,67 @@ ChangeLog
|
||||
English version [by Google](https://gitflic-ru.translate.goog/project/erthink/libmdbx/blob?file=ChangeLog.md&_x_tr_sl=ru&_x_tr_tl=en)
|
||||
and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md).
|
||||
|
||||
## v0.13.1 (в процессе подготовки релиза)
|
||||
|
||||
Новая версия с существенным расширением API и добавлением функционала.
|
||||
|
||||
Новое:
|
||||
|
||||
- Управление основной блокировкой lock/unlock/upgrade/downgrade для координации пишущих транзакций.
|
||||
- `mdbx_env_chk() `для проверка целостности структуры БД, с переработкой и переносом функционала утилиты `mdbx_chk` внутрь библиотеки.
|
||||
- `mdbx_dbi_rename()` и `mdbx_dbi_rename()` для переименования таблиц.
|
||||
- `mdbx_cursor_unbind()` и `mdbx_txn_release_all_cursors()` для управления курсорами.
|
||||
- `mdbx_env_resurrect_after_fork()` для восстановление открытой среды работы с БД в дочернем процессе после ветвления/расщепления процесса.
|
||||
- `mdbx_cursor_compare()` для сравнения позиций курсоров.
|
||||
- `mdbx_cursor_scan()` и `mdbx_cursor_scan_from()` для сканирования таблиц с использованием функционального предиката.
|
||||
- `mdbx_cursor_on_first_dup()` и `mdbx_cursor_on_last_dup()` для проверки позиции курсора.
|
||||
- `mdbx_preopen_snapinfo()` для получения информации о БД без её открытия.
|
||||
|
||||
- Расширение и доработка C++ API:
|
||||
|
||||
- добавлен тип `mdbx::cursor::estimation_result`, а поведение методов
|
||||
`cursor::estimate()` унифицировано с `cursor::move()`;
|
||||
- для предотвращения незаметного неверного использования API, для инициализации
|
||||
возвращаемых по ссылке срезов, вместо пустых срезов задействован `slice::invalid()`;
|
||||
- добавлены дополнительные C++ операторы преобразования к типам C API;
|
||||
- для совместимости со старыми стандартами C++ и старыми версиями STL перенесены
|
||||
в public классы `buffer::move_assign_alloc` и `buffer::copy_assign_alloc`;
|
||||
- добавлен тип `mdbx::default_buffer`;
|
||||
- для срезов и буферов добавлены методы `hex_decode()`, `base64_decode()`, `base58_decode()`;
|
||||
- добавлен тип `mdbx::comparator` и функций `mdbx::default_comparator()`;
|
||||
- добавлены статические методы `buffer::hex()`, `base64()`, `base58()`;
|
||||
- для транзакций и курсоров добавлены методы `get_/set_context`;
|
||||
- добавлен метод `cursor::clone()`;
|
||||
- поддержка base58 переработана и приведена в соответствии с черновиком RFC, в текущем понимании теперь это одна из самых высокопроизводительных реализаций;
|
||||
- переработка `to_hex()` и `from_hex()`.
|
||||
|
||||
|
||||
## v0.13.0 от 2023-04-23
|
||||
|
||||
Не выпуск, а начало ветки `0.13` с новым функционалом и изменением API.
|
||||
|
||||
Новое:
|
||||
|
||||
- Расширение API функционалом проверки целостности структуры БД, с
|
||||
переработкой и переноса функционала утилиты `mdbx_chk` внутрь библиотеки.
|
||||
|
||||
- Расширение API функциями lock/unlock/upgrade/downgrade основной блокировки.
|
||||
|
||||
- Добавление в API функций `mdbx_cursor_unbind()` и `mdbx_txn_release_all_cursors()`.
|
||||
|
||||
- Возвращение `MDBX_TXN_INVALID` (`INT32_MIN`) вместо `-1`
|
||||
из `mdbx_txn_flags()` при передаче невалидной транзакции.
|
||||
|
||||
Мелочи:
|
||||
|
||||
- Обновление конфигурации Doxygen до 1.9.6.
|
||||
- Добавление `--read-var-info=yes` для Valgrind.
|
||||
- Вывод из `mdbx_chk` информации об уровне детализации/verbosity.
|
||||
|
||||
|
||||
********************************************************************************
|
||||
|
||||
|
||||
## v0.12.10 "СЭМ" от 2024-03-12
|
||||
|
||||
Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов
|
||||
@ -152,6 +213,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
|
||||
|
||||
## v0.12.8 "Владимир Уткин" от 2023-10-17
|
||||
|
||||
|
||||
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
|
||||
в день 100-летия со дня рождения выдающегося советского и российского ученого и конструктора [Влади́мира Фёдоровича У́ткина](https://ru.wikipedia.org/wiki/Уткин,_Владимир_Фёдорович).
|
||||
|
||||
|
57
GNUmakefile
57
GNUmakefile
@ -172,22 +172,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 - 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 smoke - fast smoke test"
|
||||
@echo " make smoke-memcheck - build with Valgrind support and run smoke test under memcheck tool"
|
||||
@echo " make smoke-fault - execute transaction owner failure smoke testcase"
|
||||
@echo " make smoke-singleprocess - execute single-process smoke test"
|
||||
@echo " make test - basic test"
|
||||
@echo " make test-memcheck - build with Valgrind support and run basic test under memcheck tool"
|
||||
@echo " make test-long - execute long test which runs for several weeks, or until interruption"
|
||||
@echo " make test-asan - build with AddressSanitizer and run basic test"
|
||||
@echo " make test-leak - build with LeakSanitizer and run basic test"
|
||||
@echo " make test-ubsan - build with UndefinedBehaviourSanitizer and run basic test"
|
||||
@echo " make test-singleprocess - execute single-process basic test (also used by make cross-qemu)"
|
||||
@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 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"
|
||||
@ -328,8 +328,14 @@ else
|
||||
|
||||
.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
|
||||
.PHONY: smoke-assertion test-assertion long-test-assertion
|
||||
.PHONY: smoke-singleprocess test-singleprocess test-ubsan test-valgrind test-memcheck memcheck smoke-memcheck
|
||||
.PHONY: smoke-assertion test-assertion long-test-assertion test-ci test-ci-extra
|
||||
|
||||
test-ci-extra: test-ci cross-gcc cross-qemu
|
||||
|
||||
test-ci: check \
|
||||
smoke-singleprocess smoke-fault smoke-memcheck smoke \
|
||||
test-leak test-asan test-ubsan test-singleprocess test test-memcheck
|
||||
|
||||
define uname2osal
|
||||
case "$(UNAME)" in
|
||||
@ -418,27 +424,30 @@ smoke-fault: build-test
|
||||
|
||||
test: build-test
|
||||
@echo ' RUNNING `test/long_stochastic.sh --loops 2`...'
|
||||
$(QUIET)test/long_stochastic.sh --dont-check-ram-size --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG) || (cat $(TEST_LOG) && false)
|
||||
$(QUIET)test/long_stochastic.sh --dont-check-ram-size --loops 2 --db-upto-mb 256 --extra --skip-make --taillog >$(TEST_LOG) || (cat $(TEST_LOG) && false)
|
||||
|
||||
long-test: build-test
|
||||
long-test: test-long
|
||||
test-long: build-test
|
||||
@echo ' RUNNING `test/long_stochastic.sh --loops 42`...'
|
||||
$(QUIET)test/long_stochastic.sh --loops 42 --db-upto-mb 1024 --skip-make
|
||||
$(QUIET)test/long_stochastic.sh --loops 42 --db-upto-mb 1024 --extra --skip-make --taillog
|
||||
|
||||
test-singleprocess: build-test
|
||||
@echo ' RUNNING `test/long_stochastic.sh --single --loops 2`...'
|
||||
$(QUIET)test/long_stochastic.sh --dont-check-ram-size --single --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG) || (cat $(TEST_LOG) && false)
|
||||
$(QUIET)test/long_stochastic.sh --dont-check-ram-size --single --loops 2 --db-upto-mb 256 --skip-make --taillog >$(TEST_LOG) || (cat $(TEST_LOG) && false)
|
||||
|
||||
test-valgrind: CFLAGS_EXTRA=-Ofast -DMDBX_USE_VALGRIND
|
||||
test-valgrind: build-test
|
||||
test-valgrind: test-memcheck
|
||||
test-memcheck: CFLAGS_EXTRA=-Ofast -DENABLE_MEMCHECK
|
||||
test-memcheck: 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) || (cat $(TEST_LOG) && false)
|
||||
$(QUIET)test/long_stochastic.sh --with-valgrind --extra --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG) || (cat $(TEST_LOG) && false)
|
||||
|
||||
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
|
||||
memcheck: smoke-memcheck
|
||||
smoke-memcheck: VALGRIND=valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --read-var-info=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt
|
||||
smoke-memcheck: CFLAGS_EXTRA=-Ofast -DENABLE_MEMCHECK
|
||||
smoke-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 --table=+data.fixed --keygen.split=29 --datalen=35 --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_SMOKE_EXTRA) basic && \
|
||||
$(VALGRIND) ./mdbx_chk -vvn $(TEST_DB) && \
|
||||
|
7
Makefile
7
Makefile
@ -6,9 +6,10 @@ bench bench-clean bench-couple bench-quartet bench-triplet re-bench \
|
||||
lib libs lib-static lib-shared tools-static \
|
||||
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 smoke smoke-fault smoke-singleprocess \
|
||||
smoke-assertion test-assertion long-test-assertion \
|
||||
test-asan test-leak test-singleprocess test-ubsan test-valgrind:
|
||||
release-assets tags build-test mdbx_test \
|
||||
smoke smoke-fault smoke-singleprocess smoke-assertion smoke-memcheck \
|
||||
test test-assertion test-long test-long-assertion test-ci test-ci-extra \
|
||||
test-asan test-leak test-singleprocess test-ubsan test-memcheck:
|
||||
@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;'` \
|
||||
|
2
TODO.md
2
TODO.md
@ -11,7 +11,6 @@ For the same reason ~~Github~~ is blacklisted forever.
|
||||
|
||||
So currently most of the links are broken due to noted malicious ~~Github~~ sabotage.
|
||||
|
||||
- [Move most of `mdbx_chk` functional to the library API](https://libmdbx.dqdkfa.ru/dead-github/issues/204).
|
||||
- [Replace SRW-lock on Windows to allow shrink DB with `MDBX_NOTLS` option](https://libmdbx.dqdkfa.ru/dead-github/issues/210).
|
||||
- [More flexible support of asynchronous runtime/framework(s)](https://libmdbx.dqdkfa.ru/dead-github/issues/200).
|
||||
- [Migration guide from LMDB to MDBX](https://libmdbx.dqdkfa.ru/dead-github/issues/199).
|
||||
@ -23,6 +22,7 @@ So currently most of the links are broken due to noted malicious ~~Github~~ sabo
|
||||
Done
|
||||
----
|
||||
|
||||
- [Move most of `mdbx_chk` functional to the library API](https://libmdbx.dqdkfa.ru/dead-github/issues/204).
|
||||
- [Simple careful mode for working with corrupted DB](https://libmdbx.dqdkfa.ru/dead-github/issues/223).
|
||||
- [Engage an "overlapped I/O" on Windows](https://libmdbx.dqdkfa.ru/dead-github/issues/224).
|
||||
- [Large/Overflow pages accounting for dirty-room](https://libmdbx.dqdkfa.ru/dead-github/issues/192).
|
||||
|
@ -24,6 +24,25 @@ endif()
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
|
||||
|
||||
unset(MEMCHECK_OPTION_NAME)
|
||||
if(NOT DEFINED ENABLE_MEMCHECK)
|
||||
if (DEFINED MDBX_USE_VALGRIND)
|
||||
set(MEMCHECK_OPTION_NAME "MDBX_USE_VALGRIND")
|
||||
elseif(DEFINED ENABLE_VALGRIND)
|
||||
set(MEMCHECK_OPTION_NAME "ENABLE_VALGRIND")
|
||||
else()
|
||||
set(MEMCHECK_OPTION_NAME "ENABLE_MEMCHECK")
|
||||
endif()
|
||||
if(MEMCHECK_OPTION_NAME STREQUAL "ENABLE_MEMCHECK")
|
||||
option(ENABLE_MEMCHECK
|
||||
"Enable integration with valgrind, a memory analyzing tool" OFF)
|
||||
elseif(${MEMCHECK_OPTION_NAME})
|
||||
set(ENABLE_MEMCHECK ON)
|
||||
else()
|
||||
set(ENABLE_MEMCHECK OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckLibraryExists)
|
||||
check_library_exists(gcov __gcov_flush "" HAVE_GCOV)
|
||||
|
||||
@ -33,23 +52,23 @@ option(ENABLE_GCOV
|
||||
option(ENABLE_GPROF
|
||||
"Enable integration with gprof, a performance analyzing tool" OFF)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_LOADED)
|
||||
include(CheckIncludeFileCXX)
|
||||
check_include_file_cxx(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||
else()
|
||||
include(CheckIncludeFile)
|
||||
check_include_file(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||
endif()
|
||||
|
||||
option(MDBX_USE_VALGRIND "Enable integration with valgrind, a memory analyzing tool" OFF)
|
||||
if(MDBX_USE_VALGRIND AND NOT HAVE_VALGRIND_MEMCHECK_H)
|
||||
message(FATAL_ERROR "MDBX_USE_VALGRIND option is set but valgrind/memcheck.h is not found")
|
||||
endif()
|
||||
|
||||
option(ENABLE_ASAN
|
||||
"Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF)
|
||||
|
||||
option(ENABLE_UBSAN
|
||||
"Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector based on compiler instrumentation" OFF)
|
||||
|
||||
if(ENABLE_MEMCHECK)
|
||||
if(CMAKE_CXX_COMPILER_LOADED)
|
||||
include(CheckIncludeFileCXX)
|
||||
check_include_file_cxx(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||
else()
|
||||
include(CheckIncludeFile)
|
||||
check_include_file(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||
endif()
|
||||
if(NOT HAVE_VALGRIND_MEMCHECK_H)
|
||||
message(FATAL_ERROR "${MEMCHECK_OPTION_NAME} option is set but valgrind/memcheck.h is not found")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cmake_policy(POP)
|
||||
|
557
docs/Doxyfile.in
557
docs/Doxyfile.in
@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.9.1
|
||||
# Doxyfile 1.9.6
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@ -12,6 +12,16 @@
|
||||
# For lists, items can also be appended using:
|
||||
# TAG += value [value, ...]
|
||||
# Values that contain spaces should be placed between quotes (\" \").
|
||||
#
|
||||
# Note:
|
||||
#
|
||||
# Use doxygen to compare the used configuration file with the template
|
||||
# configuration file:
|
||||
# doxygen -x [configFile]
|
||||
# Use doxygen to compare the used configuration file with the template
|
||||
# configuration file without replacing the environment variables or CMake type
|
||||
# replacement variables:
|
||||
# doxygen -x_noenv [configFile]
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Project related configuration options
|
||||
@ -60,16 +70,28 @@ PROJECT_LOGO =
|
||||
|
||||
OUTPUT_DIRECTORY = .
|
||||
|
||||
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
|
||||
# directories (in 2 levels) under the output directory of each output format and
|
||||
# will distribute the generated files over these directories. Enabling this
|
||||
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
|
||||
# sub-directories (in 2 levels) under the output directory of each output format
|
||||
# and will distribute the generated files over these directories. Enabling this
|
||||
# option can be useful when feeding doxygen a huge amount of source files, where
|
||||
# putting all generated files in the same directory would otherwise causes
|
||||
# performance problems for the file system.
|
||||
# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
|
||||
# control the number of sub-directories.
|
||||
# The default value is: NO.
|
||||
|
||||
CREATE_SUBDIRS = NO
|
||||
|
||||
# Controls the number of sub-directories that will be created when
|
||||
# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
|
||||
# level increment doubles the number of directories, resulting in 4096
|
||||
# directories at level 8 which is the default and also the maximum value. The
|
||||
# sub-directories are organized in 2 levels, the first level always has a fixed
|
||||
# number of 16 directories.
|
||||
# Minimum value: 0, maximum value: 8, default value: 8.
|
||||
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
|
||||
|
||||
CREATE_SUBDIRS_LEVEL = 8
|
||||
|
||||
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
|
||||
# characters to appear in the names of generated files. If set to NO, non-ASCII
|
||||
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
|
||||
@ -81,26 +103,18 @@ ALLOW_UNICODE_NAMES = NO
|
||||
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all constant output in the proper language.
|
||||
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
|
||||
# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
|
||||
# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
|
||||
# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
|
||||
# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
|
||||
# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
|
||||
# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
|
||||
# Ukrainian and Vietnamese.
|
||||
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
|
||||
# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
|
||||
# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
|
||||
# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
|
||||
# English messages), Korean, Korean-en (Korean with English messages), Latvian,
|
||||
# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
|
||||
# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
|
||||
# Swedish, Turkish, Ukrainian and Vietnamese.
|
||||
# The default value is: English.
|
||||
|
||||
OUTPUT_LANGUAGE = English
|
||||
|
||||
# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all generated output in the proper direction.
|
||||
# Possible values are: None, LTR, RTL and Context.
|
||||
# The default value is: None.
|
||||
|
||||
OUTPUT_TEXT_DIRECTION = None
|
||||
|
||||
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
|
||||
# descriptions after the members that are listed in the file and class
|
||||
# documentation (similar to Javadoc). Set to NO to disable this.
|
||||
@ -258,16 +272,16 @@ TAB_SIZE = 4
|
||||
# the documentation. An alias has the form:
|
||||
# name=value
|
||||
# For example adding
|
||||
# "sideeffect=@par Side Effects:\n"
|
||||
# "sideeffect=@par Side Effects:^^"
|
||||
# will allow you to put the command \sideeffect (or @sideeffect) in the
|
||||
# documentation, which will result in a user-defined paragraph with heading
|
||||
# "Side Effects:". You can put \n's in the value part of an alias to insert
|
||||
# newlines (in the resulting output). You can put ^^ in the value part of an
|
||||
# alias to insert a newline as if a physical newline was in the original file.
|
||||
# When you need a literal { or } or , in the value part of an alias you have to
|
||||
# escape them by means of a backslash (\), this can lead to conflicts with the
|
||||
# commands \{ and \} for these it is advised to use the version @{ and @} or use
|
||||
# a double escape (\\{ and \\})
|
||||
# "Side Effects:". Note that you cannot put \n's in the value part of an alias
|
||||
# to insert newlines (in the resulting output). You can put ^^ in the value part
|
||||
# of an alias to insert a newline as if a physical newline was in the original
|
||||
# file. When you need a literal { or } or , in the value part of an alias you
|
||||
# have to escape them by means of a backslash (\), this can lead to conflicts
|
||||
# with the commands \{ and \} for these it is advised to use the version @{ and
|
||||
# @} or use a double escape (\\{ and \\})
|
||||
|
||||
ALIASES =
|
||||
|
||||
@ -312,8 +326,8 @@ OPTIMIZE_OUTPUT_SLICE = NO
|
||||
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
||||
# using this tag. The format is ext=language, where ext is a file extension, and
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
|
||||
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
|
||||
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||
# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
|
||||
# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
|
||||
# tries to guess whether the code is fixed or free formatted code, this is the
|
||||
# default for Fortran type files). For instance to make doxygen treat .inc files
|
||||
@ -460,13 +474,13 @@ TYPEDEF_HIDES_STRUCT = YES
|
||||
|
||||
LOOKUP_CACHE_SIZE = 0
|
||||
|
||||
# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
|
||||
# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
|
||||
# during processing. When set to 0 doxygen will based this on the number of
|
||||
# cores available in the system. You can set it explicitly to a value larger
|
||||
# than 0 to get more control over the balance between CPU load and processing
|
||||
# speed. At this moment only the input processing can be done using multiple
|
||||
# threads. Since this is still an experimental feature the default is set to 1,
|
||||
# which efficively disables parallel processing. Please report any issues you
|
||||
# which effectively disables parallel processing. Please report any issues you
|
||||
# encounter. Generating dot graphs in parallel is controlled by the
|
||||
# DOT_NUM_THREADS setting.
|
||||
# Minimum value: 0, maximum value: 32, default value: 1.
|
||||
@ -554,7 +568,8 @@ HIDE_UNDOC_MEMBERS = NO
|
||||
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
|
||||
# undocumented classes that are normally visible in the class hierarchy. If set
|
||||
# to NO, these classes will be included in the various overviews. This option
|
||||
# has no effect if EXTRACT_ALL is enabled.
|
||||
# will also hide undocumented C++ concepts if enabled. This option has no effect
|
||||
# if EXTRACT_ALL is enabled.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
@ -585,14 +600,15 @@ INTERNAL_DOCS = NO
|
||||
# filesystem is case sensitive (i.e. it supports files in the same directory
|
||||
# whose names only differ in casing), the option must be set to YES to properly
|
||||
# deal with such files in case they appear in the input. For filesystems that
|
||||
# are not case sensitive the option should be be set to NO to properly deal with
|
||||
# are not case sensitive the option should be set to NO to properly deal with
|
||||
# output files written for symbols that only differ in casing, such as for two
|
||||
# classes, one named CLASS and the other named Class, and to also support
|
||||
# references to files without having to specify the exact matching casing. On
|
||||
# Windows (including Cygwin) and MacOS, users should typically set this option
|
||||
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
||||
# YES.
|
||||
# The default value is: system dependent.
|
||||
# Possible values are: SYSTEM, NO and YES.
|
||||
# The default value is: SYSTEM.
|
||||
|
||||
CASE_SENSE_NAMES = NO
|
||||
|
||||
@ -610,6 +626,12 @@ HIDE_SCOPE_NAMES = NO
|
||||
|
||||
HIDE_COMPOUND_REFERENCE= NO
|
||||
|
||||
# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
|
||||
# will show which file needs to be included to use the class.
|
||||
# The default value is: YES.
|
||||
|
||||
SHOW_HEADERFILE = YES
|
||||
|
||||
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
|
||||
# the files that are included by a file in the documentation of that file.
|
||||
# The default value is: YES.
|
||||
@ -767,7 +789,8 @@ FILE_VERSION_FILTER =
|
||||
# output files in an output format independent way. To create the layout file
|
||||
# that represents doxygen's defaults, run doxygen with the -l option. You can
|
||||
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
|
||||
# will be used as the name of the layout file.
|
||||
# will be used as the name of the layout file. See also section "Changing the
|
||||
# layout of pages" for information.
|
||||
#
|
||||
# Note that if you run doxygen from a directory containing a file called
|
||||
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
|
||||
@ -813,22 +836,38 @@ WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
|
||||
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
|
||||
# potential errors in the documentation, such as not documenting some parameters
|
||||
# in a documented function, or documenting parameters that don't exist or using
|
||||
# markup commands wrongly.
|
||||
# potential errors in the documentation, such as documenting some parameters in
|
||||
# a documented function twice, or documenting parameters that don't exist or
|
||||
# using markup commands wrongly.
|
||||
# The default value is: YES.
|
||||
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
|
||||
# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
|
||||
# function parameter documentation. If set to NO, doxygen will accept that some
|
||||
# parameters have no documentation without warning.
|
||||
# The default value is: YES.
|
||||
|
||||
WARN_IF_INCOMPLETE_DOC = YES
|
||||
|
||||
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
|
||||
# are documented, but have no documentation for their parameters or return
|
||||
# value. If set to NO, doxygen will only warn about wrong or incomplete
|
||||
# parameter documentation, but not about the absence of documentation. If
|
||||
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
|
||||
# value. If set to NO, doxygen will only warn about wrong parameter
|
||||
# documentation, but not about the absence of documentation. If EXTRACT_ALL is
|
||||
# set to YES then this flag will automatically be disabled. See also
|
||||
# WARN_IF_INCOMPLETE_DOC
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_NO_PARAMDOC = NO
|
||||
|
||||
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
|
||||
# undocumented enumeration values. If set to NO, doxygen will accept
|
||||
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
|
||||
# will automatically be disabled.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_IF_UNDOC_ENUM_VAL = NO
|
||||
|
||||
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||
@ -844,13 +883,27 @@ WARN_AS_ERROR = NO
|
||||
# and the warning text. Optionally the format may contain $version, which will
|
||||
# be replaced by the version of the file (if it could be obtained via
|
||||
# FILE_VERSION_FILTER)
|
||||
# See also: WARN_LINE_FORMAT
|
||||
# The default value is: $file:$line: $text.
|
||||
|
||||
WARN_FORMAT = "$file:$line: $text"
|
||||
|
||||
# In the $text part of the WARN_FORMAT command it is possible that a reference
|
||||
# to a more specific place is given. To make it easier to jump to this place
|
||||
# (outside of doxygen) the user can define a custom "cut" / "paste" string.
|
||||
# Example:
|
||||
# WARN_LINE_FORMAT = "'vi $file +$line'"
|
||||
# See also: WARN_FORMAT
|
||||
# The default value is: at line $line of file $file.
|
||||
|
||||
WARN_LINE_FORMAT = "at line $line of file $file"
|
||||
|
||||
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
|
||||
# messages should be written. If left blank the output is written to standard
|
||||
# error (stderr).
|
||||
# error (stderr). In case the file specified cannot be opened for writing the
|
||||
# warning and error messages are written to standard error. When as file - is
|
||||
# specified the warning and error messages are written to standard output
|
||||
# (stdout).
|
||||
|
||||
WARN_LOGFILE =
|
||||
|
||||
@ -877,10 +930,21 @@ INPUT = overall.md \
|
||||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||
# documentation (see:
|
||||
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
||||
# See also: INPUT_FILE_ENCODING
|
||||
# The default value is: UTF-8.
|
||||
|
||||
INPUT_ENCODING = UTF-8
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
|
||||
# character encoding on a per file pattern basis. Doxygen will compare the file
|
||||
# name with each pattern and apply the encoding instead of the default
|
||||
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
|
||||
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
|
||||
# "INPUT_ENCODING" for further information on supported encodings.
|
||||
|
||||
INPUT_FILE_ENCODING =
|
||||
|
||||
# If the value of the INPUT tag contains directories, you can use the
|
||||
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
|
||||
# *.h) to filter out the source-files in the directories.
|
||||
@ -894,10 +958,10 @@ INPUT_ENCODING = UTF-8
|
||||
#
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
|
||||
# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
|
||||
# *.ucf, *.qsf and *.ice.
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
|
||||
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
|
||||
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.h
|
||||
|
||||
@ -936,20 +1000,40 @@ EXCLUDE_PATTERNS =
|
||||
# (namespaces, classes, functions, etc.) that should be excluded from the
|
||||
# output. The symbol name can be a fully qualified name, a word, or if the
|
||||
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
||||
# AClass::ANamespace, ANamespace::*Test
|
||||
# ANamespace::AClass, ANamespace::*Test
|
||||
#
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS = NOMINMAX __ORDER_BIG_ENDIAN__ __ORDER_LITTLE_ENDIAN__ \
|
||||
__has_include __has_attribute __has_builtin __has_cpp_attribute __has_extension __has_feature \
|
||||
HAVE_STRUCT_IOVEC MDBX_STRINGIFY_HELPER MDBX_STRINGIFY \
|
||||
MDBX_NOSANITIZE_ENUM MDBX_PRINTF_ARGS \
|
||||
MDBX_HAVE_CXX20_CONCEPTS \
|
||||
CONSTEXPR_ENUM_FLAGS_OPERATIONS DEFINE_ENUM_FLAG_OPERATORS \
|
||||
bool false true __dll_export __dll_import \
|
||||
MDBX_64BIT_ATOMIC_CONFIG MDBX_64BIT_CAS_CONFIG MDBX_ENV_CHECKPID_CONFIG MDBX_LOCKING_CONFIG \
|
||||
MDBX_TRUST_RTC_CONFIG MDBX_TXN_CHECKOWNER_CONFIG MDBX_USE_OFDLOCKS_CONFIG
|
||||
EXCLUDE_SYMBOLS = NOMINMAX \
|
||||
__ORDER_BIG_ENDIAN__ \
|
||||
__ORDER_LITTLE_ENDIAN__ \
|
||||
__has_include \
|
||||
__has_attribute \
|
||||
__has_builtin \
|
||||
__has_cpp_attribute \
|
||||
__has_extension \
|
||||
__has_feature \
|
||||
HAVE_STRUCT_IOVEC \
|
||||
MDBX_STRINGIFY_HELPER \
|
||||
MDBX_STRINGIFY \
|
||||
MDBX_NOSANITIZE_ENUM \
|
||||
MDBX_PRINTF_ARGS \
|
||||
MDBX_HAVE_CXX20_CONCEPTS \
|
||||
CONSTEXPR_ENUM_FLAGS_OPERATIONS \
|
||||
DEFINE_ENUM_FLAG_OPERATORS \
|
||||
bool \
|
||||
false \
|
||||
true \
|
||||
__dll_export \
|
||||
__dll_import \
|
||||
MDBX_64BIT_ATOMIC_CONFIG \
|
||||
MDBX_64BIT_CAS_CONFIG \
|
||||
MDBX_ENV_CHECKPID_CONFIG \
|
||||
MDBX_LOCKING_CONFIG \
|
||||
MDBX_TRUST_RTC_CONFIG \
|
||||
MDBX_TXN_CHECKOWNER_CONFIG \
|
||||
MDBX_USE_OFDLOCKS_CONFIG
|
||||
|
||||
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
|
||||
# that contain example code fragments that are included (see the \include
|
||||
@ -992,6 +1076,11 @@ IMAGE_PATH =
|
||||
# code is scanned, but not when the output code is generated. If lines are added
|
||||
# or removed, the anchors will not be placed correctly.
|
||||
#
|
||||
# Note that doxygen will use the data processed and written to standard output
|
||||
# for further processing, therefore nothing else, like debug statements or used
|
||||
# commands (so in case of a Windows batch file always use @echo OFF), should be
|
||||
# written to standard output.
|
||||
#
|
||||
# Note that for custom extensions or not directly supported extensions you also
|
||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||
# properly processed by doxygen.
|
||||
@ -1033,6 +1122,15 @@ FILTER_SOURCE_PATTERNS =
|
||||
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
|
||||
# The Fortran standard specifies that for fixed formatted Fortran code all
|
||||
# characters from position 72 are to be considered as comment. A common
|
||||
# extension is to allow longer lines before the automatic comment starts. The
|
||||
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
|
||||
# be processed before the automatic comment starts.
|
||||
# Minimum value: 7, maximum value: 10000, default value: 72.
|
||||
|
||||
FORTRAN_COMMENT_AFTER = 72
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
@ -1130,9 +1228,11 @@ VERBATIM_HEADERS = YES
|
||||
|
||||
CLANG_ASSISTED_PARSING = NO
|
||||
|
||||
# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to
|
||||
# YES then doxygen will add the directory of each input to the include path.
|
||||
# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
|
||||
# tag is set to YES then doxygen will add the directory of each input to the
|
||||
# include path.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
|
||||
|
||||
CLANG_ADD_INC_PATHS = YES
|
||||
|
||||
@ -1168,10 +1268,11 @@ CLANG_DATABASE_PATH =
|
||||
|
||||
ALPHABETICAL_INDEX = YES
|
||||
|
||||
# In case all classes in a project start with a common prefix, all classes will
|
||||
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
|
||||
# can be used to specify a prefix (or a list of prefixes) that should be ignored
|
||||
# while generating the index headers.
|
||||
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
|
||||
# that should be ignored while generating the index headers. The IGNORE_PREFIX
|
||||
# tag works for classes, function and member names. The entity will be placed in
|
||||
# the alphabetical list under the first letter of the entity name that remains
|
||||
# after removing the prefix.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
IGNORE_PREFIX =
|
||||
@ -1250,7 +1351,12 @@ HTML_STYLESHEET =
|
||||
# Doxygen will copy the style sheet files to the output directory.
|
||||
# Note: The order of the extra style sheet files is of importance (e.g. the last
|
||||
# style sheet in the list overrules the setting of the previous ones in the
|
||||
# list). For an example see the documentation.
|
||||
# list).
|
||||
# Note: Since the styling of scrollbars can currently not be overruled in
|
||||
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
|
||||
# one or more extra stylesheets have been specified. So if scrollbar
|
||||
# customization is desired it has to be added explicitly. For an example see the
|
||||
# documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
@ -1265,9 +1371,22 @@ HTML_EXTRA_STYLESHEET =
|
||||
|
||||
HTML_EXTRA_FILES =
|
||||
|
||||
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
|
||||
# should be rendered with a dark or light theme.
|
||||
# Possible values are: LIGHT always generate light mode output, DARK always
|
||||
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
|
||||
# the user preference, use light mode if no preference is set (the default),
|
||||
# AUTO_DARK automatically set the mode according to the user preference, use
|
||||
# dark mode if no preference is set and TOGGLE allow to user to switch between
|
||||
# light and dark mode via a button.
|
||||
# The default value is: AUTO_LIGHT.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_COLORSTYLE = AUTO_LIGHT
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
# this color. Hue is specified as an angle on a colorwheel, see
|
||||
# this color. Hue is specified as an angle on a color-wheel, see
|
||||
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
|
||||
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
|
||||
# purple, and 360 is red again.
|
||||
@ -1277,7 +1396,7 @@ HTML_EXTRA_FILES =
|
||||
HTML_COLORSTYLE_HUE = 220
|
||||
|
||||
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
|
||||
# in the HTML output. For a value of 0 the output will use grayscales only. A
|
||||
# in the HTML output. For a value of 0 the output will use gray-scales only. A
|
||||
# value of 255 will produce the most vivid colors.
|
||||
# Minimum value: 0, maximum value: 255, default value: 100.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
@ -1359,6 +1478,13 @@ GENERATE_DOCSET = NO
|
||||
|
||||
DOCSET_FEEDNAME = "Doxygen generated docs"
|
||||
|
||||
# This tag determines the URL of the docset feed. A documentation feed provides
|
||||
# an umbrella under which multiple documentation sets from a single provider
|
||||
# (such as a company or product suite) can be grouped.
|
||||
# This tag requires that the tag GENERATE_DOCSET is set to YES.
|
||||
|
||||
DOCSET_FEEDURL =
|
||||
|
||||
# This tag specifies a string that should uniquely identify the documentation
|
||||
# set bundle. This should be a reverse domain-name style string, e.g.
|
||||
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
|
||||
@ -1384,8 +1510,12 @@ DOCSET_PUBLISHER_NAME = Publisher
|
||||
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
|
||||
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
|
||||
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
|
||||
# (see:
|
||||
# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
|
||||
# on Windows. In the beginning of 2021 Microsoft took the original page, with
|
||||
# a.o. the download links, offline the HTML help workshop was already many years
|
||||
# in maintenance mode). You can download the HTML help workshop from the web
|
||||
# archives at Installation executable (see:
|
||||
# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
|
||||
# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
|
||||
#
|
||||
# The HTML Help Workshop contains a compiler that can convert all HTML output
|
||||
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
|
||||
@ -1544,16 +1674,28 @@ DISABLE_INDEX = YES
|
||||
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
|
||||
# (i.e. any modern browser). Windows users are probably better off using the
|
||||
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
|
||||
# further fine-tune the look of the index. As an example, the default style
|
||||
# sheet generated by doxygen has an example that shows how to put an image at
|
||||
# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
|
||||
# the same information as the tab index, you could consider setting
|
||||
# DISABLE_INDEX to YES when enabling this option.
|
||||
# further fine tune the look of the index (see "Fine-tuning the output"). As an
|
||||
# example, the default style sheet generated by doxygen has an example that
|
||||
# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
|
||||
# Since the tree basically has the same information as the tab index, you could
|
||||
# consider setting DISABLE_INDEX to YES when enabling this option.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
GENERATE_TREEVIEW = YES
|
||||
|
||||
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
|
||||
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
|
||||
# area (value NO) or if it should extend to the full height of the window (value
|
||||
# YES). Setting this to YES gives a layout similar to
|
||||
# https://docs.readthedocs.io with more room for contents, but less room for the
|
||||
# project logo, title, and description. If either GENERATE_TREEVIEW or
|
||||
# DISABLE_INDEX is set to NO, this option has no effect.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
FULL_SIDEBAR = NO
|
||||
|
||||
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
|
||||
# doxygen will group on one line in the generated HTML documentation.
|
||||
#
|
||||
@ -1578,6 +1720,13 @@ TREEVIEW_WIDTH = 250
|
||||
|
||||
EXT_LINKS_IN_WINDOW = NO
|
||||
|
||||
# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
|
||||
# addresses.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
OBFUSCATE_EMAILS = YES
|
||||
|
||||
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
|
||||
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
|
||||
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
|
||||
@ -1598,17 +1747,6 @@ HTML_FORMULA_FORMAT = png
|
||||
|
||||
FORMULA_FONTSIZE = 10
|
||||
|
||||
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
|
||||
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||
#
|
||||
# Note that when changing this option you need to delete any form_*.png files in
|
||||
# the HTML output directory before the changes have effect.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||
# the section "Including formulas" for details.
|
||||
@ -1626,11 +1764,29 @@ FORMULA_MACROFILE =
|
||||
|
||||
USE_MATHJAX = YES
|
||||
|
||||
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
|
||||
# Note that the different versions of MathJax have different requirements with
|
||||
# regards to the different settings, so it is possible that also other MathJax
|
||||
# settings have to be changed when switching between the different MathJax
|
||||
# versions.
|
||||
# Possible values are: MathJax_2 and MathJax_3.
|
||||
# The default value is: MathJax_2.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_VERSION = MathJax_2
|
||||
|
||||
# When MathJax is enabled you can set the default output format to be used for
|
||||
# the MathJax output. See the MathJax site (see:
|
||||
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
|
||||
# the MathJax output. For more details about the output format see MathJax
|
||||
# version 2 (see:
|
||||
# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
|
||||
# (see:
|
||||
# http://docs.mathjax.org/en/latest/web/components/output.html).
|
||||
# Possible values are: HTML-CSS (which is slower, but has the best
|
||||
# compatibility), NativeMML (i.e. MathML) and SVG.
|
||||
# compatibility. This is the name for Mathjax version 2, for MathJax version 3
|
||||
# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
|
||||
# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
|
||||
# is the name for Mathjax version 3, for MathJax version 2 this will be
|
||||
# translated into HTML-CSS) and SVG.
|
||||
# The default value is: HTML-CSS.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
@ -1643,15 +1799,21 @@ MATHJAX_FORMAT = HTML-CSS
|
||||
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
|
||||
# Content Delivery Network so you can quickly see the result without installing
|
||||
# MathJax. However, it is strongly recommended to install a local copy of
|
||||
# MathJax from https://www.mathjax.org before deployment.
|
||||
# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
|
||||
# MathJax from https://www.mathjax.org before deployment. The default value is:
|
||||
# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
|
||||
# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
|
||||
|
||||
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
|
||||
# extension names that should be enabled during MathJax rendering. For example
|
||||
# for MathJax version 2 (see
|
||||
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
|
||||
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
|
||||
# For example for MathJax version 3 (see
|
||||
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
|
||||
# MATHJAX_EXTENSIONS = ams
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_EXTENSIONS =
|
||||
@ -1831,29 +1993,31 @@ PAPER_TYPE = a4
|
||||
|
||||
EXTRA_PACKAGES =
|
||||
|
||||
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
|
||||
# generated LaTeX document. The header should contain everything until the first
|
||||
# chapter. If it is left blank doxygen will generate a standard header. See
|
||||
# section "Doxygen usage" for information on how to let doxygen write the
|
||||
# default header to a separate file.
|
||||
# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
|
||||
# the generated LaTeX document. The header should contain everything until the
|
||||
# first chapter. If it is left blank doxygen will generate a standard header. It
|
||||
# is highly recommended to start with a default header using
|
||||
# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
|
||||
# and then modify the file new_header.tex. See also section "Doxygen usage" for
|
||||
# information on how to generate the default header that doxygen normally uses.
|
||||
#
|
||||
# Note: Only use a user-defined header if you know what you are doing! The
|
||||
# following commands have a special meaning inside the header: $title,
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
|
||||
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
|
||||
# string, for the replacement values of the other commands the user is referred
|
||||
# to HTML_HEADER.
|
||||
# Note: Only use a user-defined header if you know what you are doing!
|
||||
# Note: The header is subject to change so you typically have to regenerate the
|
||||
# default header when upgrading to a newer version of doxygen. The following
|
||||
# commands have a special meaning inside the header (and footer): For a
|
||||
# description of the possible markers and block names see the documentation.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_HEADER =
|
||||
|
||||
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
|
||||
# generated LaTeX document. The footer should contain everything after the last
|
||||
# chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
|
||||
# the generated LaTeX document. The footer should contain everything after the
|
||||
# last chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# LATEX_HEADER for more information on how to generate a default footer and what
|
||||
# special commands can be used inside the footer.
|
||||
#
|
||||
# Note: Only use a user-defined footer if you know what you are doing!
|
||||
# special commands can be used inside the footer. See also section "Doxygen
|
||||
# usage" for information on how to generate the default footer that doxygen
|
||||
# normally uses. Note: Only use a user-defined footer if you know what you are
|
||||
# doing!
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_FOOTER =
|
||||
@ -1898,8 +2062,7 @@ USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
# if errors occur, instead of asking the user for help. This option is also used
|
||||
# when generating formulas in HTML.
|
||||
# if errors occur, instead of asking the user for help.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
@ -1912,16 +2075,6 @@ LATEX_BATCHMODE = NO
|
||||
|
||||
LATEX_HIDE_INDICES = NO
|
||||
|
||||
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
|
||||
# code with syntax highlighting in the LaTeX output.
|
||||
#
|
||||
# Note that which sources are shown also depends on other settings such as
|
||||
# SOURCE_BROWSER.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_SOURCE_CODE = NO
|
||||
|
||||
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
|
||||
# bibliography, e.g. plainnat, or ieeetr. See
|
||||
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
@ -2002,16 +2155,6 @@ RTF_STYLESHEET_FILE =
|
||||
|
||||
RTF_EXTENSIONS_FILE =
|
||||
|
||||
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
|
||||
# with syntax highlighting in the RTF output.
|
||||
#
|
||||
# Note that which sources are shown also depends on other settings such as
|
||||
# SOURCE_BROWSER.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_SOURCE_CODE = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
@ -2108,15 +2251,6 @@ GENERATE_DOCBOOK = NO
|
||||
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
|
||||
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
|
||||
# program listings (including syntax highlighting and cross-referencing
|
||||
# information) to the DOCBOOK output. Note that enabling this will significantly
|
||||
# increase the size of the DOCBOOK output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
|
||||
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
@ -2203,7 +2337,8 @@ SEARCH_INCLUDES = YES
|
||||
|
||||
# The INCLUDE_PATH tag can be used to specify one or more directories that
|
||||
# contain include files that are not input files but should be processed by the
|
||||
# preprocessor.
|
||||
# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
|
||||
# RECURSIVE has no effect here.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH =
|
||||
@ -2224,24 +2359,30 @@ INCLUDE_FILE_PATTERNS =
|
||||
# recursively expanded use the := operator instead of the = operator.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
|
||||
PREDEFINED = DOXYGEN \
|
||||
MDBX_CXX20_CONCEPT(CONCEPT,NAME)="CONCEPT NAME" \
|
||||
MDBX_STD_FILESYSTEM_PATH=::mdbx::filesystem::path \
|
||||
MDBX_U128_TYPE=uint128_t MDBX_I128_TYPE=int128_t \
|
||||
MDBX_DECLARE_EXCEPTION(NAME)="struct LIBMDBX_API_TYPE NAME : public exception{NAME(const ::mdbx::error &); virtual ~NAME() noexcept; }" \
|
||||
MDBX_PURE_FUNCTION=[[gnu::pure]] \
|
||||
MDBX_NOTHROW_PURE_FUNCTION="[[gnu::pure, gnu::nothrow]]" \
|
||||
MDBX_CONST_FUNCTION=[[gnu::const]] \
|
||||
MDBX_NOTHROW_CONST_FUNCTION="[[gnu::const, gnu::nothrow]]" \
|
||||
MDBX_CXX01_CONSTEXPR=constexpr MDBX_CXX01_CONSTEXPR_VAR=constexpr \
|
||||
MDBX_CXX11_CONSTEXPR=constexpr MDBX_CXX11_CONSTEXPR_VAR=constexpr \
|
||||
MDBX_CXX14_CONSTEXPR=constexpr MDBX_CXX14_CONSTEXPR_VAR=constexpr \
|
||||
MDBX_CXX17_CONSTEXPR=constexpr MDBX_CXX20_CONSTEXPR=constexpr \
|
||||
MDBX_CXX17_NOEXCEPT=noexcept MDBX_IF_CONSTEXPR=constexpr \
|
||||
MDBX_CXX20_LIKELY=[[likely]] MDBX_CXX20_UNLIKELY=[[unlikely]] \
|
||||
MDBX_MAYBE_UNUSED=[[maybe_unused]] \
|
||||
MDBX_DEPRECATED=[[deprecated]]
|
||||
"MDBX_CXX20_CONCEPT(CONCEPT,NAME)=CONCEPT NAME" \
|
||||
MDBX_STD_FILESYSTEM_PATH=::mdbx::filesystem::path \
|
||||
MDBX_U128_TYPE=uint128_t \
|
||||
MDBX_I128_TYPE=int128_t \
|
||||
"MDBX_DECLARE_EXCEPTION(NAME)=struct LIBMDBX_API_TYPE NAME : public exception{NAME(const ::mdbx::error &); virtual ~NAME() noexcept; }" \
|
||||
MDBX_PURE_FUNCTION=[[gnu::pure]] \
|
||||
"MDBX_NOTHROW_PURE_FUNCTION=[[gnu::pure, gnu::nothrow]]" \
|
||||
MDBX_CONST_FUNCTION=[[gnu::const]] \
|
||||
"MDBX_NOTHROW_CONST_FUNCTION=[[gnu::const, gnu::nothrow]]" \
|
||||
MDBX_CXX01_CONSTEXPR=constexpr \
|
||||
MDBX_CXX01_CONSTEXPR_VAR=constexpr \
|
||||
MDBX_CXX11_CONSTEXPR=constexpr \
|
||||
MDBX_CXX11_CONSTEXPR_VAR=constexpr \
|
||||
MDBX_CXX14_CONSTEXPR=constexpr \
|
||||
MDBX_CXX14_CONSTEXPR_VAR=constexpr \
|
||||
MDBX_CXX17_CONSTEXPR=constexpr \
|
||||
MDBX_CXX20_CONSTEXPR=constexpr \
|
||||
MDBX_CXX17_NOEXCEPT=noexcept \
|
||||
MDBX_IF_CONSTEXPR=constexpr \
|
||||
MDBX_CXX20_LIKELY=[[likely]] \
|
||||
MDBX_CXX20_UNLIKELY=[[unlikely]] \
|
||||
MDBX_MAYBE_UNUSED=[[maybe_unused]] \
|
||||
MDBX_DEPRECATED=[[deprecated]]
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
@ -2312,15 +2453,6 @@ EXTERNAL_PAGES = NO
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
|
||||
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
|
||||
# NO turns the diagrams off. Note that this option also works with HAVE_DOT
|
||||
# disabled, but it is recommended to install and use dot, since it yields more
|
||||
# powerful graphs.
|
||||
# The default value is: YES.
|
||||
|
||||
CLASS_DIAGRAMS = NO
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
@ -2339,7 +2471,7 @@ HIDE_UNDOC_RELATIONS = YES
|
||||
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# Bell Labs. The other options in this section have no effect if this option is
|
||||
# set to NO
|
||||
# The default value is: YES.
|
||||
# The default value is: NO.
|
||||
|
||||
HAVE_DOT = NO
|
||||
|
||||
@ -2353,37 +2485,52 @@ HAVE_DOT = NO
|
||||
|
||||
DOT_NUM_THREADS = 0
|
||||
|
||||
# When you want a differently looking font in the dot files that doxygen
|
||||
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||
# sure dot is able to find the font, which can be done by putting it in a
|
||||
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||
# setting DOT_FONTPATH to the directory containing the font.
|
||||
# The default value is: Helvetica.
|
||||
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
|
||||
# subgraphs. When you want a differently looking font in the dot files that
|
||||
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
|
||||
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
|
||||
# Edge and Graph Attributes specification</a> You need to make sure dot is able
|
||||
# to find the font, which can be done by putting it in a standard location or by
|
||||
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
|
||||
# directory containing the font. Default graphviz fontsize is 14.
|
||||
# The default value is: fontname=Helvetica,fontsize=10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTNAME = Helvetica
|
||||
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
|
||||
|
||||
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
|
||||
# dot graphs.
|
||||
# Minimum value: 4, maximum value: 24, default value: 10.
|
||||
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
|
||||
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
|
||||
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
|
||||
# arrows shapes.</a>
|
||||
# The default value is: labelfontname=Helvetica,labelfontsize=10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTSIZE = 10
|
||||
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
|
||||
|
||||
# By default doxygen will tell dot to use the default font as specified with
|
||||
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
|
||||
# the path where dot can find it using this tag.
|
||||
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
|
||||
# around nodes set 'shape=plain' or 'shape=plaintext' <a
|
||||
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
|
||||
# The default value is: shape=box,height=0.2,width=0.4.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
|
||||
|
||||
# You can set the path where dot can find font specified with fontname in
|
||||
# DOT_COMMON_ATTR and others dot attributes.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
|
||||
# each documented class showing the direct and indirect inheritance relations.
|
||||
# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
|
||||
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect inheritance
|
||||
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
|
||||
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
|
||||
# to TEXT the direct and indirect inheritance relations will be shown as texts /
|
||||
# links.
|
||||
# Possible values are: NO, YES, TEXT and GRAPH.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
CLASS_GRAPH = YES
|
||||
CLASS_GRAPH = TEXT
|
||||
|
||||
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect implementation
|
||||
@ -2395,7 +2542,8 @@ CLASS_GRAPH = YES
|
||||
COLLABORATION_GRAPH = YES
|
||||
|
||||
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
|
||||
# groups, showing the direct groups dependencies.
|
||||
# groups, showing the direct groups dependencies. See also the chapter Grouping
|
||||
# in the manual.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@ -2510,6 +2658,13 @@ GRAPHICAL_HIERARCHY = YES
|
||||
|
||||
DIRECTORY_GRAPH = YES
|
||||
|
||||
# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
|
||||
# of child directories generated in directory dependency graphs by dot.
|
||||
# Minimum value: 1, maximum value: 25, default value: 1.
|
||||
# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
|
||||
|
||||
DIR_GRAPH_MAX_DEPTH = 1
|
||||
|
||||
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
||||
# generated by dot. For an explanation of the image formats see the section
|
||||
# output formats in the documentation of the dot tool (Graphviz (see:
|
||||
@ -2517,9 +2672,7 @@ DIRECTORY_GRAPH = YES
|
||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||
# requirement).
|
||||
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
|
||||
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
|
||||
# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
|
||||
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
|
||||
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
|
||||
# png:gdiplus:gdiplus.
|
||||
# The default value is: png.
|
||||
@ -2565,10 +2718,10 @@ MSCFILE_DIRS =
|
||||
DIAFILE_DIRS =
|
||||
|
||||
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
|
||||
# path where java can find the plantuml.jar file. If left blank, it is assumed
|
||||
# PlantUML is not used or called during a preprocessing step. Doxygen will
|
||||
# generate a warning when it encounters a \startuml command in this case and
|
||||
# will not generate output for the diagram.
|
||||
# path where java can find the plantuml.jar file or to the filename of jar file
|
||||
# to be used. If left blank, it is assumed PlantUML is not used or called during
|
||||
# a preprocessing step. Doxygen will generate a warning when it encounters a
|
||||
# \startuml command in this case and will not generate output for the diagram.
|
||||
|
||||
PLANTUML_JAR_PATH =
|
||||
|
||||
@ -2606,18 +2759,6 @@ DOT_GRAPH_MAX_NODES = 50
|
||||
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
|
||||
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||
# background. This is disabled by default, because dot on Windows does not seem
|
||||
# to support this out of the box.
|
||||
#
|
||||
# Warning: Depending on the platform used, enabling this option may lead to
|
||||
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
|
||||
# read).
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_TRANSPARENT = NO
|
||||
|
||||
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
||||
# files in one run (i.e. multiple -o and -T options on the command line). This
|
||||
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
||||
@ -2630,6 +2771,8 @@ DOT_MULTI_TARGETS = NO
|
||||
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
|
||||
# explaining the meaning of the various boxes and arrows in the dot generated
|
||||
# graphs.
|
||||
# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
|
||||
# graphical representation for inheritance and collaboration diagrams is used.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@ -2638,8 +2781,8 @@ GENERATE_LEGEND = YES
|
||||
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
|
||||
# files that are used to generate the various graphs.
|
||||
#
|
||||
# Note: This setting is not only used for dot files but also for msc and
|
||||
# plantuml temporary files.
|
||||
# Note: This setting is not only used for dot files but also for msc temporary
|
||||
# files.
|
||||
# The default value is: YES.
|
||||
|
||||
DOT_CLEANUP = YES
|
||||
|
@ -106,6 +106,7 @@ reservation can deplete system resources (trigger ENOMEM error, etc)
|
||||
when setting an inadequately large upper DB size using \ref
|
||||
mdbx_env_set_geometry() or \ref mdbx::env::geometry. So just avoid this.
|
||||
|
||||
|
||||
## Remote filesystems
|
||||
Do not use MDBX databases on remote filesystems, even between processes
|
||||
on the same host. This breaks file locks on some platforms, possibly
|
||||
@ -132,6 +133,11 @@ corruption in such cases.
|
||||
On the other hand, MDBX allow calling \ref mdbx_env_close() in such cases to
|
||||
release resources, but no more and in general this is a wrong way.
|
||||
|
||||
#### Since v0.13.1 and later
|
||||
Начиная с версии 0.13.1 в API доступна функция \ref mdbx_env_resurrect_after_fork(),
|
||||
которая позволяет пере-использовать в дочерних процессах уже открытую среду БД,
|
||||
но строго без наследования транзакций от родительского процесса.
|
||||
|
||||
|
||||
## Read-only mode
|
||||
There is no pure read-only mode in a normal explicitly way, since
|
||||
|
526
mdbx.h
526
mdbx.h
@ -634,9 +634,9 @@ typedef mode_t mdbx_mode_t;
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* MDBX version 0.12.x */
|
||||
/* MDBX version 0.13.x */
|
||||
#define MDBX_VERSION_MAJOR 0
|
||||
#define MDBX_VERSION_MINOR 12
|
||||
#define MDBX_VERSION_MINOR 13
|
||||
|
||||
#ifndef LIBMDBX_API
|
||||
#if defined(LIBMDBX_EXPORTS)
|
||||
@ -816,7 +816,7 @@ typedef struct iovec MDBX_val;
|
||||
#endif /* ! SunOS */
|
||||
|
||||
enum MDBX_constants {
|
||||
/** The hard limit for DBI handles */
|
||||
/** The hard limit for DBI handles. */
|
||||
MDBX_MAX_DBI = UINT32_C(32765),
|
||||
|
||||
/** The maximum size of a data item. */
|
||||
@ -1778,7 +1778,7 @@ enum MDBX_cursor_op {
|
||||
* return both key and data, and the return code depends on whether a
|
||||
* upper-bound was found.
|
||||
*
|
||||
* For non DUPSORT-ed collections this work the same to \ref MDBX_SET_RANGE,
|
||||
* For non DUPSORT-ed collections this work like \ref MDBX_SET_RANGE,
|
||||
* but returns \ref MDBX_SUCCESS if the greater key was found or
|
||||
* \ref MDBX_NOTFOUND otherwise.
|
||||
*
|
||||
@ -1786,7 +1786,28 @@ enum MDBX_cursor_op {
|
||||
* i.e. for a pairs/tuples of a key and an each data value of duplicates.
|
||||
* Returns \ref MDBX_SUCCESS if the greater pair was returned or
|
||||
* \ref MDBX_NOTFOUND otherwise. */
|
||||
MDBX_SET_UPPERBOUND
|
||||
MDBX_SET_UPPERBOUND,
|
||||
|
||||
/* Doubtless cursor positioning at a specified key. */
|
||||
MDBX_TO_KEY_LESSER_THAN,
|
||||
MDBX_TO_KEY_LESSER_OR_EQUAL,
|
||||
MDBX_TO_KEY_EQUAL,
|
||||
MDBX_TO_KEY_GREATER_OR_EQUAL,
|
||||
MDBX_TO_KEY_GREATER_THAN,
|
||||
|
||||
/* Doubtless cursor positioning at a specified key-value pair
|
||||
* for dupsort/multi-value hives. */
|
||||
MDBX_TO_EXACT_KEY_VALUE_LESSER_THAN,
|
||||
MDBX_TO_EXACT_KEY_VALUE_LESSER_OR_EQUAL,
|
||||
MDBX_TO_EXACT_KEY_VALUE_EQUAL,
|
||||
MDBX_TO_EXACT_KEY_VALUE_GREATER_OR_EQUAL,
|
||||
MDBX_TO_EXACT_KEY_VALUE_GREATER_THAN,
|
||||
|
||||
MDBX_TO_PAIR_LESSER_THAN,
|
||||
MDBX_TO_PAIR_LESSER_OR_EQUAL,
|
||||
MDBX_TO_PAIR_EQUAL,
|
||||
MDBX_TO_PAIR_GREATER_OR_EQUAL,
|
||||
MDBX_TO_PAIR_GREATER_THAN
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_cursors */
|
||||
@ -1921,7 +1942,7 @@ enum MDBX_error_t {
|
||||
MDBX_TOO_LARGE = -30417,
|
||||
|
||||
/** A thread has attempted to use a not owned object,
|
||||
* e.g. a transaction that started by another thread. */
|
||||
* e.g. a transaction that started by another thread */
|
||||
MDBX_THREAD_MISMATCH = -30416,
|
||||
|
||||
/** Overlapping read and write transactions for the current thread */
|
||||
@ -1936,8 +1957,12 @@ enum MDBX_error_t {
|
||||
/** Alternative/Duplicate LCK-file is exists and should be removed manually */
|
||||
MDBX_DUPLICATED_CLK = -30413,
|
||||
|
||||
/** Some cursors and/or other resources should be closed before subDb or
|
||||
* corresponding DBI-handle could be (re)used */
|
||||
MDBX_DANGLING_DBI = -30412,
|
||||
|
||||
/* The last of MDBX-added error codes */
|
||||
MDBX_LAST_ADDED_ERRCODE = MDBX_DUPLICATED_CLK,
|
||||
MDBX_LAST_ADDED_ERRCODE = MDBX_DANGLING_DBI,
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
MDBX_ENODATA = ERROR_HANDLE_EOF,
|
||||
@ -1950,7 +1975,8 @@ enum MDBX_error_t {
|
||||
MDBX_EPERM = ERROR_INVALID_FUNCTION,
|
||||
MDBX_EINTR = ERROR_CANCELLED,
|
||||
MDBX_ENOFILE = ERROR_FILE_NOT_FOUND,
|
||||
MDBX_EREMOTE = ERROR_REMOTE_STORAGE_MEDIA_ERROR
|
||||
MDBX_EREMOTE = ERROR_REMOTE_STORAGE_MEDIA_ERROR,
|
||||
MDBX_EDEADLK = ERROR_POSSIBLE_DEADLOCK
|
||||
#else /* Windows */
|
||||
#ifdef ENODATA
|
||||
MDBX_ENODATA = ENODATA,
|
||||
@ -1966,7 +1992,8 @@ enum MDBX_error_t {
|
||||
MDBX_EPERM = EPERM,
|
||||
MDBX_EINTR = EINTR,
|
||||
MDBX_ENOFILE = ENOENT,
|
||||
MDBX_EREMOTE = ENOTBLK
|
||||
MDBX_EREMOTE = ENOTBLK,
|
||||
MDBX_EDEADLK = EDEADLK
|
||||
#endif /* !Windows */
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
@ -2106,6 +2133,7 @@ enum MDBX_option_t {
|
||||
/** \brief Controls the in-process limit to grow a list of reclaimed/recycled
|
||||
* page's numbers for finding a sequence of contiguous pages for large data
|
||||
* items.
|
||||
* \see MDBX_opt_gc_time_limit
|
||||
*
|
||||
* \details A long values requires allocation of contiguous database pages.
|
||||
* To find such sequences, it may be necessary to accumulate very large lists,
|
||||
@ -2266,6 +2294,33 @@ enum MDBX_option_t {
|
||||
* in the \ref MDBX_WRITEMAP mode by clearing ones through file handle before
|
||||
* touching. */
|
||||
MDBX_opt_prefault_write_enable,
|
||||
|
||||
/** \brief Controls the in-process spending time limit of searching
|
||||
* consecutive pages inside GC.
|
||||
* \see MDBX_opt_rp_augment_limit
|
||||
*
|
||||
* \details Задаёт ограничение времени в 1/65536 долях секунды, которое может
|
||||
* быть потрачено в ходе пишущей транзакции на поиск последовательностей
|
||||
* страниц внутри GC/freelist после достижения ограничения задаваемого опцией
|
||||
* \ref MDBX_opt_rp_augment_limit. Контроль по времени не выполняется при
|
||||
* поиске/выделении одиночных страниц и выделении страниц под нужды GC (при
|
||||
* обновлении GC в ходе фиксации транзакции).
|
||||
*
|
||||
* Задаваемый лимит времени исчисляется по "настенным часам" и контролируется
|
||||
* в рамках транзакции, наследуется для вложенных транзакций и с
|
||||
* аккумулированием в родительской при их фиксации. Контроль по времени
|
||||
* производится только при достижении ограничения задаваемого опцией \ref
|
||||
* MDBX_opt_rp_augment_limit. Это позволяет гибко управлять поведением
|
||||
* используя обе опции.
|
||||
*
|
||||
* По умолчанию ограничение устанавливается в 0, что приводит к
|
||||
* незамедлительной остановке поиска в GC при достижении \ref
|
||||
* MDBX_opt_rp_augment_limit во внутреннем состоянии транзакции и
|
||||
* соответствует поведению до появления опции `MDBX_opt_gc_time_limit`.
|
||||
* С другой стороны, при минимальном значении (включая 0)
|
||||
* `MDBX_opt_rp_augment_limit` переработка GC будет ограничиваться
|
||||
* преимущественно затраченным временем. */
|
||||
MDBX_opt_gc_time_limit
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_settings */
|
||||
@ -2571,9 +2626,7 @@ struct MDBX_envinfo {
|
||||
uint64_t mi_latter_reader_txnid; /**< ID of the last reader transaction */
|
||||
uint64_t mi_self_latter_reader_txnid; /**< ID of the last reader transaction
|
||||
of caller process */
|
||||
uint64_t mi_meta0_txnid, mi_meta0_sign;
|
||||
uint64_t mi_meta1_txnid, mi_meta1_sign;
|
||||
uint64_t mi_meta2_txnid, mi_meta2_sign;
|
||||
uint64_t mi_meta_txnid[3], mi_meta_sign[3];
|
||||
uint32_t mi_maxreaders; /**< Total reader slots in the environment */
|
||||
uint32_t mi_numreaders; /**< Max reader slots used in the environment */
|
||||
uint32_t mi_dxb_pagesize; /**< Database pagesize */
|
||||
@ -2590,7 +2643,7 @@ struct MDBX_envinfo {
|
||||
struct {
|
||||
struct {
|
||||
uint64_t x, y;
|
||||
} current, meta0, meta1, meta2;
|
||||
} current, meta[3];
|
||||
} mi_bootid;
|
||||
|
||||
/** Bytes not explicitly synchronized to disk */
|
||||
@ -2891,6 +2944,83 @@ LIBMDBX_INLINE_API(int, mdbx_env_close, (MDBX_env * env)) {
|
||||
return mdbx_env_close_ex(env, false);
|
||||
}
|
||||
|
||||
#if defined(DOXYGEN) || !(defined(_WIN32) || defined(_WIN64))
|
||||
/** \brief Восстанавливает экземпляр среды в дочернем процессе после ветвления
|
||||
* родительского процесса посредством `fork()` и родственных системных вызовов.
|
||||
* \ingroup c_extra
|
||||
*
|
||||
* Без вызова \ref mdbx_env_resurrect_after_fork() использование открытого
|
||||
* экземпляра среды в дочернем процессе не возможно, включая все выполняющиеся
|
||||
* на момент ветвления транзакции.
|
||||
*
|
||||
* Выполняемые функцией действия можно рассматривать как повторное открытие БД
|
||||
* в дочернем процессе, с сохранением заданных опций и адресов уже созданных
|
||||
* экземпляров объектов связанных с API.
|
||||
*
|
||||
* \note Функция не доступна в ОС семейства Windows по причине отсутствия
|
||||
* функционала ветвления процесса в API операционной системы.
|
||||
*
|
||||
* Ветвление не оказывает влияния на состояние MDBX-среды в родительском
|
||||
* процессе. Все транзакции, которые были в родительском процессе на момент
|
||||
* ветвления, после ветвления в родительском процессе продолжат выполняться без
|
||||
* помех. Но в дочернем процессе все соответствующие транзакции безальтернативно
|
||||
* перестают быть валидными, а попытка их использования приведет к возврату
|
||||
* ошибки или отправке `SIGSEGV`.
|
||||
*
|
||||
* Использование экземпляра среды в дочернем процессе не возможно до вызова
|
||||
* \ref mdbx_env_resurrect_after_fork(), так как в результате ветвления у
|
||||
* процесса меняется PID, значение которого используется для организации
|
||||
* совместно работы с БД, в том числе, для отслеживания процессов/потоков
|
||||
* выполняющих читающие транзакции связанные с соответствующими снимками данных.
|
||||
* Все активные на момент ветвления транзакции не могут продолжаться в дочернем
|
||||
* процессе, так как не владеют какими-либо блокировками или каким-либо снимком
|
||||
* данных и не удерживает его от переработки при сборке мусора.
|
||||
*
|
||||
* Функция \ref mdbx_env_resurrect_after_fork() восстанавливает переданный
|
||||
* экземпляр среды в дочернем процессе после ветвления, а именно: обновляет
|
||||
* используемые системные идентификаторы, повторно открывает дескрипторы файлов,
|
||||
* производит захват необходимых блокировок связанных с LCK- и DXB-файлами БД,
|
||||
* восстанавливает отображения в память страницы БД, таблицы читателей и
|
||||
* служебных/вспомогательных данных в память. Однако унаследованные от
|
||||
* родительского процесса транзакции не восстанавливаются, прием пишущие и
|
||||
* читающие транзакции обрабатываются по-разному:
|
||||
*
|
||||
* - Пишущая транзакция, если таковая была на момент ветвления,
|
||||
* прерывается в дочернем процессе с освобождение связанных с ней ресурсов,
|
||||
* включая все вложенные транзакции.
|
||||
*
|
||||
* - Читающие же транзакции, если таковые были в родительском процессе,
|
||||
* в дочернем процессе логически прерываются, но без освобождения ресурсов.
|
||||
* Поэтому необходимо обеспечить вызов \ref mdbx_txn_abort() для каждой
|
||||
* такой читающей транзакций в дочернем процессе, либо смириться с утечкой
|
||||
* ресурсов до завершения дочернего процесса.
|
||||
*
|
||||
* Причина не-освобождения ресурсов читающих транзакций в том, что исторически
|
||||
* MDBX не ведет какой-либо общий список экземпляров читающих, так как это не
|
||||
* требуется для штатных режимов работы, но требует использования атомарных
|
||||
* операций или дополнительных объектов синхронизации при создании/разрушении
|
||||
* экземпляров \ref MDBX_txn.
|
||||
*
|
||||
* Вызов \ref mdbx_env_resurrect_after_fork() без ветвления, не в дочернем
|
||||
* процессе, либо повторные вызовы не приводят к каким-либо действиям или
|
||||
* изменениям.
|
||||
*
|
||||
* \returns Ненулевое значение ошибки при сбое и 0 при успешном выполнении,
|
||||
* некоторые возможные ошибки таковы:
|
||||
*
|
||||
* \retval MDBX_BUSY В родительском процессе БД была открыта
|
||||
* в режиме \ref MDBX_EXCLUSIVE.
|
||||
*
|
||||
* \retval MDBX_EBADSIGN При повреждении сигнатуры экземпляра объекта, а также
|
||||
* в случае одновременного вызова \ref
|
||||
* mdbx_env_resurrect_after_fork() из разных потоков.
|
||||
*
|
||||
* \retval MDBX_PANIC Произошла критическая ошибка при восстановлении
|
||||
* экземпляра среды, либо такая ошибка уже была
|
||||
* до вызова функции. */
|
||||
LIBMDBX_API int mdbx_env_resurrect_after_fork(MDBX_env *env);
|
||||
#endif /* Windows */
|
||||
|
||||
/** \brief Warming up options
|
||||
* \ingroup c_settings
|
||||
* \anchor warmup_flags
|
||||
@ -3310,6 +3440,12 @@ mdbx_limits_dbsize_max(intptr_t pagesize);
|
||||
MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
|
||||
mdbx_limits_keysize_max(intptr_t pagesize, MDBX_db_flags_t flags);
|
||||
|
||||
/** \brief Returns minimal key size in bytes for given database flags.
|
||||
* \ingroup c_statinfo
|
||||
* \see db_flags */
|
||||
MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
|
||||
mdbx_limits_keysize_min(MDBX_db_flags_t flags);
|
||||
|
||||
/** \brief Returns maximal data size in bytes for given page size
|
||||
* and database flags, or -1 if pagesize is invalid.
|
||||
* \ingroup c_statinfo
|
||||
@ -3317,6 +3453,12 @@ mdbx_limits_keysize_max(intptr_t pagesize, MDBX_db_flags_t flags);
|
||||
MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
|
||||
mdbx_limits_valsize_max(intptr_t pagesize, MDBX_db_flags_t flags);
|
||||
|
||||
/** \brief Returns minimal data size in bytes for given database flags.
|
||||
* \ingroup c_statinfo
|
||||
* \see db_flags */
|
||||
MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
|
||||
mdbx_limits_valsize_min(MDBX_db_flags_t flags);
|
||||
|
||||
/** \brief Returns maximal size of key-value pair to fit in a single page with
|
||||
* the given size and database flags, or -1 if pagesize is invalid.
|
||||
* \ingroup c_statinfo
|
||||
@ -3766,7 +3908,7 @@ mdbx_txn_env(const MDBX_txn *txn);
|
||||
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
|
||||
*
|
||||
* \returns A transaction flags, valid if input is an valid transaction,
|
||||
* otherwise -1. */
|
||||
* otherwise \ref MDBX_TXN_INVALID. */
|
||||
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int mdbx_txn_flags(const MDBX_txn *txn);
|
||||
|
||||
/** \brief Return the transaction's ID.
|
||||
@ -4221,6 +4363,11 @@ MDBX_DEPRECATED LIBMDBX_API int
|
||||
mdbx_dbi_open_ex2(MDBX_txn *txn, const MDBX_val *name, MDBX_db_flags_t flags,
|
||||
MDBX_dbi *dbi, MDBX_cmp_func *keycmp, MDBX_cmp_func *datacmp);
|
||||
|
||||
/** FIXME */
|
||||
LIBMDBX_API int mdbx_dbi_rename(MDBX_txn *txn, MDBX_dbi dbi, const char *name);
|
||||
LIBMDBX_API int mdbx_dbi_rename2(MDBX_txn *txn, MDBX_dbi dbi,
|
||||
const MDBX_val *name);
|
||||
|
||||
/** \defgroup value2key Value-to-Key functions
|
||||
* \brief Value-to-Key functions to
|
||||
* \ref avoid_custom_comparators "avoid using custom comparators"
|
||||
@ -4734,6 +4881,28 @@ mdbx_cursor_get_userctx(const MDBX_cursor *cursor);
|
||||
LIBMDBX_API int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *cursor,
|
||||
MDBX_dbi dbi);
|
||||
|
||||
/** \brief Unbind cursor from a transaction.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
* Unbinded cursor is disassociated with any transactions but still holds
|
||||
* the original DBI-handle internally. Thus it could be renewed with any running
|
||||
* transaction or closed.
|
||||
*
|
||||
* \see mdbx_cursor_renew()
|
||||
* \see mdbx_cursor_bind()
|
||||
* \see mdbx_cursor_close()
|
||||
*
|
||||
* \note In contrast to LMDB, the MDBX required that any opened cursors can be
|
||||
* reused and must be freed explicitly, regardless ones was opened in a
|
||||
* read-only or write transaction. The REASON for this is eliminates ambiguity
|
||||
* which helps to avoid errors such as: use-after-free, double-free, i.e.
|
||||
* memory corruption and segfaults.
|
||||
*
|
||||
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open().
|
||||
*
|
||||
* \returns A non-zero error value on failure and 0 on success. */
|
||||
LIBMDBX_API int mdbx_cursor_unbind(MDBX_cursor *cursor);
|
||||
|
||||
/** \brief Create a cursor handle for the specified transaction and DBI handle.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
@ -4783,6 +4952,27 @@ LIBMDBX_API int mdbx_cursor_open(const MDBX_txn *txn, MDBX_dbi dbi,
|
||||
* or \ref mdbx_cursor_create(). */
|
||||
LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
|
||||
|
||||
/** \brief Unbind or closes all cursors of a given transaction.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
* Unbinds either closes all cursors associated (opened or renewed) with
|
||||
* a given transaction in a bulk with minimal overhead.
|
||||
*
|
||||
* \see mdbx_cursor_unbind()
|
||||
* \see mdbx_cursor_close()
|
||||
*
|
||||
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
|
||||
* \param [in] unbind If non-zero, unbinds cursors and leaves ones reusable.
|
||||
* Otherwise close and dispose cursors.
|
||||
*
|
||||
* \returns A negative error value on failure or the number of closed cursors
|
||||
* on success, some possible errors are:
|
||||
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
|
||||
* by current thread.
|
||||
* \retval MDBX_BAD_TXN Given transaction is invalid or has
|
||||
* a child/nested transaction transaction. */
|
||||
LIBMDBX_API int mdbx_txn_release_all_cursors(const MDBX_txn *txn, bool unbind);
|
||||
|
||||
/** \brief Renew a cursor handle for use within the given transaction.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
@ -4834,6 +5024,11 @@ LIBMDBX_API MDBX_dbi mdbx_cursor_dbi(const MDBX_cursor *cursor);
|
||||
* \returns A non-zero error value on failure and 0 on success. */
|
||||
LIBMDBX_API int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest);
|
||||
|
||||
/** FIXME */
|
||||
LIBMDBX_API int mdbx_cursor_compare(const MDBX_cursor *left,
|
||||
const MDBX_cursor *right,
|
||||
bool ignore_nested);
|
||||
|
||||
/** \brief Retrieve by cursor.
|
||||
* \ingroup c_crud
|
||||
*
|
||||
@ -4867,6 +5062,21 @@ LIBMDBX_API int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest);
|
||||
* \retval MDBX_EINVAL An invalid parameter was specified. */
|
||||
LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key,
|
||||
MDBX_val *data, MDBX_cursor_op op);
|
||||
/** FIXME */
|
||||
typedef int(MDBX_predicate_func)(void *context, MDBX_val *key, MDBX_val *value,
|
||||
void *arg) MDBX_CXX17_NOEXCEPT;
|
||||
/** FIXME */
|
||||
LIBMDBX_API int mdbx_cursor_scan(MDBX_cursor *cursor,
|
||||
MDBX_predicate_func *predicate, void *context,
|
||||
MDBX_cursor_op start_op,
|
||||
MDBX_cursor_op turn_op, void *arg);
|
||||
|
||||
/** FIXME */
|
||||
LIBMDBX_API int mdbx_cursor_scan_from(MDBX_cursor *cursor,
|
||||
MDBX_predicate_func *predicate,
|
||||
void *context, MDBX_cursor_op from_op,
|
||||
MDBX_val *from_key, MDBX_val *from_value,
|
||||
MDBX_cursor_op turn_op, void *arg);
|
||||
|
||||
/** \brief Retrieve multiple non-dupsort key/value pairs by cursor.
|
||||
* \ingroup c_crud
|
||||
@ -5076,6 +5286,10 @@ mdbx_cursor_eof(const MDBX_cursor *cursor);
|
||||
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
|
||||
mdbx_cursor_on_first(const MDBX_cursor *cursor);
|
||||
|
||||
/** FIXME */
|
||||
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
|
||||
mdbx_cursor_on_first_dup(const MDBX_cursor *cursor);
|
||||
|
||||
/** \brief Determines whether the cursor is pointed to the last key-value pair
|
||||
* or not.
|
||||
* \ingroup c_cursors
|
||||
@ -5090,6 +5304,10 @@ mdbx_cursor_on_first(const MDBX_cursor *cursor);
|
||||
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
|
||||
mdbx_cursor_on_last(const MDBX_cursor *cursor);
|
||||
|
||||
/** FIXME */
|
||||
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
|
||||
mdbx_cursor_on_last_dup(const MDBX_cursor *cursor);
|
||||
|
||||
/** \addtogroup c_rqest
|
||||
* \details \note The estimation result varies greatly depending on the filling
|
||||
* of specific pages and the overall balance of the b-tree:
|
||||
@ -5521,48 +5739,21 @@ LIBMDBX_API int mdbx_env_set_hsr(MDBX_env *env, MDBX_hsr_func *hsr_callback);
|
||||
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API MDBX_hsr_func *
|
||||
mdbx_env_get_hsr(const MDBX_env *env);
|
||||
|
||||
/** \defgroup btree_traversal B-tree Traversal
|
||||
* This is internal API for mdbx_chk tool. You should avoid to use it, except
|
||||
* some extremal special cases.
|
||||
/** \defgroup chk Checking and Recovery
|
||||
* Basically this is internal API for `mdbx_chk` tool, etc.
|
||||
* You should avoid to use it, except some extremal special cases.
|
||||
* \ingroup c_extra
|
||||
* @{ */
|
||||
|
||||
/** \brief Page types for traverse the b-tree.
|
||||
* \see mdbx_env_pgwalk() \see MDBX_pgvisitor_func */
|
||||
enum MDBX_page_type_t {
|
||||
MDBX_page_broken,
|
||||
MDBX_page_meta,
|
||||
MDBX_page_large,
|
||||
MDBX_page_branch,
|
||||
MDBX_page_leaf,
|
||||
MDBX_page_dupfixed_leaf,
|
||||
MDBX_subpage_leaf,
|
||||
MDBX_subpage_dupfixed_leaf,
|
||||
MDBX_subpage_broken,
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
typedef enum MDBX_page_type_t MDBX_page_type_t;
|
||||
#endif
|
||||
/** \brief Acquires write-transaction lock.
|
||||
* Provided for custom and/or complex locking scenarios.
|
||||
* \returns A non-zero error value on failure and 0 on success. */
|
||||
LIBMDBX_API int mdbx_txn_lock(MDBX_env *env, bool dont_wait);
|
||||
|
||||
/** \brief Pseudo-name for MainDB */
|
||||
#define MDBX_PGWALK_MAIN ((void *)((ptrdiff_t)0))
|
||||
/** \brief Pseudo-name for GarbageCollectorDB */
|
||||
#define MDBX_PGWALK_GC ((void *)((ptrdiff_t)-1))
|
||||
/** \brief Pseudo-name for MetaPages */
|
||||
#define MDBX_PGWALK_META ((void *)((ptrdiff_t)-2))
|
||||
|
||||
/** \brief Callback function for traverse the b-tree. \see mdbx_env_pgwalk() */
|
||||
typedef int
|
||||
MDBX_pgvisitor_func(const uint64_t pgno, const unsigned number, void *const ctx,
|
||||
const int deep, const MDBX_val *dbi_name,
|
||||
const size_t page_size, const MDBX_page_type_t type,
|
||||
const MDBX_error_t err, const size_t nentries,
|
||||
const size_t payload_bytes, const size_t header_bytes,
|
||||
const size_t unused_bytes) MDBX_CXX17_NOEXCEPT;
|
||||
|
||||
/** \brief B-tree traversal function. */
|
||||
LIBMDBX_API int mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor,
|
||||
void *ctx, bool dont_check_keys_ordering);
|
||||
/** \brief Releases write-transaction lock.
|
||||
* Provided for custom and/or complex locking scenarios.
|
||||
* \returns A non-zero error value on failure and 0 on success. */
|
||||
LIBMDBX_API int mdbx_txn_unlock(MDBX_env *env);
|
||||
|
||||
/** \brief Open an environment instance using specific meta-page
|
||||
* for checking and recovery.
|
||||
@ -5594,7 +5785,236 @@ LIBMDBX_API int mdbx_env_open_for_recoveryW(MDBX_env *env,
|
||||
* leg(s). */
|
||||
LIBMDBX_API int mdbx_env_turn_for_recovery(MDBX_env *env, unsigned target_meta);
|
||||
|
||||
/** end of btree_traversal @} */
|
||||
/** \brief FIXME
|
||||
*/
|
||||
LIBMDBX_API int mdbx_preopen_snapinfo(const char *pathname, MDBX_envinfo *arg,
|
||||
size_t bytes);
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(DOXYGEN)
|
||||
/** \copydoc mdbx_preopen_snapinfo()
|
||||
* \note Available only on Windows.
|
||||
* \see mdbx_preopen_snapinfo() */
|
||||
LIBMDBX_API int mdbx_preopen_snapinfoW(const wchar_t *pathname,
|
||||
MDBX_envinfo *arg, size_t bytes);
|
||||
#endif /* Windows */
|
||||
|
||||
/** \brief Флаги/опции для проверки целостности БД.
|
||||
* \see mdbx_env_chk() */
|
||||
enum MDBX_chk_flags_t {
|
||||
/** Режим проверки по-умолчанию, в том числе в режиме только-чтения. */
|
||||
MDBX_CHK_DEFAULTS = 0,
|
||||
|
||||
/** Проверка в режиме чтения-записи, с захватом блокировки и приостановки
|
||||
* пишущих транзакций. */
|
||||
MDBX_CHK_READWRITE = 1,
|
||||
|
||||
/** Пропустить обход дерева страниц. */
|
||||
MDBX_CHK_SKIP_BTREE_TRAVERSAL = 2,
|
||||
|
||||
/** Пропустить просмотр записей ключ-значение. */
|
||||
MDBX_CHK_SKIP_KV_TRAVERSAL = 4,
|
||||
|
||||
/** Игнорировать порядок ключей и записей.
|
||||
* \note Требуется при проверке унаследованных БД созданных с использованием
|
||||
* нестандартных (пользовательских) функций сравнения ключей или значений. */
|
||||
MDBX_CHK_IGNORE_ORDER = 8
|
||||
};
|
||||
#ifndef __cplusplus
|
||||
/** \ingroup c_opening */
|
||||
typedef enum MDBX_chk_flags_t MDBX_chk_flags_t;
|
||||
#else
|
||||
DEFINE_ENUM_FLAG_OPERATORS(MDBX_chk_flags_t)
|
||||
#endif
|
||||
|
||||
/** \brief Уровни логирование/детализации информации,
|
||||
* поставляемой через обратные вызовы при проверке целостности БД.
|
||||
* \see mdbx_env_chk() */
|
||||
enum MDBX_chk_severity {
|
||||
MDBX_chk_severity_prio_shift = 4,
|
||||
MDBX_chk_severity_kind_mask = 0xF,
|
||||
MDBX_chk_fatal = 0x00u,
|
||||
MDBX_chk_error = 0x11u,
|
||||
MDBX_chk_warning = 0x22u,
|
||||
MDBX_chk_notice = 0x33u,
|
||||
MDBX_chk_result = 0x44u,
|
||||
MDBX_chk_resolution = 0x55u,
|
||||
MDBX_chk_processing = 0x56u,
|
||||
MDBX_chk_info = 0x67u,
|
||||
MDBX_chk_verbose = 0x78u,
|
||||
MDBX_chk_details = 0x89u,
|
||||
MDBX_chk_extra = 0x9Au
|
||||
};
|
||||
|
||||
/** \brief Стадии проверки,
|
||||
* сообщаемые через обратные вызовы при проверке целостности БД.
|
||||
* \see mdbx_env_chk() */
|
||||
enum MDBX_chk_stage {
|
||||
MDBX_chk_none,
|
||||
MDBX_chk_init,
|
||||
MDBX_chk_lock,
|
||||
MDBX_chk_meta,
|
||||
MDBX_chk_traversal_tree,
|
||||
MDBX_chk_traversal_freedb,
|
||||
MDBX_chk_space,
|
||||
MDBX_chk_traversal_maindb,
|
||||
MDBX_chk_traversal_subdbs,
|
||||
MDBX_chk_conclude,
|
||||
MDBX_chk_unlock,
|
||||
MDBX_chk_finalize
|
||||
};
|
||||
|
||||
/** \brief Виртуальная строка отчета, формируемого при проверке целостности БД.
|
||||
* \see mdbx_env_chk() */
|
||||
typedef struct MDBX_chk_line {
|
||||
struct MDBX_chk_context *ctx;
|
||||
uint8_t severity, scope_depth, empty;
|
||||
char *begin, *end, *out;
|
||||
} MDBX_chk_line_t;
|
||||
|
||||
/** \brief Проблема обнаруженная при проверке целостности БД.
|
||||
* \see mdbx_env_chk() */
|
||||
typedef struct MDBX_chk_issue {
|
||||
struct MDBX_chk_issue *next;
|
||||
size_t count;
|
||||
const char *caption;
|
||||
} MDBX_chk_issue_t;
|
||||
|
||||
/** \brief Иерархический контекст при проверке целостности БД.
|
||||
* \see mdbx_env_chk() */
|
||||
typedef struct MDBX_chk_scope {
|
||||
MDBX_chk_issue_t *issues;
|
||||
struct MDBX_chk_internal *internal;
|
||||
const void *object;
|
||||
enum MDBX_chk_stage stage;
|
||||
enum MDBX_chk_severity verbosity;
|
||||
size_t subtotal_issues;
|
||||
union {
|
||||
void *ptr;
|
||||
size_t number;
|
||||
} usr_z, usr_v, usr_o;
|
||||
} MDBX_chk_scope_t;
|
||||
|
||||
/** \brief Пользовательский тип для привязки дополнительных данных,
|
||||
* связанных с некоторой таблицей ключ-значение, при проверке целостности БД.
|
||||
* \see mdbx_env_chk() */
|
||||
typedef struct MDBX_chk_user_subdb_cookie MDBX_chk_user_subdb_cookie_t;
|
||||
|
||||
/** \brief Гистограмма с некоторой статистической информацией,
|
||||
* собираемой при проверке целостности БД.
|
||||
* \see mdbx_env_chk() */
|
||||
struct MDBX_chk_histogram {
|
||||
size_t amount, count, ones, pad;
|
||||
struct {
|
||||
size_t begin, end, amount, count;
|
||||
} ranges[9];
|
||||
};
|
||||
|
||||
/** \brief Информация о некоторой таблицей ключ-значение,
|
||||
* при проверке целостности БД.
|
||||
* \see mdbx_env_chk() */
|
||||
typedef struct MDBX_chk_subdb {
|
||||
MDBX_chk_user_subdb_cookie_t *cookie;
|
||||
|
||||
/** \brief Pseudo-name for MainDB */
|
||||
#define MDBX_CHK_MAIN ((void *)((ptrdiff_t)0))
|
||||
/** \brief Pseudo-name for GarbageCollectorDB */
|
||||
#define MDBX_CHK_GC ((void *)((ptrdiff_t)-1))
|
||||
/** \brief Pseudo-name for MetaPages */
|
||||
#define MDBX_CHK_META ((void *)((ptrdiff_t)-2))
|
||||
|
||||
MDBX_val name;
|
||||
MDBX_db_flags_t flags;
|
||||
int id;
|
||||
|
||||
size_t payload_bytes, lost_bytes;
|
||||
struct {
|
||||
size_t all, empty, other;
|
||||
size_t branch, leaf;
|
||||
size_t nested_branch, nested_leaf, nested_subleaf;
|
||||
} pages;
|
||||
struct {
|
||||
/// Tree deep histogram
|
||||
struct MDBX_chk_histogram deep;
|
||||
/// Histogram of large/overflow pages length
|
||||
struct MDBX_chk_histogram large_pages;
|
||||
/// Histogram of nested trees height, span length for GC
|
||||
struct MDBX_chk_histogram nested_tree;
|
||||
/// Keys length histogram
|
||||
struct MDBX_chk_histogram key_len;
|
||||
/// Values length histogram
|
||||
struct MDBX_chk_histogram val_len;
|
||||
} histogram;
|
||||
} MDBX_chk_subdb_t;
|
||||
|
||||
/** \brief Контекст проверки целостности БД.
|
||||
* \see mdbx_env_chk() */
|
||||
typedef struct MDBX_chk_context {
|
||||
struct MDBX_chk_internal *internal;
|
||||
MDBX_env *env;
|
||||
MDBX_txn *txn;
|
||||
MDBX_chk_scope_t *scope;
|
||||
uint8_t scope_nesting;
|
||||
struct {
|
||||
size_t total_payload_bytes;
|
||||
size_t subdb_total, subdb_processed;
|
||||
size_t total_unused_bytes, unused_pages;
|
||||
size_t processed_pages, reclaimable_pages, gc_pages, alloc_pages,
|
||||
backed_pages;
|
||||
size_t problems_meta, tree_problems, gc_tree_problems, kv_tree_problems,
|
||||
problems_gc, problems_kv, total_problems;
|
||||
uint64_t steady_txnid, recent_txnid;
|
||||
/** Указатель на массив размером subdb_total с указателями на экземпляры
|
||||
* структур MDBX_chk_subdb_t с информацией о всех таблицах ключ-значение,
|
||||
* включая MainDB и GC/FreeDB. */
|
||||
const MDBX_chk_subdb_t *const *subdbs;
|
||||
} result;
|
||||
} MDBX_chk_context_t;
|
||||
|
||||
/** FIXME */
|
||||
typedef struct MDBX_chk_callbacks {
|
||||
bool (*check_break)(MDBX_chk_context_t *ctx);
|
||||
int (*scope_push)(MDBX_chk_context_t *ctx, MDBX_chk_scope_t *outer,
|
||||
MDBX_chk_scope_t *inner, const char *fmt, va_list args);
|
||||
int (*scope_conclude)(MDBX_chk_context_t *ctx, MDBX_chk_scope_t *outer,
|
||||
MDBX_chk_scope_t *inner, int err);
|
||||
void (*scope_pop)(MDBX_chk_context_t *ctx, MDBX_chk_scope_t *outer,
|
||||
MDBX_chk_scope_t *inner);
|
||||
void (*issue)(MDBX_chk_context_t *ctx, const char *object,
|
||||
uint64_t entry_number, const char *issue, const char *extra_fmt,
|
||||
va_list extra_args);
|
||||
MDBX_chk_user_subdb_cookie_t *(*subdb_filter)(MDBX_chk_context_t *ctx,
|
||||
const MDBX_val *name,
|
||||
MDBX_db_flags_t flags);
|
||||
int (*subdb_conclude)(MDBX_chk_context_t *ctx, const MDBX_chk_subdb_t *subdb,
|
||||
MDBX_cursor *cursor, int err);
|
||||
void (*subdb_dispose)(MDBX_chk_context_t *ctx, const MDBX_chk_subdb_t *subdb);
|
||||
|
||||
int (*subdb_handle_kv)(MDBX_chk_context_t *ctx, const MDBX_chk_subdb_t *subdb,
|
||||
size_t entry_number, const MDBX_val *key,
|
||||
const MDBX_val *value);
|
||||
|
||||
int (*stage_begin)(MDBX_chk_context_t *ctx, enum MDBX_chk_stage);
|
||||
int (*stage_end)(MDBX_chk_context_t *ctx, enum MDBX_chk_stage, int err);
|
||||
|
||||
MDBX_chk_line_t *(*print_begin)(MDBX_chk_context_t *ctx,
|
||||
enum MDBX_chk_severity severity);
|
||||
void (*print_flush)(MDBX_chk_line_t *);
|
||||
void (*print_done)(MDBX_chk_line_t *);
|
||||
void (*print_chars)(MDBX_chk_line_t *, const char *str, size_t len);
|
||||
void (*print_format)(MDBX_chk_line_t *, const char *fmt, va_list args);
|
||||
void (*print_size)(MDBX_chk_line_t *, const char *prefix,
|
||||
const uint64_t value, const char *suffix);
|
||||
} MDBX_chk_callbacks_t;
|
||||
|
||||
/** FIXME */
|
||||
LIBMDBX_API int mdbx_env_chk(MDBX_env *env, const MDBX_chk_callbacks_t *cb,
|
||||
MDBX_chk_context_t *ctx,
|
||||
const enum MDBX_chk_flags_t flags,
|
||||
enum MDBX_chk_severity verbosity,
|
||||
unsigned timeout_seconds_16dot16);
|
||||
/** FIXME */
|
||||
LIBMDBX_API int mdbx_env_chk_problem(MDBX_chk_context_t *ctx);
|
||||
|
||||
/** end of chk @} */
|
||||
|
||||
/** end of c_api @} */
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
@ -685,7 +686,7 @@ __extern_C key_t ftok(const char *, int);
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(MDBX_USE_VALGRIND)
|
||||
#if defined(ENABLE_MEMCHECK)
|
||||
#include <valgrind/memcheck.h>
|
||||
#ifndef VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE
|
||||
/* LY: available since Valgrind 3.10 */
|
||||
@ -707,7 +708,7 @@ __extern_C key_t ftok(const char *, int);
|
||||
#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(a, s) (0)
|
||||
#define VALGRIND_CHECK_MEM_IS_DEFINED(a, s) (0)
|
||||
#define RUNNING_ON_VALGRIND (0)
|
||||
#endif /* MDBX_USE_VALGRIND */
|
||||
#endif /* ENABLE_MEMCHECK */
|
||||
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
#include <sanitizer/asan_interface.h>
|
||||
|
@ -5,9 +5,9 @@ N | MASK | ENV | TXN | DB | PUT | DBI | NOD
|
||||
2 |0000 0004|ALLOC_COLSC|TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW| |
|
||||
3 |0000 0008|ALLOC_SSCAN|TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | |
|
||||
4 |0000 0010|ALLOC_FIFO |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | |
|
||||
5 |0000 0020| |TXN_DRAINED_GC|INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2 | |
|
||||
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP | |
|
||||
7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | | | |
|
||||
5 |0000 0020| |TXN_DRAINED_GC|INTEGERDUP|NODUPDATA | | |P_LEAF2 | |
|
||||
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_OLDEN | |P_SUBP | |
|
||||
7 |0000 0080| | | |ALLDUPS |DBI_LINDO | | | |
|
||||
8 |0000 0100| _MAY_MOVE | | | | | | | <= |
|
||||
9 |0000 0200| _MAY_UNMAP| | | | | | | <= |
|
||||
10|0000 0400| | | | | | | | |
|
||||
|
@ -5,7 +5,7 @@
|
||||
/* clang-format off */
|
||||
|
||||
#cmakedefine LTO_ENABLED
|
||||
#cmakedefine MDBX_USE_VALGRIND
|
||||
#cmakedefine ENABLE_MEMCHECK
|
||||
#cmakedefine ENABLE_GPROF
|
||||
#cmakedefine ENABLE_GCOV
|
||||
#cmakedefine ENABLE_ASAN
|
||||
@ -33,6 +33,8 @@
|
||||
#cmakedefine01 MDBX_ENABLE_BIGFOOT
|
||||
#cmakedefine01 MDBX_ENABLE_PGOP_STAT
|
||||
#cmakedefine01 MDBX_ENABLE_PROFGC
|
||||
#cmakedefine01 MDBX_ENABLE_DBI_SPARSE
|
||||
#cmakedefine01 MDBX_ENABLE_DBI_LOCKFREE
|
||||
|
||||
/* Windows */
|
||||
#cmakedefine01 MDBX_WITHOUT_MSVC_CRT
|
||||
|
7210
src/core.c
7210
src/core.c
File diff suppressed because it is too large
Load Diff
160
src/internals.h
160
src/internals.h
@ -93,6 +93,10 @@
|
||||
disable : 5105) /* winbase.h(9531): warning C5105: macro expansion \
|
||||
producing 'defined' has undefined behavior */
|
||||
#endif
|
||||
#if _MSC_VER < 1920
|
||||
/* avoid "error C2219: syntax error: type qualifier must be after '*'" */
|
||||
#define __restrict
|
||||
#endif
|
||||
#if _MSC_VER > 1930
|
||||
#pragma warning(disable : 6235) /* <expression> is always a constant */
|
||||
#pragma warning(disable : 6237) /* <expression> is never evaluated and might \
|
||||
@ -703,7 +707,8 @@ typedef struct MDBX_page {
|
||||
|
||||
#define PAGETYPE_WHOLE(p) ((uint8_t)(p)->mp_flags)
|
||||
|
||||
/* Drop legacy P_DIRTY flag for sub-pages for compatilibity */
|
||||
/* Drop legacy P_DIRTY flag for sub-pages for compatilibity,
|
||||
* for assertions only. */
|
||||
#define PAGETYPE_COMPAT(p) \
|
||||
(unlikely(PAGETYPE_WHOLE(p) & P_SUBP) \
|
||||
? PAGETYPE_WHOLE(p) & ~(P_SUBP | P_LEGACY_DIRTY) \
|
||||
@ -812,7 +817,7 @@ typedef sem_t osal_ipclock_t;
|
||||
#endif /* MDBX_LOCKING */
|
||||
|
||||
#if MDBX_LOCKING > MDBX_LOCKING_SYSV && !defined(__cplusplus)
|
||||
MDBX_INTERNAL_FUNC int osal_ipclock_stub(osal_ipclock_t *ipc);
|
||||
MDBX_INTERNAL_FUNC int osal_ipclock_stubinit(osal_ipclock_t *ipc);
|
||||
MDBX_INTERNAL_FUNC int osal_ipclock_destroy(osal_ipclock_t *ipc);
|
||||
#endif /* MDBX_LOCKING */
|
||||
|
||||
@ -1136,10 +1141,10 @@ typedef struct troika {
|
||||
#if MDBX_WORDBITS > 32 /* Workaround for false-positives from Valgrind */
|
||||
uint32_t unused_pad;
|
||||
#endif
|
||||
#define TROIKA_HAVE_STEADY(troika) ((troika)->fsm & 7)
|
||||
#define TROIKA_STRICT_VALID(troika) ((troika)->tail_and_flags & 64)
|
||||
#define TROIKA_VALID(troika) ((troika)->tail_and_flags & 128)
|
||||
#define TROIKA_TAIL(troika) ((troika)->tail_and_flags & 3)
|
||||
#define TROIKA_HAVE_STEADY(troika) ((troika)->fsm & 7u)
|
||||
#define TROIKA_STRICT_VALID(troika) ((troika)->tail_and_flags & 64u)
|
||||
#define TROIKA_VALID(troika) ((troika)->tail_and_flags & 128u)
|
||||
#define TROIKA_TAIL(troika) ((troika)->tail_and_flags & 3u)
|
||||
txnid_t txnid[NUM_METAS];
|
||||
} meta_troika_t;
|
||||
|
||||
@ -1169,6 +1174,8 @@ struct MDBX_txn {
|
||||
#error "Oops, some txn flags overlapped or wrong"
|
||||
#endif
|
||||
uint32_t mt_flags;
|
||||
unsigned mt_numdbs;
|
||||
size_t mt_owner; /* thread ID that owns this transaction */
|
||||
|
||||
MDBX_txn *mt_parent; /* parent of a nested txn */
|
||||
/* Nested txn under this txn, set together with flag MDBX_TXN_HAS_CHILD */
|
||||
@ -1186,31 +1193,30 @@ struct MDBX_txn {
|
||||
txnid_t mt_front;
|
||||
|
||||
MDBX_env *mt_env; /* the DB environment */
|
||||
/* Array of records for each DB known in the environment. */
|
||||
MDBX_dbx *mt_dbxs;
|
||||
/* Array of MDBX_db records for each known DB */
|
||||
MDBX_db *mt_dbs;
|
||||
/* Array of sequence numbers for each DB handle */
|
||||
MDBX_atomic_uint32_t *mt_dbiseqs;
|
||||
|
||||
/* Transaction DBI Flags */
|
||||
#define DBI_DIRTY MDBX_DBI_DIRTY /* DB was written in this txn */
|
||||
#define DBI_STALE MDBX_DBI_STALE /* Named-DB record is older than txnID */
|
||||
#define DBI_FRESH MDBX_DBI_FRESH /* Named-DB handle opened in this txn */
|
||||
#define DBI_CREAT MDBX_DBI_CREAT /* Named-DB handle created in this txn */
|
||||
#define DBI_VALID 0x10 /* DB handle is valid, see also DB_VALID */
|
||||
#define DBI_USRVALID 0x20 /* As DB_VALID, but not set for FREE_DBI */
|
||||
#define DBI_AUDITED 0x40 /* Internal flag for accounting during audit */
|
||||
/* Array of flags for each DB */
|
||||
uint8_t *mt_dbistate;
|
||||
/* Number of DB records in use, or 0 when the txn is finished.
|
||||
* This number only ever increments until the txn finishes; we
|
||||
* don't decrement it when individual DB handles are closed. */
|
||||
MDBX_dbi mt_numdbs;
|
||||
size_t mt_owner; /* thread ID that owns this transaction */
|
||||
#if MDBX_ENABLE_DBI_SPARSE
|
||||
unsigned *__restrict mt_dbi_sparse;
|
||||
#endif /* MDBX_ENABLE_DBI_SPARSE */
|
||||
|
||||
/* Non-shared DBI state flags inside transaction */
|
||||
#define DBI_DIRTY 0x01 /* DB was written in this txn */
|
||||
#define DBI_STALE 0x02 /* Named-DB record is older than txnID */
|
||||
#define DBI_FRESH 0x04 /* Named-DB handle opened in this txn */
|
||||
#define DBI_CREAT 0x08 /* Named-DB handle created in this txn */
|
||||
#define DBI_VALID 0x10 /* Handle is valid, see also DB_VALID */
|
||||
#define DBI_OLDEN 0x40 /* Handle was closed/reopened outside txn */
|
||||
#define DBI_LINDO 0x80 /* Lazy initialization done for DBI-slot */
|
||||
/* Array of non-shared txn's flags of DBI */
|
||||
uint8_t *__restrict mt_dbi_state;
|
||||
|
||||
/* Array of sequence numbers for each DB handle. */
|
||||
uint32_t *__restrict mt_dbi_seqs;
|
||||
MDBX_cursor **mt_cursors;
|
||||
|
||||
MDBX_canary mt_canary;
|
||||
void *mt_userctx; /* User-settable context */
|
||||
MDBX_cursor **mt_cursors;
|
||||
|
||||
union {
|
||||
struct {
|
||||
@ -1220,8 +1226,8 @@ struct MDBX_txn {
|
||||
struct {
|
||||
meta_troika_t troika;
|
||||
/* In write txns, array of cursors for each DB */
|
||||
MDBX_PNL relist; /* Reclaimed GC pages */
|
||||
txnid_t last_reclaimed; /* ID of last used record */
|
||||
MDBX_PNL __restrict relist; /* Reclaimed GC pages */
|
||||
txnid_t last_reclaimed; /* ID of last used record */
|
||||
#if MDBX_ENABLE_REFUND
|
||||
pgno_t loose_refund_wl /* FIXME: describe */;
|
||||
#endif /* MDBX_ENABLE_REFUND */
|
||||
@ -1233,14 +1239,14 @@ struct MDBX_txn {
|
||||
* dirtylist into mt_parent after freeing hidden mt_parent pages. */
|
||||
size_t dirtyroom;
|
||||
/* For write txns: Modified pages. Sorted when not MDBX_WRITEMAP. */
|
||||
MDBX_dpl *dirtylist;
|
||||
MDBX_dpl *__restrict dirtylist;
|
||||
/* The list of reclaimed txns from GC */
|
||||
MDBX_TXL lifo_reclaimed;
|
||||
MDBX_TXL __restrict lifo_reclaimed;
|
||||
/* The list of pages that became unused during this transaction. */
|
||||
MDBX_PNL retired_pages;
|
||||
MDBX_PNL __restrict retired_pages;
|
||||
/* The list of loose pages that became unused and may be reused
|
||||
* in this transaction, linked through `mp_next`. */
|
||||
MDBX_page *loose_pages;
|
||||
MDBX_page *__restrict loose_pages;
|
||||
/* Number of loose pages (tw.loose_pages) */
|
||||
size_t loose_count;
|
||||
union {
|
||||
@ -1249,11 +1255,12 @@ struct MDBX_txn {
|
||||
/* The sorted list of dirty pages we temporarily wrote to disk
|
||||
* because the dirty list was full. page numbers in here are
|
||||
* shifted left by 1, deleted slots have the LSB set. */
|
||||
MDBX_PNL list;
|
||||
MDBX_PNL __restrict list;
|
||||
} spilled;
|
||||
size_t writemap_dirty_npages;
|
||||
size_t writemap_spilled_npages;
|
||||
};
|
||||
uint64_t gc_time_acc;
|
||||
} tw;
|
||||
};
|
||||
};
|
||||
@ -1292,8 +1299,8 @@ struct MDBX_cursor {
|
||||
MDBX_db *mc_db;
|
||||
/* The database auxiliary record for this cursor */
|
||||
MDBX_dbx *mc_dbx;
|
||||
/* The mt_dbistate for this database */
|
||||
uint8_t *mc_dbistate;
|
||||
/* The mt_dbi_state[] for this DBI */
|
||||
uint8_t *__restrict mc_dbi_state;
|
||||
uint8_t mc_snum; /* number of pushed pages */
|
||||
uint8_t mc_top; /* index of top page, normally mc_snum-1 */
|
||||
|
||||
@ -1346,6 +1353,11 @@ typedef struct MDBX_cursor_couple {
|
||||
MDBX_xcursor inner;
|
||||
} MDBX_cursor_couple;
|
||||
|
||||
struct mdbx_defer_free_item {
|
||||
struct mdbx_defer_free_item *next;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
/* The database environment. */
|
||||
struct MDBX_env {
|
||||
/* ----------------------------------------------------- mostly static part */
|
||||
@ -1363,6 +1375,7 @@ struct MDBX_env {
|
||||
#define MDBX_DEPRECATED_COALESCE UINT32_C(0x2000000)
|
||||
#define ENV_INTERNAL_FLAGS (MDBX_FATAL_ERROR | MDBX_ENV_ACTIVE | MDBX_ENV_TXKEY)
|
||||
uint32_t me_flags;
|
||||
unsigned me_psize; /* DB page size, initialized from me_os_psize */
|
||||
osal_mmap_t me_dxb_mmap; /* The main data file */
|
||||
#define me_map me_dxb_mmap.base
|
||||
#define me_lazy_fd me_dxb_mmap.fd
|
||||
@ -1375,7 +1388,6 @@ struct MDBX_env {
|
||||
#define me_lfd me_lck_mmap.fd
|
||||
struct MDBX_lockinfo *me_lck;
|
||||
|
||||
unsigned me_psize; /* DB page size, initialized from me_os_psize */
|
||||
uint16_t me_leaf_nodemax; /* max size of a leaf-node */
|
||||
uint16_t me_branch_nodemax; /* max size of a branch-node */
|
||||
uint16_t me_subpage_limit;
|
||||
@ -1393,13 +1405,15 @@ struct MDBX_env {
|
||||
MDBX_dbi me_maxdbs; /* size of the DB table */
|
||||
uint32_t me_pid; /* process ID of this env */
|
||||
osal_thread_key_t me_txkey; /* thread-key for readers */
|
||||
pathchar_t *me_pathname; /* path to the DB files */
|
||||
void *me_pbuf; /* scratch area for DUPSORT put() */
|
||||
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 */
|
||||
MDBX_atomic_uint32_t *me_dbiseqs; /* array of dbi sequence numbers */
|
||||
struct { /* path to the DB files */
|
||||
pathchar_t *lck, *dxb, *specified;
|
||||
void *buffer;
|
||||
} me_pathname;
|
||||
void *me_pbuf; /* scratch area for DUPSORT put() */
|
||||
MDBX_txn *me_txn0; /* preallocated write transaction */
|
||||
MDBX_dbx *me_dbxs; /* array of static DB info */
|
||||
uint16_t *__restrict me_db_flags; /* array of flags from MDBX_db.md_flags */
|
||||
MDBX_atomic_uint32_t *me_dbi_seqs; /* array of dbi sequence numbers */
|
||||
unsigned
|
||||
me_maxgc_ov1page; /* Number of pgno_t fit in a single overflow page */
|
||||
unsigned me_maxgc_per_branch;
|
||||
@ -1413,6 +1427,7 @@ struct MDBX_env {
|
||||
unsigned rp_augment_limit;
|
||||
unsigned dp_limit;
|
||||
unsigned dp_initial;
|
||||
uint64_t gc_time_limit;
|
||||
uint8_t dp_loose_limit;
|
||||
uint8_t spill_max_denominator;
|
||||
uint8_t spill_min_denominator;
|
||||
@ -1422,6 +1437,8 @@ struct MDBX_env {
|
||||
unsigned writethrough_threshold;
|
||||
#endif /* Windows */
|
||||
bool prefault_write;
|
||||
bool prefer_waf_insteadof_balance; /* Strive to minimize WAF instead of
|
||||
balancing pages fullment */
|
||||
union {
|
||||
unsigned all;
|
||||
/* tracks options with non-auto values but tuned by user */
|
||||
@ -1451,20 +1468,23 @@ struct MDBX_env {
|
||||
} me_sysv_ipc;
|
||||
#endif /* MDBX_LOCKING == MDBX_LOCKING_SYSV */
|
||||
bool me_incore;
|
||||
bool me_prefault_write;
|
||||
|
||||
MDBX_env *me_lcklist_next;
|
||||
#if MDBX_ENABLE_DBI_LOCKFREE
|
||||
struct mdbx_defer_free_item *me_defer_free;
|
||||
#endif /* MDBX_ENABLE_DBI_LOCKFREE */
|
||||
|
||||
/* --------------------------------------------------- mostly volatile part */
|
||||
|
||||
MDBX_txn *me_txn; /* current write transaction */
|
||||
osal_fastmutex_t me_dbi_lock;
|
||||
MDBX_dbi me_numdbs; /* number of DBs opened */
|
||||
bool me_prefault_write;
|
||||
unsigned me_numdbs; /* number of DBs opened */
|
||||
|
||||
MDBX_page *me_dp_reserve; /* list of malloc'ed blocks for re-use */
|
||||
unsigned me_dp_reserve_len;
|
||||
MDBX_page *__restrict me_dp_reserve; /* list of malloc'ed blocks for re-use */
|
||||
|
||||
/* PNL of pages that became unused in a write txn */
|
||||
MDBX_PNL me_retired_pages;
|
||||
MDBX_PNL __restrict me_retired_pages;
|
||||
osal_ioring_t me_ioring;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
@ -1482,13 +1502,12 @@ struct MDBX_env {
|
||||
#if MDBX_DEBUG
|
||||
MDBX_assert_func *me_assert_func; /* Callback for assertion failures */
|
||||
#endif
|
||||
#ifdef MDBX_USE_VALGRIND
|
||||
#ifdef ENABLE_MEMCHECK
|
||||
int me_valgrind_handle;
|
||||
#endif
|
||||
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
|
||||
MDBX_atomic_uint32_t me_ignore_EDEADLK;
|
||||
#if defined(ENABLE_MEMCHECK) || defined(__SANITIZE_ADDRESS__)
|
||||
pgno_t me_poison_edge;
|
||||
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
|
||||
#endif /* ENABLE_MEMCHECK || __SANITIZE_ADDRESS__ */
|
||||
|
||||
#ifndef xMDBX_DEBUG_SPILLING
|
||||
#define xMDBX_DEBUG_SPILLING 0
|
||||
@ -1548,10 +1567,6 @@ osal_flush_incoherent_mmap(const void *addr, size_t nbytes,
|
||||
|
||||
MDBX_INTERNAL_FUNC int cleanup_dead_readers(MDBX_env *env, int rlocked,
|
||||
int *dead);
|
||||
MDBX_INTERNAL_FUNC int rthc_alloc(osal_thread_key_t *key, MDBX_reader *begin,
|
||||
MDBX_reader *end);
|
||||
MDBX_INTERNAL_FUNC void rthc_remove(const osal_thread_key_t key);
|
||||
|
||||
MDBX_INTERNAL_FUNC void global_ctor(void);
|
||||
MDBX_INTERNAL_FUNC void osal_ctor(void);
|
||||
MDBX_INTERNAL_FUNC void global_dtor(void);
|
||||
@ -1666,7 +1681,8 @@ typedef struct MDBX_node {
|
||||
/* mdbx_dbi_open() flags */
|
||||
#define DB_USABLE_FLAGS (DB_PERSISTENT_FLAGS | MDBX_CREATE | MDBX_DB_ACCEDE)
|
||||
|
||||
#define DB_VALID 0x8000 /* DB handle is valid, for me_dbflags */
|
||||
#define DB_VALID 0x8000u /* DB handle is valid, for me_db_flags */
|
||||
#define DB_POISON 0x7fffu /* update pending */
|
||||
#define DB_INTERNAL_FLAGS DB_VALID
|
||||
|
||||
#if DB_INTERNAL_FLAGS & DB_USABLE_FLAGS
|
||||
@ -1792,3 +1808,33 @@ MDBX_MAYBE_UNUSED static void static_checks(void) {
|
||||
(size_t)(size), __LINE__); \
|
||||
ASAN_UNPOISON_MEMORY_REGION(addr, size); \
|
||||
} while (0)
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/** \brief Page types for traverse the b-tree.
|
||||
* \see mdbx_env_pgwalk() \see MDBX_pgvisitor_func */
|
||||
enum MDBX_page_type_t {
|
||||
MDBX_page_broken,
|
||||
MDBX_page_large,
|
||||
MDBX_page_branch,
|
||||
MDBX_page_leaf,
|
||||
MDBX_page_dupfixed_leaf,
|
||||
MDBX_subpage_leaf,
|
||||
MDBX_subpage_dupfixed_leaf,
|
||||
MDBX_subpage_broken,
|
||||
};
|
||||
typedef enum MDBX_page_type_t MDBX_page_type_t;
|
||||
|
||||
typedef struct MDBX_walk_sdb {
|
||||
MDBX_val name;
|
||||
struct MDBX_db *internal, *nested;
|
||||
} MDBX_walk_sdb_t;
|
||||
|
||||
/** \brief Callback function for traverse the b-tree. \see mdbx_env_pgwalk() */
|
||||
typedef int
|
||||
MDBX_pgvisitor_func(const size_t pgno, const unsigned number, void *const ctx,
|
||||
const int deep, const MDBX_walk_sdb_t *subdb,
|
||||
const size_t page_size, const MDBX_page_type_t page_type,
|
||||
const MDBX_error_t err, const size_t nentries,
|
||||
const size_t payload_bytes, const size_t header_bytes,
|
||||
const size_t unused_bytes);
|
||||
|
136
src/lck-posix.c
136
src/lck-posix.c
@ -120,7 +120,7 @@ mdbx_global_destructor(void) {
|
||||
* - Блокировка таблицы читателей для регистрации,
|
||||
* т.е. функции osal_rdt_lock() и osal_rdt_unlock().
|
||||
* - Блокировка БД для пишущих транзакций,
|
||||
* т.е. функции mdbx_txn_lock() и mdbx_txn_unlock().
|
||||
* т.е. функции osal_txn_lock() и osal_txn_unlock().
|
||||
*
|
||||
* Остальной функционал реализуется отдельно посредством файловых блокировок:
|
||||
* - Первоначальный захват БД в режиме exclusive/shared и последующий перевод
|
||||
@ -294,7 +294,7 @@ MDBX_INTERNAL_FUNC int osal_rpid_check(MDBX_env *env, uint32_t pid) {
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#if MDBX_LOCKING > MDBX_LOCKING_SYSV
|
||||
MDBX_INTERNAL_FUNC int osal_ipclock_stub(osal_ipclock_t *ipc) {
|
||||
MDBX_INTERNAL_FUNC int osal_ipclock_stubinit(osal_ipclock_t *ipc) {
|
||||
#if MDBX_LOCKING == MDBX_LOCKING_POSIX1988
|
||||
return sem_init(ipc, false, 1) ? errno : 0;
|
||||
#elif MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
|
||||
@ -527,15 +527,42 @@ MDBX_INTERNAL_FUNC int osal_lck_downgrade(MDBX_env *env) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
__cold MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env,
|
||||
MDBX_env *inprocess_neighbor) {
|
||||
MDBX_INTERNAL_FUNC int osal_lck_upgrade(MDBX_env *env, bool dont_wait) {
|
||||
assert(env->me_lfd != INVALID_HANDLE_VALUE);
|
||||
if (unlikely(osal_getpid() != env->me_pid))
|
||||
return MDBX_PANIC;
|
||||
|
||||
const int cmd = dont_wait ? op_setlk : op_setlkw;
|
||||
int rc = lck_op(env->me_lfd, cmd, F_WRLCK, 0, 1);
|
||||
if (rc == MDBX_SUCCESS && (env->me_flags & MDBX_EXCLUSIVE) == 0) {
|
||||
rc = (env->me_pid > 1)
|
||||
? lck_op(env->me_lazy_fd, cmd, F_WRLCK, 0, env->me_pid - 1)
|
||||
: MDBX_SUCCESS;
|
||||
if (rc == MDBX_SUCCESS) {
|
||||
rc = lck_op(env->me_lazy_fd, cmd, F_WRLCK, env->me_pid + 1,
|
||||
OFF_T_MAX - env->me_pid - 1);
|
||||
if (rc != MDBX_SUCCESS && env->me_pid > 1 &&
|
||||
lck_op(env->me_lazy_fd, op_setlk, F_UNLCK, 0, env->me_pid - 1))
|
||||
rc = MDBX_PANIC;
|
||||
}
|
||||
if (rc != MDBX_SUCCESS && lck_op(env->me_lfd, op_setlk, F_RDLCK, 0, 1))
|
||||
rc = MDBX_PANIC;
|
||||
}
|
||||
if (unlikely(rc != 0)) {
|
||||
ERROR("%s, err %u", "lck", rc);
|
||||
assert(MDBX_IS_ERROR(rc));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
__cold MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env,
|
||||
MDBX_env *inprocess_neighbor,
|
||||
const uint32_t current_pid) {
|
||||
eASSERT(env, osal_getpid() == current_pid);
|
||||
int rc = MDBX_SUCCESS;
|
||||
struct stat lck_info;
|
||||
MDBX_lockinfo *lck = env->me_lck_mmap.lck;
|
||||
if (env->me_lfd != INVALID_HANDLE_VALUE && !inprocess_neighbor && lck &&
|
||||
MDBX_lockinfo *lck = env->me_lck;
|
||||
if (lck && lck == env->me_lck_mmap.lck && !inprocess_neighbor &&
|
||||
/* try get exclusive access */
|
||||
lck_op(env->me_lfd, op_setlk, F_WRLCK, 0, OFF_T_MAX) == 0 &&
|
||||
/* if LCK was not removed */
|
||||
@ -544,7 +571,8 @@ __cold MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env,
|
||||
(env->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK, 0,
|
||||
OFF_T_MAX) == 0) {
|
||||
|
||||
VERBOSE("%p got exclusive, drown locks", (void *)env);
|
||||
VERBOSE("%p got exclusive, drown ipc-locks", (void *)env);
|
||||
eASSERT(env, current_pid == env->me_pid);
|
||||
#if MDBX_LOCKING == MDBX_LOCKING_SYSV
|
||||
if (env->me_sysv_ipc.semid != -1)
|
||||
rc = semctl(env->me_sysv_ipc.semid, 2, IPC_RMID) ? errno : 0;
|
||||
@ -558,13 +586,20 @@ __cold MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env,
|
||||
if (rc == 0) {
|
||||
const bool synced = lck->mti_unsynced_pages.weak == 0;
|
||||
osal_munmap(&env->me_lck_mmap);
|
||||
if (synced)
|
||||
if (synced && env->me_lfd != INVALID_HANDLE_VALUE)
|
||||
rc = ftruncate(env->me_lfd, 0) ? errno : 0;
|
||||
}
|
||||
|
||||
jitter4testing(false);
|
||||
}
|
||||
|
||||
if (current_pid != env->me_pid) {
|
||||
eASSERT(env, !inprocess_neighbor);
|
||||
NOTICE("drown env %p after-fork pid %d -> %d",
|
||||
__Wpedantic_format_voidptr(env), env->me_pid, current_pid);
|
||||
inprocess_neighbor = nullptr;
|
||||
}
|
||||
|
||||
/* 1) POSIX's fcntl() locks (i.e. when op_setlk == F_SETLK) should be restored
|
||||
* after file was closed.
|
||||
*
|
||||
@ -761,7 +796,7 @@ bailout:
|
||||
#endif /* MDBX_LOCKING > 0 */
|
||||
}
|
||||
|
||||
__cold static int mdbx_ipclock_failed(MDBX_env *env, osal_ipclock_t *ipc,
|
||||
__cold static int osal_ipclock_failed(MDBX_env *env, osal_ipclock_t *ipc,
|
||||
const int err) {
|
||||
int rc = err;
|
||||
#if MDBX_LOCKING == MDBX_LOCKING_POSIX2008 || MDBX_LOCKING == MDBX_LOCKING_SYSV
|
||||
@ -822,11 +857,6 @@ __cold static int mdbx_ipclock_failed(MDBX_env *env, osal_ipclock_t *ipc,
|
||||
#error "FIXME"
|
||||
#endif /* MDBX_LOCKING */
|
||||
|
||||
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
|
||||
if (rc == EDEADLK && atomic_load32(&env->me_ignore_EDEADLK, mo_Relaxed) > 0)
|
||||
return rc;
|
||||
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
|
||||
|
||||
ERROR("mutex (un)lock failed, %s", mdbx_strerror(err));
|
||||
if (rc != EDEADLK)
|
||||
env->me_flags |= MDBX_FATAL_ERROR;
|
||||
@ -852,7 +882,7 @@ MDBX_INTERNAL_FUNC int osal_check_tid4bionic(void) {
|
||||
}
|
||||
#endif /* __ANDROID_API__ || ANDROID) || BIONIC */
|
||||
|
||||
static int mdbx_ipclock_lock(MDBX_env *env, osal_ipclock_t *ipc,
|
||||
static int osal_ipclock_lock(MDBX_env *env, osal_ipclock_t *ipc,
|
||||
const bool dont_wait) {
|
||||
#if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
|
||||
MDBX_LOCKING == MDBX_LOCKING_POSIX2008
|
||||
@ -888,63 +918,87 @@ static int mdbx_ipclock_lock(MDBX_env *env, osal_ipclock_t *ipc,
|
||||
#endif /* MDBX_LOCKING */
|
||||
|
||||
if (unlikely(rc != MDBX_SUCCESS && rc != MDBX_BUSY))
|
||||
rc = mdbx_ipclock_failed(env, ipc, rc);
|
||||
rc = osal_ipclock_failed(env, ipc, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mdbx_ipclock_unlock(MDBX_env *env, osal_ipclock_t *ipc) {
|
||||
int osal_ipclock_unlock(MDBX_env *env, osal_ipclock_t *ipc) {
|
||||
int err = MDBX_ENOSYS;
|
||||
#if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
|
||||
MDBX_LOCKING == MDBX_LOCKING_POSIX2008
|
||||
int rc = pthread_mutex_unlock(ipc);
|
||||
(void)env;
|
||||
err = pthread_mutex_unlock(ipc);
|
||||
#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
|
||||
int rc = sem_post(ipc) ? errno : MDBX_SUCCESS;
|
||||
(void)env;
|
||||
err = sem_post(ipc) ? errno : MDBX_SUCCESS;
|
||||
#elif MDBX_LOCKING == MDBX_LOCKING_SYSV
|
||||
if (unlikely(*ipc != (pid_t)env->me_pid))
|
||||
return EPERM;
|
||||
*ipc = 0;
|
||||
struct sembuf op = {.sem_num = (ipc != &env->me_lck->mti_wlock),
|
||||
.sem_op = 1,
|
||||
.sem_flg = SEM_UNDO};
|
||||
int rc = semop(env->me_sysv_ipc.semid, &op, 1) ? errno : MDBX_SUCCESS;
|
||||
err = EPERM;
|
||||
else {
|
||||
*ipc = 0;
|
||||
struct sembuf op = {.sem_num = (ipc != &env->me_lck->mti_wlock),
|
||||
.sem_op = 1,
|
||||
.sem_flg = SEM_UNDO};
|
||||
err = semop(env->me_sysv_ipc.semid, &op, 1) ? errno : MDBX_SUCCESS;
|
||||
}
|
||||
#else
|
||||
#error "FIXME"
|
||||
#endif /* MDBX_LOCKING */
|
||||
int rc = err;
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
const uint32_t current_pid = osal_getpid();
|
||||
if (current_pid == env->me_pid || LOG_ENABLED(MDBX_LOG_NOTICE))
|
||||
debug_log((current_pid == env->me_pid)
|
||||
? MDBX_LOG_FATAL
|
||||
: (rc = MDBX_SUCCESS, MDBX_LOG_NOTICE),
|
||||
"ipc-unlock()", __LINE__, "failed: env %p, lck-%s %p, err %d\n",
|
||||
__Wpedantic_format_voidptr(env),
|
||||
(env->me_lck == env->me_lck_mmap.lck) ? "mmap" : "stub",
|
||||
__Wpedantic_format_voidptr(env->me_lck), err);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int osal_rdt_lock(MDBX_env *env) {
|
||||
TRACE("%s", ">>");
|
||||
jitter4testing(true);
|
||||
int rc = mdbx_ipclock_lock(env, &env->me_lck->mti_rlock, false);
|
||||
int rc = osal_ipclock_lock(env, &env->me_lck->mti_rlock, false);
|
||||
TRACE("<< rc %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC void osal_rdt_unlock(MDBX_env *env) {
|
||||
TRACE("%s", ">>");
|
||||
int rc = mdbx_ipclock_unlock(env, &env->me_lck->mti_rlock);
|
||||
TRACE("<< rc %d", rc);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
mdbx_panic("%s() failed: err %d\n", __func__, rc);
|
||||
int err = osal_ipclock_unlock(env, &env->me_lck->mti_rlock);
|
||||
TRACE("<< err %d", err);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
mdbx_panic("%s() failed: err %d\n", __func__, err);
|
||||
jitter4testing(true);
|
||||
}
|
||||
|
||||
int mdbx_txn_lock(MDBX_env *env, bool dont_wait) {
|
||||
int osal_txn_lock(MDBX_env *env, bool dont_wait) {
|
||||
TRACE("%swait %s", dont_wait ? "dont-" : "", ">>");
|
||||
jitter4testing(true);
|
||||
int rc = mdbx_ipclock_lock(env, &env->me_lck->mti_wlock, dont_wait);
|
||||
TRACE("<< rc %d", rc);
|
||||
return MDBX_IS_ERROR(rc) ? rc : MDBX_SUCCESS;
|
||||
const int err = osal_ipclock_lock(env, &env->me_lck->mti_wlock, dont_wait);
|
||||
int rc = err;
|
||||
if (likely(!MDBX_IS_ERROR(err))) {
|
||||
eASSERT(env, !env->me_txn0->mt_owner ||
|
||||
err == /* если другой поток в этом-же процессе завершился
|
||||
не освободив блокировку */
|
||||
MDBX_RESULT_TRUE);
|
||||
env->me_txn0->mt_owner = osal_thread_self();
|
||||
rc = MDBX_SUCCESS;
|
||||
}
|
||||
TRACE("<< err %d, rc %d", err, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void mdbx_txn_unlock(MDBX_env *env) {
|
||||
void osal_txn_unlock(MDBX_env *env) {
|
||||
TRACE("%s", ">>");
|
||||
int rc = mdbx_ipclock_unlock(env, &env->me_lck->mti_wlock);
|
||||
TRACE("<< rc %d", rc);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
mdbx_panic("%s() failed: err %d\n", __func__, rc);
|
||||
eASSERT(env, env->me_txn0->mt_owner == osal_thread_self());
|
||||
env->me_txn0->mt_owner = 0;
|
||||
int err = osal_ipclock_unlock(env, &env->me_lck->mti_wlock);
|
||||
TRACE("<< err %d", err);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
mdbx_panic("%s() failed: err %d\n", __func__, err);
|
||||
jitter4testing(true);
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ static int funlock(mdbx_filehandle_t fd, size_t offset, size_t bytes) {
|
||||
#define DXB_BODY (env->me_psize * (size_t)NUM_METAS), DXB_MAXLEN
|
||||
#define DXB_WHOLE 0, DXB_MAXLEN
|
||||
|
||||
int mdbx_txn_lock(MDBX_env *env, bool dontwait) {
|
||||
int osal_txn_lock(MDBX_env *env, bool dontwait) {
|
||||
if (dontwait) {
|
||||
if (!TryEnterCriticalSection(&env->me_windowsbug_lock))
|
||||
return MDBX_BUSY;
|
||||
@ -190,16 +190,13 @@ int mdbx_txn_lock(MDBX_env *env, bool dontwait) {
|
||||
0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
|
||||
? EXCEPTION_EXECUTE_HANDLER
|
||||
: EXCEPTION_CONTINUE_SEARCH) {
|
||||
return ERROR_POSSIBLE_DEADLOCK;
|
||||
return MDBX_EDEADLK;
|
||||
}
|
||||
}
|
||||
|
||||
if (env->me_flags & MDBX_EXCLUSIVE) {
|
||||
/* Zap: Failing to release lock 'env->me_windowsbug_lock'
|
||||
* in function 'mdbx_txn_lock' */
|
||||
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
eASSERT(env, !env->me_txn0->mt_owner);
|
||||
if (env->me_flags & MDBX_EXCLUSIVE)
|
||||
goto done;
|
||||
|
||||
const HANDLE fd4data =
|
||||
env->me_overlapped_fd ? env->me_overlapped_fd : env->me_lazy_fd;
|
||||
@ -218,17 +215,20 @@ int mdbx_txn_lock(MDBX_env *env, bool dontwait) {
|
||||
}
|
||||
}
|
||||
if (rc == MDBX_SUCCESS) {
|
||||
done:
|
||||
/* Zap: Failing to release lock 'env->me_windowsbug_lock'
|
||||
* in function 'mdbx_txn_lock' */
|
||||
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
|
||||
return rc;
|
||||
env->me_txn0->mt_owner = osal_thread_self();
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&env->me_windowsbug_lock);
|
||||
return (!dontwait || rc != ERROR_LOCK_VIOLATION) ? rc : MDBX_BUSY;
|
||||
}
|
||||
|
||||
void mdbx_txn_unlock(MDBX_env *env) {
|
||||
void osal_txn_unlock(MDBX_env *env) {
|
||||
eASSERT(env, env->me_txn0->mt_owner == osal_thread_self());
|
||||
if ((env->me_flags & MDBX_EXCLUSIVE) == 0) {
|
||||
const HANDLE fd4data =
|
||||
env->me_overlapped_fd ? env->me_overlapped_fd : env->me_lazy_fd;
|
||||
@ -236,6 +236,7 @@ void mdbx_txn_unlock(MDBX_env *env) {
|
||||
if (err != MDBX_SUCCESS)
|
||||
mdbx_panic("%s failed: err %u", __func__, err);
|
||||
}
|
||||
env->me_txn0->mt_owner = 0;
|
||||
LeaveCriticalSection(&env->me_windowsbug_lock);
|
||||
}
|
||||
|
||||
@ -442,7 +443,7 @@ osal_resume_threads_after_remap(mdbx_handle_array_t *array) {
|
||||
* The osal_lck_downgrade() moves the locking-FSM from "exclusive write"
|
||||
* state to the "used" (i.e. shared) state.
|
||||
*
|
||||
* The mdbx_lck_upgrade() moves the locking-FSM from "used" (i.e. shared)
|
||||
* The osal_lck_upgrade() moves the locking-FSM from "used" (i.e. shared)
|
||||
* state to the "exclusive write" state.
|
||||
*/
|
||||
|
||||
@ -615,7 +616,7 @@ MDBX_INTERNAL_FUNC int osal_lck_downgrade(MDBX_env *env) {
|
||||
return MDBX_SUCCESS /* 5) now at S-? (used), done */;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_lck_upgrade(MDBX_env *env) {
|
||||
MDBX_INTERNAL_FUNC int osal_lck_upgrade(MDBX_env *env, bool dont_wait) {
|
||||
/* Transite from used state (S-?) to exclusive-write (E-E) */
|
||||
assert(env->me_lfd != INVALID_HANDLE_VALUE);
|
||||
|
||||
@ -625,7 +626,9 @@ MDBX_INTERNAL_FUNC int mdbx_lck_upgrade(MDBX_env *env) {
|
||||
|
||||
/* 1) now on S-? (used), try S-E (locked) */
|
||||
jitter4testing(false);
|
||||
int rc = flock(env->me_lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_UPPER);
|
||||
int rc = flock(env->me_lfd,
|
||||
dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE,
|
||||
LCK_UPPER);
|
||||
if (rc != MDBX_SUCCESS) {
|
||||
/* 2) something went wrong, give up */;
|
||||
VERBOSE("%s, err %u", "S-?(used) >> S-E(locked)", rc);
|
||||
@ -640,7 +643,9 @@ MDBX_INTERNAL_FUNC int mdbx_lck_upgrade(MDBX_env *env) {
|
||||
|
||||
/* 4) now on ?-E (middle), try E-E (exclusive-write) */
|
||||
jitter4testing(false);
|
||||
rc = flock(env->me_lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_LOWER);
|
||||
rc = flock(env->me_lfd,
|
||||
dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE,
|
||||
LCK_LOWER);
|
||||
if (rc != MDBX_SUCCESS) {
|
||||
/* 5) something went wrong, give up */;
|
||||
VERBOSE("%s, err %u", "?-E(middle) >> E-E(exclusive-write)", rc);
|
||||
@ -677,7 +682,9 @@ MDBX_INTERNAL_FUNC int osal_lck_init(MDBX_env *env,
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env,
|
||||
MDBX_env *inprocess_neighbor) {
|
||||
MDBX_env *inprocess_neighbor,
|
||||
const uint32_t current_pid) {
|
||||
(void)current_pid;
|
||||
/* LY: should unmap before releasing the locks to avoid race condition and
|
||||
* STATUS_USER_MAPPED_FILE/ERROR_USER_MAPPED_FILE */
|
||||
if (env->me_map)
|
||||
@ -686,7 +693,7 @@ MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env,
|
||||
const bool synced = env->me_lck_mmap.lck->mti_unsynced_pages.weak == 0;
|
||||
osal_munmap(&env->me_lck_mmap);
|
||||
if (synced && !inprocess_neighbor && env->me_lfd != INVALID_HANDLE_VALUE &&
|
||||
mdbx_lck_upgrade(env) == MDBX_SUCCESS)
|
||||
osal_lck_upgrade(env, true) == MDBX_SUCCESS)
|
||||
/* this will fail if LCK is used/mmapped by other process(es) */
|
||||
osal_ftruncate(env->me_lfd, 0);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.TH MDBX_CHK 1 "2024-03-13" "MDBX 0.12.10"
|
||||
.TH MDBX_CHK 1 "2024-03-21" "MDBX 0.13"
|
||||
.SH NAME
|
||||
mdbx_chk \- MDBX checking tool
|
||||
.SH SYNOPSIS
|
||||
|
@ -2,7 +2,7 @@
|
||||
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
||||
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.TH MDBX_COPY 1 "2024-03-13" "MDBX 0.12.10"
|
||||
.TH MDBX_COPY 1 "2024-03-21" "MDBX 0.13"
|
||||
.SH NAME
|
||||
mdbx_copy \- MDBX environment copy tool
|
||||
.SH SYNOPSIS
|
||||
|
@ -1,7 +1,7 @@
|
||||
.\" Copyright 2021-2024 Leonid Yuriev <leo@yuriev.ru>.
|
||||
.\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.TH MDBX_DROP 1 "2024-03-13" "MDBX 0.12.10"
|
||||
.TH MDBX_DROP 1 "2024-03-21" "MDBX 0.13"
|
||||
.SH NAME
|
||||
mdbx_drop \- MDBX database delete tool
|
||||
.SH SYNOPSIS
|
||||
|
@ -2,7 +2,7 @@
|
||||
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
||||
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.TH MDBX_DUMP 1 "2024-03-13" "MDBX 0.12.10"
|
||||
.TH MDBX_DUMP 1 "2024-03-21" "MDBX 0.13"
|
||||
.SH NAME
|
||||
mdbx_dump \- MDBX environment export tool
|
||||
.SH SYNOPSIS
|
||||
|
@ -2,7 +2,7 @@
|
||||
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
||||
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.TH MDBX_LOAD 1 "2024-03-13" "MDBX 0.12.10"
|
||||
.TH MDBX_LOAD 1 "2024-03-21" "MDBX 0.13"
|
||||
.SH NAME
|
||||
mdbx_load \- MDBX environment import tool
|
||||
.SH SYNOPSIS
|
||||
|
@ -2,7 +2,7 @@
|
||||
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
||||
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.TH MDBX_STAT 1 "2024-03-13" "MDBX 0.12.10"
|
||||
.TH MDBX_STAT 1 "2024-03-21" "MDBX 0.13"
|
||||
.SH NAME
|
||||
mdbx_stat \- MDBX environment status tool
|
||||
.SH SYNOPSIS
|
||||
|
51
src/mdbx.c++
51
src/mdbx.c++
@ -271,6 +271,11 @@ namespace mdbx {
|
||||
"into an incompatible memory allocation scheme.");
|
||||
}
|
||||
|
||||
[[noreturn]] __cold void throw_incomparable_cursors() {
|
||||
throw std::logic_error(
|
||||
"mdbx:: incomparable and/or invalid cursors to compare positions.");
|
||||
}
|
||||
|
||||
[[noreturn]] __cold void throw_bad_value_size() {
|
||||
throw bad_value_size(MDBX_BAD_VALSIZE);
|
||||
}
|
||||
@ -324,6 +329,8 @@ DEFINE_EXCEPTION(thread_mismatch)
|
||||
DEFINE_EXCEPTION(transaction_full)
|
||||
DEFINE_EXCEPTION(transaction_overlapping)
|
||||
DEFINE_EXCEPTION(duplicated_lck_file)
|
||||
DEFINE_EXCEPTION(dangling_map_id)
|
||||
|
||||
#undef DEFINE_EXCEPTION
|
||||
|
||||
__cold const char *error::what() const noexcept {
|
||||
@ -410,6 +417,7 @@ __cold void error::throw_exception() const {
|
||||
CASE_EXCEPTION(transaction_full, MDBX_TXN_FULL);
|
||||
CASE_EXCEPTION(transaction_overlapping, MDBX_TXN_OVERLAPPING);
|
||||
CASE_EXCEPTION(duplicated_lck_file, MDBX_DUPLICATED_CLK);
|
||||
CASE_EXCEPTION(dangling_map_id, MDBX_DANGLING_DBI);
|
||||
#undef CASE_EXCEPTION
|
||||
default:
|
||||
if (is_mdbx_error())
|
||||
@ -527,48 +535,48 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
|
||||
}
|
||||
|
||||
#ifdef MDBX_U128_TYPE
|
||||
MDBX_U128_TYPE slice::as_uint128() const {
|
||||
MDBX_U128_TYPE slice::as_uint128_adapt() const {
|
||||
static_assert(sizeof(MDBX_U128_TYPE) == 16, "WTF?");
|
||||
if (size() == 16) {
|
||||
MDBX_U128_TYPE r;
|
||||
memcpy(&r, data(), sizeof(r));
|
||||
return r;
|
||||
} else
|
||||
return as_uint64();
|
||||
return as_uint64_adapt();
|
||||
}
|
||||
#endif /* MDBX_U128_TYPE */
|
||||
|
||||
uint64_t slice::as_uint64() const {
|
||||
uint64_t slice::as_uint64_adapt() const {
|
||||
static_assert(sizeof(uint64_t) == 8, "WTF?");
|
||||
if (size() == 8) {
|
||||
uint64_t r;
|
||||
memcpy(&r, data(), sizeof(r));
|
||||
return r;
|
||||
} else
|
||||
return as_uint32();
|
||||
return as_uint32_adapt();
|
||||
}
|
||||
|
||||
uint32_t slice::as_uint32() const {
|
||||
uint32_t slice::as_uint32_adapt() const {
|
||||
static_assert(sizeof(uint32_t) == 4, "WTF?");
|
||||
if (size() == 4) {
|
||||
uint32_t r;
|
||||
memcpy(&r, data(), sizeof(r));
|
||||
return r;
|
||||
} else
|
||||
return as_uint16();
|
||||
return as_uint16_adapt();
|
||||
}
|
||||
|
||||
uint16_t slice::as_uint16() const {
|
||||
uint16_t slice::as_uint16_adapt() const {
|
||||
static_assert(sizeof(uint16_t) == 2, "WTF?");
|
||||
if (size() == 2) {
|
||||
uint16_t r;
|
||||
memcpy(&r, data(), sizeof(r));
|
||||
return r;
|
||||
} else
|
||||
return as_uint8();
|
||||
return as_uint8_adapt();
|
||||
}
|
||||
|
||||
uint8_t slice::as_uint8() const {
|
||||
uint8_t slice::as_uint8_adapt() const {
|
||||
static_assert(sizeof(uint8_t) == 1, "WTF?");
|
||||
if (size() == 1)
|
||||
return *static_cast<const uint8_t *>(data());
|
||||
@ -579,48 +587,48 @@ uint8_t slice::as_uint8() const {
|
||||
}
|
||||
|
||||
#ifdef MDBX_I128_TYPE
|
||||
MDBX_I128_TYPE slice::as_int128() const {
|
||||
MDBX_I128_TYPE slice::as_int128_adapt() const {
|
||||
static_assert(sizeof(MDBX_I128_TYPE) == 16, "WTF?");
|
||||
if (size() == 16) {
|
||||
MDBX_I128_TYPE r;
|
||||
memcpy(&r, data(), sizeof(r));
|
||||
return r;
|
||||
} else
|
||||
return as_int64();
|
||||
return as_int64_adapt();
|
||||
}
|
||||
#endif /* MDBX_I128_TYPE */
|
||||
|
||||
int64_t slice::as_int64() const {
|
||||
int64_t slice::as_int64_adapt() const {
|
||||
static_assert(sizeof(int64_t) == 8, "WTF?");
|
||||
if (size() == 8) {
|
||||
uint64_t r;
|
||||
memcpy(&r, data(), sizeof(r));
|
||||
return r;
|
||||
} else
|
||||
return as_int32();
|
||||
return as_int32_adapt();
|
||||
}
|
||||
|
||||
int32_t slice::as_int32() const {
|
||||
int32_t slice::as_int32_adapt() const {
|
||||
static_assert(sizeof(int32_t) == 4, "WTF?");
|
||||
if (size() == 4) {
|
||||
int32_t r;
|
||||
memcpy(&r, data(), sizeof(r));
|
||||
return r;
|
||||
} else
|
||||
return as_int16();
|
||||
return as_int16_adapt();
|
||||
}
|
||||
|
||||
int16_t slice::as_int16() const {
|
||||
int16_t slice::as_int16_adapt() const {
|
||||
static_assert(sizeof(int16_t) == 2, "WTF?");
|
||||
if (size() == 2) {
|
||||
int16_t r;
|
||||
memcpy(&r, data(), sizeof(r));
|
||||
return r;
|
||||
} else
|
||||
return as_int8();
|
||||
return as_int8_adapt();
|
||||
}
|
||||
|
||||
int8_t slice::as_int8() const {
|
||||
int8_t slice::as_int8_adapt() const {
|
||||
if (size() == 1)
|
||||
return *static_cast<const int8_t *>(data());
|
||||
else if (size() == 0)
|
||||
@ -1535,6 +1543,13 @@ void txn_managed::commit(commit_latency *latency) {
|
||||
MDBX_CXX20_UNLIKELY err.throw_exception();
|
||||
}
|
||||
|
||||
void txn_managed::commit_embark_read() {
|
||||
auto env = this->env();
|
||||
commit();
|
||||
error::success_or_throw(
|
||||
::mdbx_txn_begin(env, nullptr, MDBX_TXN_RDONLY, &handle_));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool txn::drop_map(const char *name, bool throw_if_absent) {
|
||||
|
1898
src/mdbx_chk.c
1898
src/mdbx_chk.c
File diff suppressed because it is too large
Load Diff
235
src/mdbx_load.c
235
src/mdbx_load.c
@ -505,7 +505,7 @@ static int equal_or_greater(const MDBX_val *a, const MDBX_val *b) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, rc;
|
||||
int i, err;
|
||||
MDBX_env *env = nullptr;
|
||||
MDBX_txn *txn = nullptr;
|
||||
MDBX_cursor *mc = nullptr;
|
||||
@ -608,40 +608,45 @@ int main(int argc, char *argv[]) {
|
||||
dbuf.iov_len = 4096;
|
||||
dbuf.iov_base = osal_malloc(dbuf.iov_len);
|
||||
if (!dbuf.iov_base) {
|
||||
rc = MDBX_ENOMEM;
|
||||
error("value-buffer", rc);
|
||||
goto env_close;
|
||||
err = MDBX_ENOMEM;
|
||||
error("value-buffer", err);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
/* read first header for mapsize= */
|
||||
if (!(mode & NOHDR)) {
|
||||
rc = readhdr();
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
if (rc == EOF)
|
||||
rc = MDBX_ENODATA;
|
||||
error("readheader", rc);
|
||||
goto env_close;
|
||||
err = readhdr();
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
if (err == EOF)
|
||||
err = MDBX_ENODATA;
|
||||
error("readheader", err);
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
|
||||
rc = mdbx_env_create(&env);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_env_create", rc);
|
||||
return EXIT_FAILURE;
|
||||
err = mdbx_env_create(&env);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_env_create", err);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
err = mdbx_env_set_maxdbs(env, 2);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_env_set_maxdbs", err);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
mdbx_env_set_maxdbs(env, 2);
|
||||
if (envinfo.mi_maxreaders) {
|
||||
rc = mdbx_env_set_maxreaders(env, envinfo.mi_maxreaders);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_env_set_maxreaders", rc);
|
||||
goto env_close;
|
||||
err = mdbx_env_set_maxreaders(env, envinfo.mi_maxreaders);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_env_set_maxreaders", err);
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
|
||||
if (envinfo.mi_geo.current | envinfo.mi_mapsize) {
|
||||
if (envinfo.mi_geo.current) {
|
||||
rc = mdbx_env_set_geometry(
|
||||
err = mdbx_env_set_geometry(
|
||||
env, (intptr_t)envinfo.mi_geo.lower, (intptr_t)envinfo.mi_geo.current,
|
||||
(intptr_t)envinfo.mi_geo.upper, (intptr_t)envinfo.mi_geo.shrink,
|
||||
(intptr_t)envinfo.mi_geo.grow,
|
||||
@ -654,23 +659,23 @@ int main(int argc, char *argv[]) {
|
||||
"Database size is too large for current system (mapsize=%" PRIu64
|
||||
" is great than system-limit %zu)\n",
|
||||
envinfo.mi_mapsize, (size_t)MAX_MAPSIZE);
|
||||
goto env_close;
|
||||
goto bailout;
|
||||
}
|
||||
rc = mdbx_env_set_geometry(
|
||||
err = mdbx_env_set_geometry(
|
||||
env, (intptr_t)envinfo.mi_mapsize, (intptr_t)envinfo.mi_mapsize,
|
||||
(intptr_t)envinfo.mi_mapsize, 0, 0,
|
||||
envinfo.mi_dxb_pagesize ? (intptr_t)envinfo.mi_dxb_pagesize : -1);
|
||||
}
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_env_set_geometry", rc);
|
||||
goto env_close;
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_env_set_geometry", err);
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
|
||||
rc = mdbx_env_open(env, envname, envflags, 0664);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_env_open", rc);
|
||||
goto env_close;
|
||||
err = mdbx_env_open(env, envname, envflags, 0664);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_env_open", err);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
kbuf.iov_len = mdbx_env_get_maxvalsize_ex(env, 0) + (size_t)1;
|
||||
@ -678,54 +683,54 @@ int main(int argc, char *argv[]) {
|
||||
if (!quiet)
|
||||
fprintf(stderr, "mdbx_env_get_maxkeysize() failed, returns %zu\n",
|
||||
kbuf.iov_len);
|
||||
goto env_close;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
kbuf.iov_base = malloc(kbuf.iov_len);
|
||||
if (!kbuf.iov_base) {
|
||||
rc = MDBX_ENOMEM;
|
||||
error("key-buffer", rc);
|
||||
goto env_close;
|
||||
err = MDBX_ENOMEM;
|
||||
error("key-buffer", err);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
while (rc == MDBX_SUCCESS) {
|
||||
while (err == MDBX_SUCCESS) {
|
||||
if (user_break) {
|
||||
rc = MDBX_EINTR;
|
||||
err = MDBX_EINTR;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = mdbx_txn_begin(env, nullptr, 0, &txn);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_txn_begin", rc);
|
||||
goto env_close;
|
||||
err = mdbx_txn_begin(env, nullptr, 0, &txn);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_txn_begin", err);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (mode & GLOBAL) {
|
||||
mode -= GLOBAL;
|
||||
if (canary.v | canary.x | canary.y | canary.z) {
|
||||
rc = mdbx_canary_put(txn, &canary);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_canary_put", rc);
|
||||
goto txn_abort;
|
||||
err = mdbx_canary_put(txn, &canary);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_canary_put", err);
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *const dbi_name = subname ? subname : "@MAIN";
|
||||
rc =
|
||||
err =
|
||||
mdbx_dbi_open_ex(txn, subname, dbi_flags | MDBX_CREATE, &dbi,
|
||||
(putflags & MDBX_APPEND) ? equal_or_greater : nullptr,
|
||||
(putflags & MDBX_APPEND) ? equal_or_greater : nullptr);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_dbi_open_ex", rc);
|
||||
goto txn_abort;
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_dbi_open_ex", err);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
uint64_t present_sequence;
|
||||
rc = mdbx_dbi_sequence(txn, dbi, &present_sequence, 0);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_dbi_sequence", rc);
|
||||
goto txn_abort;
|
||||
err = mdbx_dbi_sequence(txn, dbi, &present_sequence, 0);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_dbi_sequence", err);
|
||||
goto bailout;
|
||||
}
|
||||
if (present_sequence > sequence) {
|
||||
if (!quiet)
|
||||
@ -733,22 +738,22 @@ int main(int argc, char *argv[]) {
|
||||
"present sequence for '%s' value (%" PRIu64
|
||||
") is greater than loaded (%" PRIu64 ")\n",
|
||||
dbi_name, present_sequence, sequence);
|
||||
rc = MDBX_RESULT_TRUE;
|
||||
goto txn_abort;
|
||||
err = MDBX_RESULT_TRUE;
|
||||
goto bailout;
|
||||
}
|
||||
if (present_sequence < sequence) {
|
||||
rc = mdbx_dbi_sequence(txn, dbi, nullptr, sequence - present_sequence);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_dbi_sequence", rc);
|
||||
goto txn_abort;
|
||||
err = mdbx_dbi_sequence(txn, dbi, nullptr, sequence - present_sequence);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_dbi_sequence", err);
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
|
||||
if (purge) {
|
||||
rc = mdbx_drop(txn, dbi, false);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_drop", rc);
|
||||
goto txn_abort;
|
||||
err = mdbx_drop(txn, dbi, false);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_drop", err);
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
|
||||
@ -756,85 +761,85 @@ int main(int argc, char *argv[]) {
|
||||
putflags = (dbi_flags & MDBX_DUPSORT) ? putflags | MDBX_APPENDDUP
|
||||
: putflags & ~MDBX_APPENDDUP;
|
||||
|
||||
rc = mdbx_cursor_open(txn, dbi, &mc);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_cursor_open", rc);
|
||||
goto txn_abort;
|
||||
err = mdbx_cursor_open(txn, dbi, &mc);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_cursor_open", err);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
int batch = 0;
|
||||
while (rc == MDBX_SUCCESS) {
|
||||
while (err == MDBX_SUCCESS) {
|
||||
MDBX_val key, data;
|
||||
rc = readline(&key, &kbuf);
|
||||
if (rc == EOF)
|
||||
err = readline(&key, &kbuf);
|
||||
if (err == EOF)
|
||||
break;
|
||||
|
||||
if (rc == MDBX_SUCCESS)
|
||||
rc = readline(&data, &dbuf);
|
||||
if (rc) {
|
||||
if (err == MDBX_SUCCESS)
|
||||
err = readline(&data, &dbuf);
|
||||
if (err) {
|
||||
if (!quiet)
|
||||
fprintf(stderr, "%s: line %" PRIiSIZE ": failed to read key value\n",
|
||||
prog, lineno);
|
||||
goto txn_abort;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
rc = mdbx_cursor_put(mc, &key, &data, putflags);
|
||||
if (rc == MDBX_KEYEXIST && putflags)
|
||||
err = mdbx_cursor_put(mc, &key, &data, putflags);
|
||||
if (err == MDBX_KEYEXIST && putflags)
|
||||
continue;
|
||||
if (rc == MDBX_BAD_VALSIZE && rescue) {
|
||||
if (err == MDBX_BAD_VALSIZE && rescue) {
|
||||
if (!quiet)
|
||||
fprintf(stderr, "%s: skip line %" PRIiSIZE ": due %s\n", prog, lineno,
|
||||
mdbx_strerror(rc));
|
||||
mdbx_strerror(err));
|
||||
continue;
|
||||
}
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_cursor_put", rc);
|
||||
goto txn_abort;
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_cursor_put", err);
|
||||
goto bailout;
|
||||
}
|
||||
batch++;
|
||||
|
||||
MDBX_txn_info txn_info;
|
||||
rc = mdbx_txn_info(txn, &txn_info, false);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_txn_info", rc);
|
||||
goto txn_abort;
|
||||
err = mdbx_txn_info(txn, &txn_info, false);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_txn_info", err);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (batch == 10000 || txn_info.txn_space_dirty > MEGABYTE * 256) {
|
||||
rc = mdbx_txn_commit(txn);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_txn_commit", rc);
|
||||
goto env_close;
|
||||
err = mdbx_txn_commit(txn);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_txn_commit", err);
|
||||
goto bailout;
|
||||
}
|
||||
batch = 0;
|
||||
|
||||
rc = mdbx_txn_begin(env, nullptr, 0, &txn);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_txn_begin", rc);
|
||||
goto env_close;
|
||||
err = mdbx_txn_begin(env, nullptr, 0, &txn);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_txn_begin", err);
|
||||
goto bailout;
|
||||
}
|
||||
rc = mdbx_cursor_bind(txn, mc, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_cursor_bind", rc);
|
||||
goto txn_abort;
|
||||
err = mdbx_cursor_bind(txn, mc, dbi);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_cursor_bind", err);
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mdbx_cursor_close(mc);
|
||||
mc = nullptr;
|
||||
rc = mdbx_txn_commit(txn);
|
||||
err = mdbx_txn_commit(txn);
|
||||
txn = nullptr;
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_txn_commit", rc);
|
||||
goto env_close;
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_txn_commit", err);
|
||||
goto bailout;
|
||||
}
|
||||
if (subname) {
|
||||
assert(dbi != MAIN_DBI);
|
||||
rc = mdbx_dbi_close(env, dbi);
|
||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||
error("mdbx_dbi_close", rc);
|
||||
goto env_close;
|
||||
err = mdbx_dbi_close(env, dbi);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
error("mdbx_dbi_close", err);
|
||||
goto bailout;
|
||||
}
|
||||
} else {
|
||||
assert(dbi == MAIN_DBI);
|
||||
@ -842,14 +847,14 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
/* try read next header */
|
||||
if (!(mode & NOHDR))
|
||||
rc = readhdr();
|
||||
err = readhdr();
|
||||
else if (ferror(stdin) || feof(stdin))
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rc) {
|
||||
switch (err) {
|
||||
case EOF:
|
||||
rc = MDBX_SUCCESS;
|
||||
err = MDBX_SUCCESS;
|
||||
case MDBX_SUCCESS:
|
||||
break;
|
||||
case MDBX_EINTR:
|
||||
@ -857,17 +862,19 @@ int main(int argc, char *argv[]) {
|
||||
fprintf(stderr, "Interrupted by signal/user\n");
|
||||
break;
|
||||
default:
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
error("readline", rc);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
error("readline", err);
|
||||
}
|
||||
|
||||
txn_abort:
|
||||
mdbx_cursor_close(mc);
|
||||
mdbx_txn_abort(txn);
|
||||
env_close:
|
||||
mdbx_env_close(env);
|
||||
bailout:
|
||||
if (mc)
|
||||
mdbx_cursor_close(mc);
|
||||
if (txn)
|
||||
mdbx_txn_abort(txn);
|
||||
if (env)
|
||||
mdbx_env_close(env);
|
||||
free(kbuf.iov_base);
|
||||
free(dbuf.iov_base);
|
||||
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
return err ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -164,6 +164,20 @@
|
||||
#error MDBX_AVOID_MSYNC must be defined as 0 or 1
|
||||
#endif /* MDBX_AVOID_MSYNC */
|
||||
|
||||
/** FIXME */
|
||||
#ifndef MDBX_ENABLE_DBI_SPARSE
|
||||
#define MDBX_ENABLE_DBI_SPARSE 1
|
||||
#elif !(MDBX_ENABLE_DBI_SPARSE == 0 || MDBX_ENABLE_DBI_SPARSE == 1)
|
||||
#error MDBX_ENABLE_DBI_SPARSE must be defined as 0 or 1
|
||||
#endif /* MDBX_ENABLE_DBI_SPARSE */
|
||||
|
||||
/** FIXME */
|
||||
#ifndef MDBX_ENABLE_DBI_LOCKFREE
|
||||
#define MDBX_ENABLE_DBI_LOCKFREE 1
|
||||
#elif !(MDBX_ENABLE_DBI_LOCKFREE == 0 || MDBX_ENABLE_DBI_LOCKFREE == 1)
|
||||
#error MDBX_ENABLE_DBI_LOCKFREE must be defined as 0 or 1
|
||||
#endif /* MDBX_ENABLE_DBI_LOCKFREE */
|
||||
|
||||
/** Controls sort order of internal page number lists.
|
||||
* This mostly experimental/advanced option with not for regular MDBX users.
|
||||
* \warning The database format depend on this option and libmdbx built with
|
||||
@ -211,8 +225,8 @@
|
||||
|
||||
/** If defined then enables integration with Valgrind,
|
||||
* a memory analyzing tool. */
|
||||
#ifndef MDBX_USE_VALGRIND
|
||||
#endif /* MDBX_USE_VALGRIND */
|
||||
#ifndef ENABLE_MEMCHECK
|
||||
#endif /* ENABLE_MEMCHECK */
|
||||
|
||||
/** If defined then enables use C11 atomics,
|
||||
* otherwise detects ones availability automatically. */
|
||||
|
14
src/osal.c
14
src/osal.c
@ -503,8 +503,18 @@ MDBX_INTERNAL_FUNC int osal_fastmutex_init(osal_fastmutex_t *fastmutex) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
InitializeCriticalSection(fastmutex);
|
||||
return MDBX_SUCCESS;
|
||||
#elif MDBX_DEBUG
|
||||
pthread_mutexattr_t ma;
|
||||
int rc = pthread_mutexattr_init(&ma);
|
||||
if (likely(!rc)) {
|
||||
rc = pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK);
|
||||
if (likely(!rc) || rc == ENOTSUP)
|
||||
rc = pthread_mutex_init(fastmutex, &ma);
|
||||
pthread_mutexattr_destroy(&ma);
|
||||
}
|
||||
return rc;
|
||||
#else
|
||||
return pthread_mutex_init(fastmutex, NULL);
|
||||
return pthread_mutex_init(fastmutex, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -526,7 +536,7 @@ MDBX_INTERNAL_FUNC int osal_fastmutex_acquire(osal_fastmutex_t *fastmutex) {
|
||||
0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
|
||||
? EXCEPTION_EXECUTE_HANDLER
|
||||
: EXCEPTION_CONTINUE_SEARCH) {
|
||||
return ERROR_POSSIBLE_DEADLOCK;
|
||||
return MDBX_EDEADLK;
|
||||
}
|
||||
return MDBX_SUCCESS;
|
||||
#else
|
||||
|
17
src/osal.h
17
src/osal.h
@ -690,7 +690,8 @@ MDBX_INTERNAL_FUNC int osal_lck_init(MDBX_env *env,
|
||||
/// restore POSIX-fcntl locks after the closing of file descriptors.
|
||||
/// \return Error code (MDBX_PANIC) or zero on success.
|
||||
MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env,
|
||||
MDBX_env *inprocess_neighbor);
|
||||
MDBX_env *inprocess_neighbor,
|
||||
const uint32_t current_pid);
|
||||
|
||||
/// \brief Connects to shared interprocess locking objects and tries to acquire
|
||||
/// the maximum lock level (shared if exclusive is not available)
|
||||
@ -718,6 +719,8 @@ MDBX_INTERNAL_FUNC int osal_lck_seize(MDBX_env *env);
|
||||
/// operational lock.
|
||||
/// \return Error code or zero on success
|
||||
MDBX_INTERNAL_FUNC int osal_lck_downgrade(MDBX_env *env);
|
||||
MDBX_MAYBE_UNUSED MDBX_INTERNAL_FUNC int osal_lck_upgrade(MDBX_env *env,
|
||||
bool dont_wait);
|
||||
|
||||
/// \brief Locks LCK-file or/and table of readers for (de)registering.
|
||||
/// \return Error code or zero on success
|
||||
@ -726,16 +729,12 @@ MDBX_INTERNAL_FUNC int osal_rdt_lock(MDBX_env *env);
|
||||
/// \brief Unlocks LCK-file or/and table of readers after (de)registering.
|
||||
MDBX_INTERNAL_FUNC void osal_rdt_unlock(MDBX_env *env);
|
||||
|
||||
/// \brief Acquires lock for DB change (on writing transaction start)
|
||||
/// Reading transactions will not be blocked.
|
||||
/// Declared as LIBMDBX_API because it is used in mdbx_chk.
|
||||
/// \brief Acquires write-transaction lock.
|
||||
/// \return Error code or zero on success
|
||||
LIBMDBX_API int mdbx_txn_lock(MDBX_env *env, bool dont_wait);
|
||||
MDBX_INTERNAL_FUNC int osal_txn_lock(MDBX_env *env, bool dont_wait);
|
||||
|
||||
/// \brief Releases lock once DB changes is made (after writing transaction
|
||||
/// has finished).
|
||||
/// Declared as LIBMDBX_API because it is used in mdbx_chk.
|
||||
LIBMDBX_API void mdbx_txn_unlock(MDBX_env *env);
|
||||
/// \brief Releases write-transaction lock..
|
||||
MDBX_INTERNAL_FUNC void osal_txn_unlock(MDBX_env *env);
|
||||
|
||||
/// \brief Sets alive-flag of reader presence (indicative lock) for PID of
|
||||
/// the current process. The function does no more than needed for
|
||||
|
@ -28,6 +28,7 @@ set(LIBMDBX_TEST_SOURCES
|
||||
append.c++
|
||||
ttl.c++
|
||||
nested.c++
|
||||
fork.c++
|
||||
)
|
||||
|
||||
if(NOT MDBX_BUILD_CXX)
|
||||
@ -91,6 +92,20 @@ if(UNIX AND NOT SUBPROJECT)
|
||||
set_target_properties(test_extra_dupfixed_multiple PROPERTIES
|
||||
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
add_executable(test_extra_hex_base64_base58 extra/hex_base64_base58.c++)
|
||||
target_include_directories(test_extra_hex_base64_base58 PRIVATE "${PROJECT_SOURCE_DIR}")
|
||||
target_link_libraries(test_extra_hex_base64_base58 ${TOOL_MDBX_LIB})
|
||||
if(MDBX_CXX_STANDARD)
|
||||
set_target_properties(test_extra_hex_base64_base58 PROPERTIES
|
||||
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
add_executable(test_extra_doubtless_positioning extra/doubtless_positioning.c++)
|
||||
target_include_directories(test_extra_doubtless_positioning PRIVATE "${PROJECT_SOURCE_DIR}")
|
||||
target_link_libraries(test_extra_doubtless_positioning ${TOOL_MDBX_LIB})
|
||||
if(MDBX_CXX_STANDARD)
|
||||
set_target_properties(test_extra_doubtless_positioning PROPERTIES
|
||||
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -107,7 +122,7 @@ else()
|
||||
|
||||
add_test(NAME smoke COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test
|
||||
--loglevel=verbose
|
||||
--keygen.seed=${test_seed}
|
||||
--prng-seed=${test_seed}
|
||||
--progress --console=no --pathname=smoke.db --dont-cleanup-after basic)
|
||||
set_tests_properties(smoke PROPERTIES
|
||||
TIMEOUT 600
|
||||
@ -129,11 +144,11 @@ else()
|
||||
|
||||
add_test(NAME dupsort_writemap COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test
|
||||
--loglevel=notice
|
||||
--keygen.seed=${test_seed}
|
||||
--table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no
|
||||
--prng-seed=${test_seed}
|
||||
--table=+data.fixed --keygen.split=29 --datalen=rnd --progress --console=no
|
||||
--repeat=2 --pathname=dupsort_writemap.db --dont-cleanup-after basic)
|
||||
set_tests_properties(dupsort_writemap PROPERTIES
|
||||
TIMEOUT 600
|
||||
TIMEOUT 3600
|
||||
RUN_SERIAL OFF)
|
||||
if(MDBX_BUILD_TOOLS)
|
||||
add_test(NAME dupsort_writemap_chk COMMAND ${MDBX_OUTPUT_DIR}/mdbx_chk -nvvwc dupsort_writemap.db)
|
||||
@ -176,6 +191,11 @@ else()
|
||||
if(MDBX_BUILD_CXX)
|
||||
add_test(NAME extra_maindb_ordinal COMMAND test_extra_maindb_ordinal)
|
||||
add_test(NAME extra_dupfixed_multiple COMMAND test_extra_dupfixed_multiple)
|
||||
add_test(NAME extra_hex_base64_base58 COMMAND test_extra_hex_base64_base58)
|
||||
add_test(NAME extra_doubtless_positioning COMMAND test_extra_doubtless_positioning)
|
||||
if (ENABLE_MEMCHECK)
|
||||
set_tests_properties(extra_doubtless_positioning PROPERTIES TIMEOUT 10800)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -20,8 +20,8 @@ public:
|
||||
: testcase(config, pid) {}
|
||||
bool run() override;
|
||||
|
||||
static bool review_params(actor_params ¶ms) {
|
||||
if (!testcase::review_params(params))
|
||||
static bool review_params(actor_params ¶ms, unsigned space_id) {
|
||||
if (!testcase::review_params(params, space_id))
|
||||
return false;
|
||||
const bool ordered = !flipcoin_x3();
|
||||
log_notice("the '%s' key-generation mode is selected",
|
||||
@ -45,7 +45,7 @@ bool testcase_append::run() {
|
||||
}
|
||||
|
||||
cursor_open(dbi);
|
||||
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
|
||||
keyvalue_maker.setup(config.params, 0 /* thread_number */);
|
||||
/* LY: тест наполнения таблиц в append-режиме,
|
||||
* при котором записи добавляются строго в конец (в порядке сортировки) */
|
||||
const MDBX_put_flags_t flags =
|
||||
|
@ -41,8 +41,9 @@ testcase *registry::create_actor(const actor_config &config,
|
||||
}
|
||||
|
||||
bool registry::review_actor_params(const actor_testcase id,
|
||||
actor_params ¶ms) {
|
||||
return instance()->id2record.at(id)->review_params(params);
|
||||
actor_params ¶ms,
|
||||
const unsigned space_id) {
|
||||
return instance()->id2record.at(id)->review_params(params, space_id);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -78,13 +79,13 @@ void configure_actor(unsigned &last_space_id, const actor_testcase testcase,
|
||||
failure("The '%s' is unexpected for space-id\n", end);
|
||||
}
|
||||
|
||||
if (!registry::review_actor_params(testcase, params))
|
||||
failure("Actor config-review failed for space-id %lu\n", space_id);
|
||||
|
||||
if (space_id > ACTOR_ID_MAX)
|
||||
failure("Invalid space-id %lu\n", space_id);
|
||||
last_space_id = unsigned(space_id);
|
||||
|
||||
if (!registry::review_actor_params(testcase, params, unsigned(space_id)))
|
||||
failure("Actor config-review failed for space-id %lu\n", space_id);
|
||||
|
||||
last_space_id = unsigned(space_id);
|
||||
log_trace("configure_actor: space %lu for %s", space_id,
|
||||
testcase2str(testcase));
|
||||
global::actors.emplace_back(
|
||||
@ -105,6 +106,10 @@ void testcase_setup(const char *casename, const actor_params ¶ms,
|
||||
configure_actor(last_space_id, ac_try, nullptr, params);
|
||||
configure_actor(last_space_id, ac_jitter, nullptr, params);
|
||||
configure_actor(last_space_id, ac_try, nullptr, params);
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
configure_actor(last_space_id, ac_forkread, nullptr, params);
|
||||
configure_actor(last_space_id, ac_forkwrite, nullptr, params);
|
||||
#endif /* Windows */
|
||||
log_notice("<<< testcase_setup(%s): done", casename);
|
||||
} else {
|
||||
failure("unknown testcase `%s`", casename);
|
||||
|
@ -145,6 +145,16 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(value_cstr, "rnd") == 0 || strcmp(value_cstr, "rand") == 0 ||
|
||||
strcmp(value_cstr, "random") == 0) {
|
||||
value = minval;
|
||||
if (maxval > minval)
|
||||
value += (prng32() + UINT64_C(44263400549519813)) % (maxval - minval);
|
||||
if (scale == intkey)
|
||||
value &= ~3u;
|
||||
return true;
|
||||
}
|
||||
|
||||
char *suffix = nullptr;
|
||||
errno = 0;
|
||||
unsigned long long raw = strtoull(value_cstr, &suffix, 0);
|
||||
@ -159,7 +169,7 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
|
||||
|
||||
uint64_t multiplier = 1;
|
||||
if (suffix && *suffix) {
|
||||
if (scale == no_scale)
|
||||
if (scale == no_scale || scale == intkey)
|
||||
failure("Option '--%s' doesn't accepts suffixes, so '%s' is unexpected\n",
|
||||
option, suffix);
|
||||
if (strcmp(suffix, "K") == 0 || strcasecmp(suffix, "Kilo") == 0)
|
||||
@ -203,6 +213,8 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
|
||||
if (value < minval)
|
||||
failure("The minimal value for option '--%s' is %" PRIu64 "\n", option,
|
||||
minval);
|
||||
if (scale == intkey)
|
||||
value &= ~3u;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -422,6 +434,7 @@ void dump(const char *title) {
|
||||
log_verbose("#%u, testcase %s, space_id/table %u\n", i->actor_id,
|
||||
testcase2str(i->testcase), i->space_id);
|
||||
indent.push();
|
||||
log_verbose("prng-seed: %u\n", i->params.prng_seed);
|
||||
|
||||
if (i->params.loglevel) {
|
||||
log_verbose("log: level %u, %s\n", i->params.loglevel,
|
||||
@ -461,7 +474,6 @@ void dump(const char *title) {
|
||||
i->params.keygen.mesh, i->params.keygen.rotate, i->params.keygen.offset,
|
||||
i->params.keygen.split,
|
||||
i->params.keygen.width - i->params.keygen.split);
|
||||
log_verbose("keygen.seed: %u\n", i->params.keygen.seed);
|
||||
log_verbose("keygen.zerofill: %s\n",
|
||||
i->params.keygen.zero_fill ? "Yes" : "No");
|
||||
log_verbose("key: minlen %u, maxlen %u\n", i->params.keylen_min,
|
||||
@ -681,7 +693,7 @@ bool actor_config::deserialize(const char *str, actor_config &config) {
|
||||
}
|
||||
|
||||
unsigned actor_params::mdbx_keylen_min() const {
|
||||
return (table_flags & MDBX_INTEGERKEY) ? 4 : 0;
|
||||
return unsigned(mdbx_limits_keysize_min(table_flags));
|
||||
}
|
||||
|
||||
unsigned actor_params::mdbx_keylen_max() const {
|
||||
@ -689,7 +701,7 @@ unsigned actor_params::mdbx_keylen_max() const {
|
||||
}
|
||||
|
||||
unsigned actor_params::mdbx_datalen_min() const {
|
||||
return (table_flags & MDBX_INTEGERDUP) ? 4 : 0;
|
||||
return unsigned(mdbx_limits_valsize_min(table_flags));
|
||||
}
|
||||
|
||||
unsigned actor_params::mdbx_datalen_max() const {
|
||||
|
@ -25,6 +25,10 @@ enum actor_testcase {
|
||||
ac_hill,
|
||||
ac_deadread,
|
||||
ac_deadwrite,
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
ac_forkread,
|
||||
ac_forkwrite,
|
||||
#endif /* Windows */
|
||||
ac_jitter,
|
||||
ac_try,
|
||||
ac_copy,
|
||||
@ -59,7 +63,7 @@ const char *keygencase2str(const keygen_case);
|
||||
|
||||
namespace config {
|
||||
|
||||
enum scale_mode { no_scale, decimal, binary, duration };
|
||||
enum scale_mode { no_scale, decimal, binary, duration, intkey };
|
||||
|
||||
bool parse_option(int argc, char *const argv[], int &narg, const char *option,
|
||||
const char **value, const char *default_value = nullptr);
|
||||
@ -270,6 +274,7 @@ struct actor_params_pod {
|
||||
unsigned batch_read{0};
|
||||
unsigned batch_write{0};
|
||||
|
||||
unsigned prng_seed{0};
|
||||
unsigned delaystart{0};
|
||||
unsigned waitfor_nops{0};
|
||||
unsigned inject_writefaultn{0};
|
||||
|
263
test/extra/doubtless_positioning.c++
Normal file
263
test/extra/doubtless_positioning.c++
Normal file
@ -0,0 +1,263 @@
|
||||
#include "mdbx.h++"
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <unistd.h>
|
||||
|
||||
static ::std::ostream &operator<<(::std::ostream &out,
|
||||
const mdbx::cursor::move_operation op) {
|
||||
static const char *const str[] = {"FIRST",
|
||||
"FIRST_DUP",
|
||||
"GET_BOTH",
|
||||
"GET_BOTH_RANGE",
|
||||
"GET_CURRENT",
|
||||
"GET_MULTIPLE",
|
||||
"LAST",
|
||||
"LAST_DUP",
|
||||
"NEXT",
|
||||
"NEXT_DUP",
|
||||
"NEXT_MULTIPLE",
|
||||
"NEXT_NODUP",
|
||||
"PREV",
|
||||
"PREV_DUP",
|
||||
"PREV_NODUP",
|
||||
"SET",
|
||||
"SET_KEY",
|
||||
"SET_RANGE",
|
||||
"PREV_MULTIPLE",
|
||||
"SET_LOWERBOUND",
|
||||
"SET_UPPERBOUND",
|
||||
"TO_KEY_LESSER_THAN",
|
||||
"TO_KEY_LESSER_OR_EQUAL",
|
||||
"TO_KEY_EQUAL",
|
||||
"TO_KEY_GREATER_OR_EQUAL",
|
||||
"TO_KEY_GREATER_THAN",
|
||||
"TO_EXACT_KEY_VALUE_LESSER_THAN",
|
||||
"TO_EXACT_KEY_VALUE_LESSER_OR_EQUAL",
|
||||
"TO_EXACT_KEY_VALUE_EQUAL",
|
||||
"TO_EXACT_KEY_VALUE_GREATER_OR_EQUAL",
|
||||
"TO_EXACT_KEY_VALUE_GREATER_THAN",
|
||||
"TO_PAIR_LESSER_THAN",
|
||||
"TO_PAIR_LESSER_OR_EQUAL",
|
||||
"TO_PAIR_EQUAL",
|
||||
"TO_PAIR_GREATER_OR_EQUAL",
|
||||
"TO_PAIR_GREATER_THAN"};
|
||||
return out << str[op];
|
||||
}
|
||||
|
||||
using buffer = mdbx::default_buffer;
|
||||
using buffer_pair = mdbx::buffer_pair<buffer>;
|
||||
|
||||
std::default_random_engine prng(42);
|
||||
|
||||
static buffer random(const unsigned &value) {
|
||||
switch (prng() % 3) {
|
||||
default:
|
||||
return buffer::hex(value);
|
||||
case 1:
|
||||
return buffer::base64(value);
|
||||
case 2:
|
||||
return buffer::base58(value);
|
||||
}
|
||||
}
|
||||
|
||||
static buffer random_key() { return random(prng() % 10007); }
|
||||
|
||||
static buffer random_value() { return random(prng() % 47); }
|
||||
|
||||
using predicate = std::function<bool(const mdbx::pair &, const mdbx::pair &)>;
|
||||
|
||||
static bool probe(mdbx::txn txn, mdbx::map_handle dbi,
|
||||
mdbx::cursor::move_operation op, predicate cmp,
|
||||
const buffer_pair &pair) {
|
||||
auto seeker = txn.open_cursor(dbi);
|
||||
auto scanner = seeker.clone();
|
||||
|
||||
const bool scan_backward =
|
||||
op == mdbx::cursor::key_lesser_than ||
|
||||
op == mdbx::cursor::key_lesser_or_equal ||
|
||||
op == mdbx::cursor::multi_exactkey_value_lesser_than ||
|
||||
op == mdbx::cursor::multi_exactkey_value_lesser_or_equal ||
|
||||
op == mdbx::cursor::pair_lesser_than ||
|
||||
op == mdbx::cursor::pair_lesser_or_equal;
|
||||
|
||||
const bool is_multi = mdbx::is_multi(txn.get_handle_info(dbi).value_mode());
|
||||
|
||||
auto seek_result = seeker.move(op, pair.key, pair.value, false);
|
||||
auto scan_result = scanner.fullscan(
|
||||
[cmp, &pair](const mdbx::pair &scan) -> bool { return cmp(scan, pair); },
|
||||
scan_backward);
|
||||
if (seek_result.done == scan_result &&
|
||||
(!scan_result ||
|
||||
seeker.is_same_position(
|
||||
scanner,
|
||||
op < mdbx::cursor::multi_exactkey_value_lesser_than && is_multi)))
|
||||
return true;
|
||||
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "bug:";
|
||||
std::cerr << std::endl;
|
||||
std::cerr << std::string(is_multi ? "multi" : "single") << "-map, op " << op
|
||||
<< ", key " << pair.key << ", value " << pair.value;
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "\tscanner: ";
|
||||
if (scan_result)
|
||||
std::cerr << " done, key " << scanner.current(false).key << ", value "
|
||||
<< scanner.current(false).value;
|
||||
else
|
||||
std::cerr << "not-found";
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "\t seeker: " << (seek_result.done ? " done" : "not-found")
|
||||
<< ", key " << seek_result.key << ", value " << seek_result.value;
|
||||
std::cerr << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool probe(mdbx::txn txn, mdbx::map_handle dbi,
|
||||
mdbx::cursor::move_operation op, predicate cmp) {
|
||||
const auto pair = buffer_pair(random_key(), random_value());
|
||||
const bool ok = probe(txn, dbi, op, cmp, pair);
|
||||
#if MDBX_DEBUG
|
||||
if (!ok)
|
||||
// повтор для отладки и поиска причин
|
||||
probe(txn, dbi, op, cmp, pair);
|
||||
#endif /* MDBX_DEBUG */
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool test(mdbx::txn txn, mdbx::map_handle dbi) {
|
||||
bool ok = true;
|
||||
|
||||
ok = probe(txn, dbi, mdbx::cursor::key_lesser_than,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return mdbx_cmp(txn, dbi, l.key, r.key) < 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::key_lesser_or_equal,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return mdbx_cmp(txn, dbi, l.key, r.key) <= 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::key_equal,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return mdbx_cmp(txn, dbi, l.key, r.key) == 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::key_greater_or_equal,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return mdbx_cmp(txn, dbi, l.key, r.key) >= 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::key_greater_than,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return mdbx_cmp(txn, dbi, l.key, r.key) > 0;
|
||||
}) &&
|
||||
ok;
|
||||
|
||||
ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_lesser_than,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return mdbx_cmp(txn, dbi, l.key, r.key) == 0 &&
|
||||
mdbx_dcmp(txn, dbi, l.value, r.value) < 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_lesser_or_equal,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return mdbx_cmp(txn, dbi, l.key, r.key) == 0 &&
|
||||
mdbx_dcmp(txn, dbi, l.value, r.value) <= 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_equal,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return mdbx_cmp(txn, dbi, l.key, r.key) == 0 &&
|
||||
mdbx_dcmp(txn, dbi, l.value, r.value) == 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_greater_or_equal,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return mdbx_cmp(txn, dbi, l.key, r.key) == 0 &&
|
||||
mdbx_dcmp(txn, dbi, l.value, r.value) >= 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::multi_exactkey_value_greater,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return mdbx_cmp(txn, dbi, l.key, r.key) == 0 &&
|
||||
mdbx_dcmp(txn, dbi, l.value, r.value) > 0;
|
||||
}) &&
|
||||
ok;
|
||||
|
||||
ok = probe(txn, dbi, mdbx::cursor::pair_lesser_than,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
auto cmp = mdbx_cmp(txn, dbi, l.key, r.key);
|
||||
if (cmp == 0)
|
||||
cmp = mdbx_dcmp(txn, dbi, l.value, r.value);
|
||||
return cmp < 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::pair_lesser_or_equal,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
auto cmp = mdbx_cmp(txn, dbi, l.key, r.key);
|
||||
if (cmp == 0)
|
||||
cmp = mdbx_dcmp(txn, dbi, l.value, r.value);
|
||||
return cmp <= 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::pair_equal,
|
||||
[](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
return l == r;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::pair_greater_or_equal,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
auto cmp = mdbx_cmp(txn, dbi, l.key, r.key);
|
||||
if (cmp == 0)
|
||||
cmp = mdbx_dcmp(txn, dbi, l.value, r.value);
|
||||
return cmp >= 0;
|
||||
}) &&
|
||||
ok;
|
||||
ok = probe(txn, dbi, mdbx::cursor::pair_greater_than,
|
||||
[txn, dbi](const mdbx::pair &l, const mdbx::pair &r) -> bool {
|
||||
auto cmp = mdbx_cmp(txn, dbi, l.key, r.key);
|
||||
if (cmp == 0)
|
||||
cmp = mdbx_dcmp(txn, dbi, l.value, r.value);
|
||||
return cmp > 0;
|
||||
}) &&
|
||||
ok;
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
unlink("." MDBX_DATANAME);
|
||||
unlink("." MDBX_LOCKNAME);
|
||||
mdbx::env_managed env(".", mdbx::env_managed::create_parameters(),
|
||||
mdbx::env::operate_parameters(3));
|
||||
|
||||
auto txn = env.start_write();
|
||||
auto single =
|
||||
txn.create_map("single", mdbx::key_mode::usual, mdbx::value_mode::single);
|
||||
auto multi =
|
||||
txn.create_map("multi", mdbx::key_mode::usual, mdbx::value_mode::multi);
|
||||
for (size_t i = 0; i < 1000; ++i) {
|
||||
auto key = random_key();
|
||||
txn.upsert(single, key, random_value());
|
||||
for (auto n = prng() % 5 + 1; n > 0; --n)
|
||||
txn.upsert(multi, key, random_value());
|
||||
}
|
||||
txn.commit_embark_read();
|
||||
|
||||
bool ok = true;
|
||||
for (size_t i = 0; ok && i < 3333; ++i) {
|
||||
ok = test(txn, single) && ok;
|
||||
ok = test(txn, multi) && ok;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
std::cerr << "Fail\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::cout << "OK\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
128
test/extra/hex_base64_base58.c++
Normal file
128
test/extra/hex_base64_base58.c++
Normal file
@ -0,0 +1,128 @@
|
||||
#include "mdbx.h++"
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <functional>
|
||||
#include <random>
|
||||
|
||||
using buffer = mdbx::default_buffer;
|
||||
|
||||
std::default_random_engine prng(42);
|
||||
|
||||
static buffer random(size_t length) {
|
||||
buffer result(length);
|
||||
#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L
|
||||
for (auto &i : result.bytes())
|
||||
i = prng();
|
||||
#else
|
||||
for (auto p = result.byte_ptr(); p < result.end_byte_ptr(); ++p)
|
||||
*p = mdbx::byte(prng());
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool basic() {
|
||||
bool ok = true;
|
||||
const char *const hex_dump = "1D58fa\n2e46E3\nBd9c7A\nC0bF";
|
||||
const uint8_t native[] = {0x1D, 0x58, 0xfa, 0x2e, 0x46, 0xE3,
|
||||
0xBd, 0x9c, 0x7A, 0xC0, 0xbF};
|
||||
|
||||
if (mdbx::slice(hex_dump).hex_decode(true) != mdbx::slice::wrap(native))
|
||||
std::cerr << "hex_decode() failed\n";
|
||||
else if (mdbx::slice::wrap(native).encode_hex(true, 4).hex_decode(true) !=
|
||||
mdbx::slice::wrap(native))
|
||||
std::cerr << "hex_encode(UPPERCASE) failed\n";
|
||||
else if (mdbx::slice::wrap(native).encode_hex(false).hex_decode(true) !=
|
||||
mdbx::slice::wrap(native))
|
||||
std::cerr << "hex_encode(lowercase) failed\n";
|
||||
|
||||
if (mdbx::slice("").as_base64_string() != "" ||
|
||||
mdbx::slice(" ").encode_base64().as_string() != "IA==" ||
|
||||
mdbx::slice("~0").encode_base64().as_string() != "fjA=" ||
|
||||
mdbx::slice("A_z").encode_base64().as_string() != "QV96" ||
|
||||
mdbx::slice("Ka9q").encode_base64().as_string() != "S2E5cQ==" ||
|
||||
mdbx::slice("123456789").encode_base64().as_string() != "MTIzNDU2Nzg5") {
|
||||
std::cerr << "encode_base64() failed\n";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
const uint8_t base58_rfc[] = {0x00, 0x00, 0x28, 0x7f, 0xb4, 0xcd};
|
||||
if (mdbx::slice("").as_base58_string() != "" ||
|
||||
mdbx::slice(" ").encode_base58().as_string() != "Z" ||
|
||||
mdbx::slice("Hello World!").as_base58_string() != "2NEpo7TZRRrLZSi2U" ||
|
||||
mdbx::slice("The quick brown fox jumps over the lazy dog.")
|
||||
.encode_base58()
|
||||
.as_string() !=
|
||||
"USm3fpXnKG5EUBx2ndxBDMPVciP5hGey2Jh4NDv6gmeo1LkMeiKrLJUUBk6Z" ||
|
||||
mdbx::slice::wrap(base58_rfc).as_base58_string() != "11233QC4" ||
|
||||
mdbx::slice("~0").encode_base58().as_string() != "Aby" ||
|
||||
mdbx::slice("A_z").encode_base58().as_string() != "NxZw" ||
|
||||
mdbx::slice("Ka9q").encode_base58().as_string() != "2vkjDi" ||
|
||||
mdbx::slice("123456789").encode_base58().as_string() != "dKYWwnRHc7Ck") {
|
||||
std::cerr << "encode_base58() failed\n";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (mdbx::slice("").base58_decode() != mdbx::slice() ||
|
||||
mdbx::slice("Z").base58_decode() != mdbx::slice(" ") ||
|
||||
mdbx::slice("2NEpo7TZRRrLZSi2U").base58_decode() != "Hello World!" ||
|
||||
mdbx::slice(
|
||||
"USm3fpXnKG5EUBx2ndxBDMPVciP5hGey2Jh4NDv6gmeo1LkMeiKrLJUUBk6Z")
|
||||
.base58_decode() !=
|
||||
mdbx::slice("The quick brown fox jumps over the lazy dog.") ||
|
||||
mdbx::slice("11233QC4").base58_decode() !=
|
||||
mdbx::slice::wrap(base58_rfc) ||
|
||||
mdbx::slice("Aby").base58_decode() != mdbx::slice("~0") ||
|
||||
mdbx::slice("NxZw").base58_decode() != mdbx::slice("A_z") ||
|
||||
mdbx::slice("2vkjDi").base58_decode() != mdbx::slice("Ka9q") ||
|
||||
mdbx::slice("dKYWwnRHc7Ck").base58_decode() != mdbx::slice("123456789")) {
|
||||
std::cerr << "decode_base58() failed\n";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
auto ok = basic();
|
||||
for (size_t n = 0; n < 1000; ++n) {
|
||||
for (size_t length = 0; ok && length < 111; ++length) {
|
||||
const auto pattern = random(length);
|
||||
if (pattern != pattern.encode_hex(bool(prng() & 1), prng() % 111)
|
||||
.hex_decode(true)
|
||||
.encode_hex()
|
||||
.hex_decode(false)) {
|
||||
std::cerr << "hex encode/decode failed: n " << n << ", length "
|
||||
<< length << std::endl;
|
||||
ok = false;
|
||||
}
|
||||
if (pattern != pattern.encode_base64(unsigned(prng() % 111))
|
||||
.base64_decode(true)
|
||||
.encode_base64()
|
||||
.base64_decode(false)) {
|
||||
std::cerr << "base64 encode/decode failed: n " << n << ", length "
|
||||
<< length << std::endl;
|
||||
ok = false;
|
||||
}
|
||||
if (pattern != pattern.encode_base58(unsigned(prng() % 111))
|
||||
.base58_decode(true)
|
||||
.encode_base58()
|
||||
.base58_decode(false)) {
|
||||
std::cerr << "base58 encode/decode failed: n " << n << ", length "
|
||||
<< length << std::endl;
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
std::cerr << "Fail\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::cout << "OK\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -34,7 +34,7 @@
|
||||
|
||||
#define IP_PRINTF_ARG_HOST(addr) \
|
||||
(int)((addr) >> 24), (int)((addr) >> 16 & 0xff), (int)((addr) >> 8 & 0xff), \
|
||||
(int)((addr)&0xff)
|
||||
(int)((addr) & 0xff)
|
||||
|
||||
char opt_db_path[PATH_MAX] = "./mdbx_bench2";
|
||||
static MDBX_env *env;
|
||||
|
297
test/fork.c++
Normal file
297
test/fork.c++
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright 2023 Leonid Yuriev <leo@yuriev.ru>
|
||||
* and other libmdbx authors: please see AUTHORS file.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#include "test.h++"
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
class testcase_smoke4fork : public testcase {
|
||||
using inherited = testcase;
|
||||
|
||||
protected:
|
||||
bool dbi_invalid{true};
|
||||
bool dbi_stable{false};
|
||||
unsigned dbi_state{0};
|
||||
|
||||
public:
|
||||
testcase_smoke4fork(const actor_config &config, const mdbx_pid_t pid)
|
||||
: testcase(config, pid) {}
|
||||
virtual void txn_end(bool abort) override;
|
||||
bool run() override;
|
||||
virtual bool smoke() = 0;
|
||||
bool open_dbi();
|
||||
};
|
||||
|
||||
bool testcase_smoke4fork::open_dbi() {
|
||||
if (!dbi || dbi_invalid) {
|
||||
if (dbi_stable ||
|
||||
(mdbx_txn_flags(txn_guard.get()) & int(MDBX_TXN_RDONLY)) == 0) {
|
||||
dbi = db_table_open(!dbi_stable);
|
||||
dbi_invalid = false;
|
||||
}
|
||||
}
|
||||
|
||||
dbi_state = 0;
|
||||
if (dbi && !dbi_invalid) {
|
||||
unsigned unused_dbi_flags;
|
||||
int err =
|
||||
mdbx_dbi_flags_ex(txn_guard.get(), dbi, &unused_dbi_flags, &dbi_state);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure_perror("mdbx_dbi_flags_ex()", err);
|
||||
if ((dbi_state & (MDBX_DBI_CREAT | MDBX_DBI_FRESH)) == 0)
|
||||
dbi_stable = true;
|
||||
}
|
||||
return !dbi_invalid;
|
||||
}
|
||||
|
||||
void testcase_smoke4fork::txn_end(bool abort) {
|
||||
if (dbi) {
|
||||
if (abort) {
|
||||
if (dbi_state & MDBX_DBI_CREAT)
|
||||
dbi_stable = false;
|
||||
if (dbi_state & MDBX_DBI_FRESH)
|
||||
dbi_invalid = true;
|
||||
} else {
|
||||
if (dbi_state & (MDBX_DBI_CREAT | MDBX_DBI_FRESH))
|
||||
dbi_stable = true;
|
||||
}
|
||||
dbi_state = 0;
|
||||
}
|
||||
inherited::txn_end(abort);
|
||||
}
|
||||
|
||||
bool testcase_smoke4fork::run() {
|
||||
static std::vector<pid_t> history;
|
||||
const pid_t current_pid = getpid();
|
||||
if (history.empty() || current_pid != history.front()) {
|
||||
history.push_back(current_pid);
|
||||
if (history.size() > /* TODO: add test option */ 2) {
|
||||
log_notice("force exit to avoid fork-bomb: deep %zu, pid stack",
|
||||
history.size());
|
||||
for (const auto pid : history)
|
||||
logging::feed(" %d", pid);
|
||||
logging::ln();
|
||||
log_flush();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
const int deep = (int)history.size();
|
||||
|
||||
int err = db_open__begin__table_create_open_clean(dbi);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
log_notice("fork[deep %d, pid %d]: bailout-prepare due '%s'", deep,
|
||||
current_pid, mdbx_strerror(err));
|
||||
return false;
|
||||
}
|
||||
open_dbi();
|
||||
|
||||
if (flipcoin()) {
|
||||
if (!smoke()) {
|
||||
log_notice("%s[deep %d, pid %d] probe %s", "pre-fork", deep, current_pid,
|
||||
"failed");
|
||||
return false;
|
||||
}
|
||||
log_verbose("%s[deep %d, pid %d] probe %s", "pre-fork", deep, current_pid,
|
||||
"done");
|
||||
} else {
|
||||
log_verbose("%s[deep %d, pid %d] probe %s", "pre-fork", deep, current_pid,
|
||||
"skipped");
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
const bool commit_txn_to_avoid_memleak = true;
|
||||
#else
|
||||
const bool commit_txn_to_avoid_memleak = !RUNNING_ON_VALGRIND && flipcoin();
|
||||
#endif
|
||||
if (commit_txn_to_avoid_memleak && txn_guard)
|
||||
txn_end(false);
|
||||
}
|
||||
|
||||
log_flush();
|
||||
const pid_t child = fork();
|
||||
if (child < 0)
|
||||
failure_perror("fork()", errno);
|
||||
|
||||
if (child == 0) {
|
||||
const pid_t new_pid = getpid();
|
||||
log_verbose(">>> %s, deep %d, parent-pid %d, child-pid %d",
|
||||
"mdbx_env_resurrect_after_fork()", deep, current_pid, new_pid);
|
||||
log_flush();
|
||||
int err = mdbx_env_resurrect_after_fork(db_guard.get());
|
||||
log_verbose("<<< %s, deep %d, parent-pid %d, child-pid %d, err %d",
|
||||
"mdbx_env_resurrect_after_fork()", deep, current_pid, new_pid,
|
||||
err);
|
||||
log_flush();
|
||||
if (err != MDBX_SUCCESS)
|
||||
failure_perror("mdbx_env_resurrect_after_fork()", err);
|
||||
if (txn_guard) {
|
||||
if (dbi_state & MDBX_DBI_CREAT)
|
||||
dbi_invalid = true;
|
||||
// if (dbi_state & MDBX_DBI_FRESH)
|
||||
// dbi_invalid = true;
|
||||
dbi_state = 0;
|
||||
mdbx_txn_abort(txn_guard.release());
|
||||
}
|
||||
if (!smoke()) {
|
||||
log_notice("%s[deep %d, pid %d] probe %s", "fork-child", deep, new_pid,
|
||||
"failed");
|
||||
return false;
|
||||
}
|
||||
log_verbose("%s[deep %d, pid %d] probe %s", "fork-child", deep, new_pid,
|
||||
"done");
|
||||
log_flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (txn_guard)
|
||||
txn_end(false);
|
||||
|
||||
int status = 0xdeadbeef;
|
||||
if (waitpid(child, &status, 0) != child)
|
||||
failure_perror("waitpid()", errno);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
const int code = WEXITSTATUS(status);
|
||||
if (code != EXIT_SUCCESS) {
|
||||
log_notice("%s[deep %d, pid %d] child-pid %d failed, err %d",
|
||||
"fork-child", deep, current_pid, child, code);
|
||||
return false;
|
||||
}
|
||||
log_notice("%s[deep %d, pid %d] child-pid %d done", "fork-child", deep,
|
||||
current_pid, child);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
const int sig = WTERMSIG(status);
|
||||
switch (sig) {
|
||||
case SIGABRT:
|
||||
case SIGBUS:
|
||||
case SIGFPE:
|
||||
case SIGILL:
|
||||
case SIGSEGV:
|
||||
log_notice("%s[deep %d, pid %d] child-pid %d %s by SIG%s", "fork-child",
|
||||
deep, current_pid, child, "terminated", signal_name(sig));
|
||||
break;
|
||||
default:
|
||||
log_notice("%s[deep %d, pid %d] child-id %d %s by SIG%s", "fork-child",
|
||||
deep, current_pid, child, "killed", signal_name(sig));
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (!smoke()) {
|
||||
log_notice("%s[deep %d, pid %d] probe %s", "post-fork", deep, current_pid,
|
||||
"failed");
|
||||
return false;
|
||||
}
|
||||
log_verbose("%s[deep %d, pid %d] probe %s", "post-fork", deep, current_pid,
|
||||
"done");
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class testcase_forkread : public testcase_smoke4fork {
|
||||
using inherited = testcase_smoke4fork;
|
||||
|
||||
public:
|
||||
testcase_forkread(const actor_config &config, const mdbx_pid_t pid)
|
||||
: testcase_smoke4fork(config, pid) {}
|
||||
bool smoke() override;
|
||||
};
|
||||
REGISTER_TESTCASE(forkread);
|
||||
|
||||
bool testcase_forkread::smoke() {
|
||||
MDBX_envinfo env_info;
|
||||
int err = mdbx_env_info_ex(db_guard.get(), txn_guard.get(), &env_info,
|
||||
sizeof(env_info));
|
||||
if (err)
|
||||
failure_perror("mdbx_env_info_ex()", err);
|
||||
|
||||
if (!txn_guard)
|
||||
txn_begin(true);
|
||||
|
||||
MDBX_txn_info txn_info;
|
||||
err = mdbx_txn_info(txn_guard.get(), &txn_info, sizeof(txn_info));
|
||||
if (err)
|
||||
failure_perror("mdbx_txn_info()", err);
|
||||
fetch_canary();
|
||||
err = mdbx_env_info_ex(db_guard.get(), txn_guard.get(), &env_info,
|
||||
sizeof(env_info));
|
||||
if (err)
|
||||
failure_perror("mdbx_env_info_ex()", err);
|
||||
|
||||
uint64_t seq;
|
||||
if (dbi_invalid) {
|
||||
err = mdbx_dbi_sequence(txn_guard.get(), dbi, &seq, 0);
|
||||
if (unlikely(err != (dbi ? MDBX_BAD_DBI : MDBX_SUCCESS)))
|
||||
failure("unexpected '%s' from mdbx_dbi_sequence(get, bad_dbi %d)",
|
||||
mdbx_strerror(err), dbi);
|
||||
open_dbi();
|
||||
}
|
||||
if (!dbi_invalid) {
|
||||
err = mdbx_dbi_sequence(txn_guard.get(), dbi, &seq, 0);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure("unexpected '%s' from mdbx_dbi_sequence(get, dbi %d)",
|
||||
mdbx_strerror(err), dbi);
|
||||
}
|
||||
txn_end(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class testcase_forkwrite : public testcase_forkread {
|
||||
using inherited = testcase_forkread;
|
||||
|
||||
public:
|
||||
testcase_forkwrite(const actor_config &config, const mdbx_pid_t pid)
|
||||
: testcase_forkread(config, pid) {}
|
||||
bool smoke() override;
|
||||
};
|
||||
REGISTER_TESTCASE(forkwrite);
|
||||
|
||||
bool testcase_forkwrite::smoke() {
|
||||
const bool firstly_read = flipcoin();
|
||||
if (firstly_read) {
|
||||
if (!testcase_forkread::smoke())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!txn_guard)
|
||||
txn_begin(false);
|
||||
|
||||
uint64_t seq;
|
||||
if (dbi_invalid) {
|
||||
int err = mdbx_dbi_sequence(txn_guard.get(), dbi, &seq, 0);
|
||||
if (unlikely(err != (dbi ? MDBX_BAD_DBI : MDBX_EACCESS)))
|
||||
failure("unexpected '%s' from mdbx_dbi_sequence(get, bad_dbi %d)",
|
||||
mdbx_strerror(err), dbi);
|
||||
open_dbi();
|
||||
}
|
||||
if (!dbi_invalid) {
|
||||
int err = mdbx_dbi_sequence(txn_guard.get(), dbi, &seq, 1);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure("unexpected '%s' from mdbx_dbi_sequence(inc, dbi %d)",
|
||||
mdbx_strerror(err), dbi);
|
||||
}
|
||||
txn_end(false);
|
||||
|
||||
if (!firstly_read && !testcase_forkread::smoke())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* Windows */
|
@ -52,7 +52,7 @@ bool testcase_hill::run() {
|
||||
speculum_committed.clear();
|
||||
|
||||
/* TODO: работа в несколько потоков */
|
||||
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
|
||||
keyvalue_maker.setup(config.params, 0 /* thread_number */);
|
||||
|
||||
keygen::buffer a_key = keygen::alloc(config.params.keylen_max);
|
||||
keygen::buffer a_data_0 = keygen::alloc(config.params.datalen_max);
|
||||
@ -90,7 +90,7 @@ bool testcase_hill::run() {
|
||||
assert(b_serial > a_serial);
|
||||
|
||||
// создаем первую запись из пары
|
||||
const keygen::serial_t age_shift = UINT64_C(1) << (a_serial % 31);
|
||||
const keygen::serial_t age_shift = keyvalue_maker.remix_age(a_serial);
|
||||
log_trace("uphill: insert-a (age %" PRIu64 ") %" PRIu64, age_shift,
|
||||
a_serial);
|
||||
generate_pair(a_serial, a_key, a_data_1, age_shift);
|
||||
@ -302,7 +302,7 @@ bool testcase_hill::run() {
|
||||
assert(b_serial > a_serial);
|
||||
|
||||
// обновляем первую запись из пары
|
||||
const keygen::serial_t age_shift = UINT64_C(1) << (a_serial % 31);
|
||||
const keygen::serial_t age_shift = keyvalue_maker.remix_age(a_serial);
|
||||
log_trace("downhill: update-a (age 0->%" PRIu64 ") %" PRIu64, age_shift,
|
||||
a_serial);
|
||||
generate_pair(a_serial, a_key, a_data_0, 0);
|
||||
|
@ -39,6 +39,12 @@ bool testcase_jitter::run() {
|
||||
if (upper_limit < 1)
|
||||
upper_limit = config.params.size_now * 2;
|
||||
|
||||
tablename_buf buffer;
|
||||
const char *const tablename = db_tablename(buffer);
|
||||
tablename_buf buffer_renamed;
|
||||
const char *const tablename_renamed =
|
||||
db_tablename(buffer_renamed, ".renamed");
|
||||
|
||||
while (should_continue()) {
|
||||
jitter_delay();
|
||||
db_open();
|
||||
@ -48,6 +54,15 @@ bool testcase_jitter::run() {
|
||||
txn_begin(false);
|
||||
dbi = db_table_open(true);
|
||||
check_dbi_error(MDBX_SUCCESS, "created-uncommitted");
|
||||
|
||||
bool renamed = false;
|
||||
if (flipcoin()) {
|
||||
err = mdbx_dbi_rename(txn_guard.get(), dbi, tablename_renamed);
|
||||
if (err != MDBX_SUCCESS)
|
||||
failure_perror("jitter.rename-1", err);
|
||||
renamed = true;
|
||||
}
|
||||
|
||||
// note: here and below the 4-byte length keys and value are used
|
||||
// to be compatible with any Db-flags given from command line.
|
||||
MDBX_val k = {(void *)"k000", 4}, v = {(void *)"v001", 4};
|
||||
@ -75,7 +90,17 @@ bool testcase_jitter::run() {
|
||||
failure_perror("jitter.put-2", err);
|
||||
check_dbi_error(MDBX_BAD_DBI, "dropped-recreated-aborted");
|
||||
// restore DBI
|
||||
dbi = db_table_open(false);
|
||||
dbi = db_table_open(false, renamed);
|
||||
if (renamed) {
|
||||
err = mdbx_dbi_open(
|
||||
txn_guard.get(), tablename_renamed,
|
||||
flipcoin() ? MDBX_DB_ACCEDE : config.params.table_flags, &dbi);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure_perror("open-renamed", err);
|
||||
err = mdbx_dbi_rename(txn_guard.get(), dbi, tablename);
|
||||
if (err != MDBX_SUCCESS)
|
||||
failure_perror("jitter.rename-2", err);
|
||||
}
|
||||
check_dbi_error(MDBX_SUCCESS, "dropped-recreated-aborted+reopened");
|
||||
v = {(void *)"v003", 4};
|
||||
err = mdbx_put(txn_guard.get(), dbi, &k, &v, MDBX_UPSERT);
|
||||
|
135
test/keygen.c++
135
test/keygen.c++
@ -14,6 +14,39 @@
|
||||
|
||||
#include "test.h++"
|
||||
|
||||
static const uint64_t primes[64] = {
|
||||
/* */
|
||||
0, 1, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381,
|
||||
/* */
|
||||
UINT64_C(32749), UINT64_C(65521), UINT64_C(131071), UINT64_C(262139),
|
||||
UINT64_C(524287), UINT64_C(1048573), UINT64_C(2097143), UINT64_C(4194301),
|
||||
UINT64_C(8388593), UINT64_C(16777213), UINT64_C(33554393),
|
||||
UINT64_C(67108859), UINT64_C(134217689), UINT64_C(268435399),
|
||||
UINT64_C(536870909), UINT64_C(1073741789), UINT64_C(2147483647),
|
||||
UINT64_C(4294967291), UINT64_C(8589934583), UINT64_C(17179869143),
|
||||
UINT64_C(34359738337), UINT64_C(68719476731), UINT64_C(137438953447),
|
||||
UINT64_C(274877906899), UINT64_C(549755813881), UINT64_C(1099511627689),
|
||||
UINT64_C(2199023255531), UINT64_C(4398046511093), UINT64_C(8796093022151),
|
||||
UINT64_C(17592186044399), UINT64_C(35184372088777),
|
||||
UINT64_C(70368744177643), UINT64_C(140737488355213),
|
||||
UINT64_C(281474976710597), UINT64_C(562949953421231),
|
||||
UINT64_C(1125899906842597), UINT64_C(2251799813685119),
|
||||
UINT64_C(4503599627370449), UINT64_C(9007199254740881),
|
||||
UINT64_C(18014398509481951), UINT64_C(36028797018963913),
|
||||
UINT64_C(72057594037927931), UINT64_C(144115188075855859),
|
||||
UINT64_C(288230376151711717), UINT64_C(576460752303423433),
|
||||
UINT64_C(1152921504606846883), UINT64_C(2305843009213693951),
|
||||
UINT64_C(4611686018427387847), UINT64_C(9223372036854775783)};
|
||||
|
||||
/* static unsigned supid_log2(uint64_t v) {
|
||||
unsigned r = 0;
|
||||
while (v > 1) {
|
||||
v >>= 1;
|
||||
r += 1;
|
||||
}
|
||||
return r;
|
||||
} */
|
||||
|
||||
namespace keygen {
|
||||
|
||||
/* LY: https://en.wikipedia.org/wiki/Injective_function */
|
||||
@ -48,19 +81,19 @@ serial_t injective(const serial_t serial,
|
||||
10, 14, 22, 19, 3, 21, 18, 19, 26, 24, 2, 21, 25, 29, 24, 10, 11, 14, 20,
|
||||
19};
|
||||
|
||||
const auto mask = actor_params::serial_mask(bits);
|
||||
const auto mult = m[bits - 8];
|
||||
const auto shift = s[bits - 8];
|
||||
serial_t result = serial * mult;
|
||||
if (salt) {
|
||||
const unsigned left = bits / 2;
|
||||
const unsigned right = bits - left;
|
||||
result = (result << left) |
|
||||
((result & actor_params::serial_mask(bits)) >> right);
|
||||
result = (result << left) | ((result & mask) >> right);
|
||||
result = (result ^ salt) * mult;
|
||||
}
|
||||
|
||||
result ^= result << shift;
|
||||
result &= actor_params::serial_mask(bits);
|
||||
result ^= (result & mask) >> shift;
|
||||
result &= mask;
|
||||
log_trace("keygen-injective: serial %" PRIu64 "/%u @%" PRIx64 ",%u,%" PRIu64
|
||||
" => %" PRIu64 "/%u",
|
||||
serial, bits, mult, shift, salt, result, bits);
|
||||
@ -79,7 +112,7 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
|
||||
unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT))));
|
||||
assert(!(value_essentials.flags &
|
||||
~(essentials::prng_fill_flag |
|
||||
unsigned(MDBX_INTEGERDUP | MDBX_REVERSEDUP))));
|
||||
unsigned(MDBX_INTEGERDUP | MDBX_REVERSEDUP | MDBX_DUPFIXED))));
|
||||
|
||||
log_trace("keygen-pair: serial %" PRIu64 ", data-age %" PRIu64, serial,
|
||||
value_age);
|
||||
@ -111,7 +144,7 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
|
||||
}
|
||||
|
||||
serial_t key_serial = serial;
|
||||
serial_t value_serial = value_age << mapping.split;
|
||||
serial_t value_serial = (value_age & value_age_mask) << mapping.split;
|
||||
if (mapping.split) {
|
||||
if (MDBX_db_flags_t(key_essentials.flags) & MDBX_DUPSORT) {
|
||||
key_serial >>= mapping.split;
|
||||
@ -126,15 +159,14 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
|
||||
actor_params::serial_mask(mapping.split);
|
||||
}
|
||||
|
||||
value_serial |= value_age << mapping.split;
|
||||
log_trace("keygen-pair: split@%u => k%" PRIu64 ", v%" PRIu64, mapping.split,
|
||||
key_serial, value_serial);
|
||||
}
|
||||
|
||||
log_trace("keygen-pair: key %" PRIu64 ", value %" PRIu64, key_serial,
|
||||
value_serial);
|
||||
mk_begin(key_serial, key_essentials, *key);
|
||||
mk_begin(value_serial, value_essentials, *value);
|
||||
key_serial = mk_begin(key_serial, key_essentials, *key);
|
||||
value_serial = mk_begin(value_serial, value_essentials, *value);
|
||||
|
||||
#if 0 /* unused for now */
|
||||
if (key->value.iov_len + value->value.iov_len > pair_maxlen) {
|
||||
@ -189,47 +221,82 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
|
||||
log_pair(logging::trace, "kv", key, value);
|
||||
}
|
||||
|
||||
void maker::setup(const config::actor_params_pod &actor, unsigned actor_id,
|
||||
void maker::setup(const config::actor_params_pod &actor,
|
||||
unsigned thread_number) {
|
||||
#if CONSTEXPR_ENUM_FLAGS_OPERATIONS
|
||||
static_assert(unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT |
|
||||
MDBX_INTEGERDUP | MDBX_REVERSEDUP) < UINT16_MAX,
|
||||
MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP) <
|
||||
UINT16_MAX,
|
||||
"WTF?");
|
||||
#else
|
||||
assert(unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT |
|
||||
MDBX_INTEGERDUP | MDBX_REVERSEDUP) < UINT16_MAX);
|
||||
MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP) <
|
||||
UINT16_MAX);
|
||||
#endif
|
||||
|
||||
key_essentials.flags = uint16_t(
|
||||
actor.table_flags &
|
||||
MDBX_db_flags_t(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT));
|
||||
assert(actor.keylen_min <= UINT16_MAX);
|
||||
key_essentials.minlen = uint16_t(actor.keylen_min);
|
||||
assert(actor.keylen_max <= UINT32_MAX);
|
||||
key_essentials.maxlen =
|
||||
std::min(uint32_t(actor.keylen_max),
|
||||
uint32_t(mdbx_limits_keysize_max(
|
||||
actor.pagesize, MDBX_db_flags_t(key_essentials.flags))));
|
||||
key_essentials.maxlen = std::min(
|
||||
uint32_t(actor.keylen_max),
|
||||
uint32_t(mdbx_limits_keysize_max(actor.pagesize, actor.table_flags)));
|
||||
key_essentials.bits = (key_essentials.maxlen < sizeof(serial_t))
|
||||
? key_essentials.maxlen * CHAR_BIT
|
||||
: sizeof(serial_t) * CHAR_BIT;
|
||||
key_essentials.mask = actor_params::serial_mask(key_essentials.bits);
|
||||
assert(key_essentials.bits > 63 ||
|
||||
key_essentials.mask > primes[key_essentials.bits]);
|
||||
|
||||
value_essentials.flags = uint16_t(
|
||||
actor.table_flags & MDBX_db_flags_t(MDBX_INTEGERDUP | MDBX_REVERSEDUP));
|
||||
actor.table_flags &
|
||||
MDBX_db_flags_t(MDBX_INTEGERDUP | MDBX_REVERSEDUP | MDBX_DUPFIXED));
|
||||
assert(actor.datalen_min <= UINT16_MAX);
|
||||
value_essentials.minlen = uint16_t(actor.datalen_min);
|
||||
assert(actor.datalen_max <= UINT32_MAX);
|
||||
value_essentials.maxlen =
|
||||
std::min(uint32_t(actor.datalen_max),
|
||||
uint32_t(mdbx_limits_valsize_max(
|
||||
actor.pagesize, MDBX_db_flags_t(key_essentials.flags))));
|
||||
value_essentials.maxlen = std::min(
|
||||
uint32_t(actor.datalen_max),
|
||||
uint32_t(mdbx_limits_valsize_max(actor.pagesize, actor.table_flags)));
|
||||
value_essentials.bits = (value_essentials.maxlen < sizeof(serial_t))
|
||||
? value_essentials.maxlen * CHAR_BIT
|
||||
: sizeof(serial_t) * CHAR_BIT;
|
||||
value_essentials.mask = actor_params::serial_mask(value_essentials.bits);
|
||||
assert(value_essentials.bits > 63 ||
|
||||
value_essentials.mask > primes[value_essentials.bits]);
|
||||
|
||||
if (!actor.keygen.zero_fill) {
|
||||
key_essentials.flags |= essentials::prng_fill_flag;
|
||||
value_essentials.flags |= essentials::prng_fill_flag;
|
||||
}
|
||||
|
||||
(void)thread_number;
|
||||
mapping = actor.keygen;
|
||||
salt =
|
||||
(actor.keygen.seed + uint64_t(actor_id)) * UINT64_C(14653293970879851569);
|
||||
const auto split = mapping.split;
|
||||
while (mapping.split >
|
||||
value_essentials.bits - essentials::value_age_minwidth ||
|
||||
mapping.split >= mapping.width)
|
||||
mapping.split -= 1;
|
||||
if (split != mapping.width)
|
||||
log_notice("keygen: reduce mapping-split from %u to %u", split,
|
||||
mapping.split);
|
||||
|
||||
const auto width = mapping.width;
|
||||
while (unsigned((actor.table_flags & MDBX_DUPSORT)
|
||||
? mapping.width - mapping.split
|
||||
: mapping.width) > key_essentials.bits)
|
||||
mapping.width -= 1;
|
||||
if (width != mapping.width)
|
||||
log_notice("keygen: reduce mapping-width from %u to %u", width,
|
||||
mapping.width);
|
||||
|
||||
value_age_bits = value_essentials.bits - mapping.split;
|
||||
value_age_mask = actor_params::serial_mask(value_age_bits);
|
||||
assert(value_age_bits >= essentials::value_age_minwidth);
|
||||
|
||||
salt = (prng_state ^
|
||||
(thread_number * 1575554837) * UINT64_C(59386707711075671)) *
|
||||
UINT64_C(14653293970879851569);
|
||||
base = actor.serial_base();
|
||||
}
|
||||
|
||||
@ -307,11 +374,24 @@ buffer alloc(size_t limit) {
|
||||
return buffer(ptr);
|
||||
}
|
||||
|
||||
void __hot maker::mk_begin(const serial_t serial, const essentials ¶ms,
|
||||
result &out) {
|
||||
serial_t __hot maker::mk_begin(serial_t serial, const essentials ¶ms,
|
||||
result &out) {
|
||||
assert(out.limit >= params.maxlen);
|
||||
assert(params.maxlen >= params.minlen);
|
||||
assert(params.maxlen >= length(serial));
|
||||
assert(serial <= params.mask);
|
||||
if (unlikely(serial > params.mask)) {
|
||||
#if 1
|
||||
serial %= primes[params.bits];
|
||||
assert(params.mask > primes[params.bits]);
|
||||
#else
|
||||
const serial_t maxbits = params.maxlen * CHAR_BIT;
|
||||
serial ^= (serial >> maxbits / 2) *
|
||||
serial_t((sizeof(serial_t) > 4) ? UINT64_C(40719303417517073)
|
||||
: UINT32_C(3708688457));
|
||||
serial &= params.mask;
|
||||
#endif
|
||||
assert(params.maxlen >= length(serial));
|
||||
}
|
||||
|
||||
out.value.iov_len = std::max(unsigned(params.minlen), length(serial));
|
||||
const auto variation = params.maxlen - params.minlen;
|
||||
@ -328,6 +408,7 @@ void __hot maker::mk_begin(const serial_t serial, const essentials ¶ms,
|
||||
assert(length(serial) <= out.value.iov_len);
|
||||
assert(out.value.iov_len >= params.minlen);
|
||||
assert(out.value.iov_len <= params.maxlen);
|
||||
return serial;
|
||||
}
|
||||
|
||||
void __hot maker::mk_continue(const serial_t serial, const essentials ¶ms,
|
||||
|
@ -108,25 +108,24 @@ class maker {
|
||||
|
||||
struct essentials {
|
||||
uint16_t minlen{0};
|
||||
enum { prng_fill_flag = 1 };
|
||||
enum { prng_fill_flag = 1, value_age_minwidth = 5 };
|
||||
uint16_t flags{0};
|
||||
uint32_t maxlen{0};
|
||||
serial_t mask{0};
|
||||
unsigned bits;
|
||||
} key_essentials, value_essentials;
|
||||
unsigned value_age_bits;
|
||||
serial_t value_age_mask{0};
|
||||
|
||||
static void mk_begin(const serial_t serial, const essentials ¶ms,
|
||||
result &out);
|
||||
static serial_t mk_begin(serial_t serial, const essentials ¶ms,
|
||||
result &out);
|
||||
static void mk_continue(const serial_t serial, const essentials ¶ms,
|
||||
result &out);
|
||||
static void mk(const serial_t serial, const essentials ¶ms, result &out) {
|
||||
mk_begin(serial, params, out);
|
||||
mk_continue(serial, params, out);
|
||||
}
|
||||
|
||||
public:
|
||||
void pair(serial_t serial, const buffer &key, buffer &value,
|
||||
serial_t value_age, const bool keylen_changeable);
|
||||
void setup(const config::actor_params_pod &actor, unsigned actor_id,
|
||||
unsigned thread_number);
|
||||
void setup(const config::actor_params_pod &actor, unsigned thread_number);
|
||||
bool is_unordered() const;
|
||||
void seek2end(serial_t &serial) const;
|
||||
|
||||
@ -141,6 +140,11 @@ public:
|
||||
}
|
||||
return increment(serial, int64_t(uint64_t(delta) << mapping.split));
|
||||
}
|
||||
|
||||
serial_t remix_age(serial_t serial) const {
|
||||
return (UINT64_C(768097847591) * (serial ^ UINT64_C(768097847591))) &
|
||||
value_age_mask;
|
||||
}
|
||||
};
|
||||
|
||||
void log_pair(logging::loglevel level, const char *prefix, const buffer &key,
|
||||
|
145
test/log.c++
145
test/log.c++
@ -53,10 +53,18 @@ static void mdbx_logger(MDBX_log_level_t priority, const char *function,
|
||||
|
||||
namespace logging {
|
||||
|
||||
static std::string prefix;
|
||||
static std::string suffix;
|
||||
/* логирование может быть вызвано после деструкторов */
|
||||
static char prefix_buf[64];
|
||||
static size_t prefix_len;
|
||||
static std::string suffix_buf;
|
||||
static const char *suffix_ptr = "~~~";
|
||||
struct suffix_cleaner {
|
||||
suffix_cleaner() { suffix_ptr = ""; }
|
||||
~suffix_cleaner() { suffix_ptr = "~~~"; }
|
||||
} static anchor;
|
||||
|
||||
static loglevel level;
|
||||
static FILE *last;
|
||||
static FILE *flow;
|
||||
|
||||
void setlevel(loglevel priority) {
|
||||
level = priority;
|
||||
@ -67,12 +75,15 @@ void setlevel(loglevel priority) {
|
||||
log_trace("set mdbx debug-opts: 0x%02x", rc);
|
||||
}
|
||||
|
||||
void setup(loglevel priority, const std::string &_prefix) {
|
||||
setlevel(priority);
|
||||
prefix = _prefix;
|
||||
void setup(const std::string &prefix) {
|
||||
prefix_len = std::min(prefix.size(), sizeof(prefix_buf) - 1);
|
||||
memcpy(prefix_buf, prefix.data(), prefix_len);
|
||||
}
|
||||
|
||||
void setup(const std::string &_prefix) { prefix = _prefix; }
|
||||
void setup(loglevel priority, const std::string &prefix) {
|
||||
setlevel(priority);
|
||||
setup(prefix);
|
||||
}
|
||||
|
||||
const char *level2str(const loglevel alevel) {
|
||||
switch (alevel) {
|
||||
@ -108,18 +119,18 @@ bool output(const loglevel priority, const char *format, ...) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ln() {
|
||||
if (flow) {
|
||||
putc('\n', flow);
|
||||
if (flow != stdout)
|
||||
putc('\n', stdout);
|
||||
flow = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void output_nocheckloglevel_ap(const logging::loglevel priority,
|
||||
const char *format, va_list ap) {
|
||||
if (last) {
|
||||
putc('\n', last);
|
||||
fflush(last);
|
||||
if (last == stderr) {
|
||||
putc('\n', stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
last = nullptr;
|
||||
}
|
||||
|
||||
ln();
|
||||
chrono::time now = chrono::now_realtime();
|
||||
struct tm tm;
|
||||
#ifdef _MSC_VER
|
||||
@ -134,30 +145,27 @@ void output_nocheckloglevel_ap(const logging::loglevel priority,
|
||||
if (rc != MDBX_SUCCESS)
|
||||
failure_perror("localtime_r()", rc);
|
||||
|
||||
last = stdout;
|
||||
fprintf(last,
|
||||
fprintf(stdout,
|
||||
"[ %02d%02d%02d-%02d:%02d:%02d.%06d_%05lu %-10s %.4s ] %s" /* TODO */,
|
||||
tm.tm_year - 100, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min,
|
||||
tm.tm_sec, chrono::fractional2us(now.fractional), (long)osal_getpid(),
|
||||
prefix.c_str(), level2str(priority), suffix.c_str());
|
||||
prefix_buf, level2str(priority), suffix_ptr);
|
||||
|
||||
va_list ones;
|
||||
memset(&ones, 0, sizeof(ones)) /* zap MSVC and other goofy compilers */;
|
||||
if (same_or_higher(priority, error))
|
||||
va_copy(ones, ap);
|
||||
vfprintf(last, format, ap);
|
||||
vfprintf(stdout, format, ap);
|
||||
|
||||
size_t len = strlen(format);
|
||||
char end = len ? format[len - 1] : '\0';
|
||||
|
||||
switch (end) {
|
||||
default:
|
||||
putc('\n', last);
|
||||
MDBX_CXX17_FALLTHROUGH; // fall through
|
||||
putc('\n', stdout);
|
||||
break;
|
||||
case '\n':
|
||||
fflush(last);
|
||||
last = nullptr;
|
||||
MDBX_CXX17_FALLTHROUGH; // fall through
|
||||
break;
|
||||
case ' ':
|
||||
case '_':
|
||||
case ':':
|
||||
@ -167,46 +175,39 @@ void output_nocheckloglevel_ap(const logging::loglevel priority,
|
||||
case '\b':
|
||||
case '\r':
|
||||
case '\0':
|
||||
flow = stdout;
|
||||
break;
|
||||
}
|
||||
|
||||
if (same_or_higher(priority, error)) {
|
||||
if (last != stderr) {
|
||||
fprintf(stderr, "[ %05lu %-10s %.4s ] %s", (long)osal_getpid(),
|
||||
prefix.c_str(), level2str(priority), suffix.c_str());
|
||||
vfprintf(stderr, format, ones);
|
||||
if (end == '\n')
|
||||
fflush(stderr);
|
||||
else
|
||||
last = stderr;
|
||||
}
|
||||
if (flow)
|
||||
flow = stderr;
|
||||
fprintf(stderr, "[ %05lu %-10s %.4s ] %s", (long)osal_getpid(), prefix_buf,
|
||||
level2str(priority), suffix_ptr);
|
||||
vfprintf(stderr, format, ones);
|
||||
va_end(ones);
|
||||
}
|
||||
}
|
||||
|
||||
bool feed_ap(const char *format, va_list ap) {
|
||||
if (!last)
|
||||
if (!flow)
|
||||
return false;
|
||||
|
||||
if (last == stderr) {
|
||||
if (flow == stderr) {
|
||||
va_list ones;
|
||||
va_copy(ones, ap);
|
||||
vfprintf(stdout, format, ones);
|
||||
va_end(ones);
|
||||
}
|
||||
vfprintf(last, format, ap);
|
||||
vfprintf(flow, format, ap);
|
||||
size_t len = strlen(format);
|
||||
if (len && format[len - 1] == '\n') {
|
||||
fflush(last);
|
||||
if (last == stderr)
|
||||
fflush(stdout);
|
||||
last = nullptr;
|
||||
}
|
||||
if (len && format[len - 1] == '\n')
|
||||
flow = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool feed(const char *format, ...) {
|
||||
if (!last)
|
||||
if (!flow)
|
||||
return false;
|
||||
|
||||
va_list ap;
|
||||
@ -217,29 +218,36 @@ bool feed(const char *format, ...) {
|
||||
}
|
||||
|
||||
local_suffix::local_suffix(const char *c_str)
|
||||
: trim_pos(suffix.size()), indent(0) {
|
||||
suffix.append(c_str);
|
||||
: trim_pos(suffix_buf.size()), indent(0) {
|
||||
suffix_buf.append(c_str);
|
||||
suffix_ptr = suffix_buf.c_str();
|
||||
}
|
||||
|
||||
local_suffix::local_suffix(const std::string &str)
|
||||
: trim_pos(suffix.size()), indent(0) {
|
||||
suffix.append(str);
|
||||
: trim_pos(suffix_buf.size()), indent(0) {
|
||||
suffix_buf.append(str);
|
||||
suffix_ptr = suffix_buf.c_str();
|
||||
}
|
||||
|
||||
void local_suffix::push() {
|
||||
indent += 1;
|
||||
suffix.push_back('\t');
|
||||
suffix_buf.push_back('\t');
|
||||
suffix_ptr = suffix_buf.c_str();
|
||||
}
|
||||
|
||||
void local_suffix::pop() {
|
||||
assert(indent > 0);
|
||||
if (indent > 0) {
|
||||
indent -= 1;
|
||||
suffix.pop_back();
|
||||
suffix_buf.pop_back();
|
||||
suffix_ptr = suffix_buf.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
local_suffix::~local_suffix() { suffix.erase(trim_pos); }
|
||||
local_suffix::~local_suffix() {
|
||||
suffix_buf.erase(trim_pos);
|
||||
suffix_ptr = suffix_buf.c_str();
|
||||
}
|
||||
|
||||
void progress_canary(bool active) {
|
||||
static chrono::time progress_timestamp;
|
||||
@ -294,73 +302,73 @@ void progress_canary(bool active) {
|
||||
} // namespace logging
|
||||
|
||||
void log_extra(const char *msg, ...) {
|
||||
logging::ln();
|
||||
if (logging::same_or_higher(logging::extra, logging::level)) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
logging::output_nocheckloglevel_ap(logging::extra, msg, ap);
|
||||
va_end(ap);
|
||||
} else
|
||||
logging::last = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void log_trace(const char *msg, ...) {
|
||||
logging::ln();
|
||||
if (logging::same_or_higher(logging::trace, logging::level)) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
logging::output_nocheckloglevel_ap(logging::trace, msg, ap);
|
||||
va_end(ap);
|
||||
} else
|
||||
logging::last = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void log_debug(const char *msg, ...) {
|
||||
logging::ln();
|
||||
if (logging::same_or_higher(logging::debug, logging::level)) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
logging::output_nocheckloglevel_ap(logging::debug, msg, ap);
|
||||
va_end(ap);
|
||||
} else
|
||||
logging::last = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void log_verbose(const char *msg, ...) {
|
||||
logging::ln();
|
||||
if (logging::same_or_higher(logging::verbose, logging::level)) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
logging::output_nocheckloglevel_ap(logging::verbose, msg, ap);
|
||||
va_end(ap);
|
||||
} else
|
||||
logging::last = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void log_notice(const char *msg, ...) {
|
||||
logging::ln();
|
||||
if (logging::same_or_higher(logging::notice, logging::level)) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
logging::output_nocheckloglevel_ap(logging::notice, msg, ap);
|
||||
va_end(ap);
|
||||
} else
|
||||
logging::last = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void log_warning(const char *msg, ...) {
|
||||
logging::ln();
|
||||
if (logging::same_or_higher(logging::warning, logging::level)) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
logging::output_nocheckloglevel_ap(logging::warning, msg, ap);
|
||||
va_end(ap);
|
||||
} else
|
||||
logging::last = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void log_error(const char *msg, ...) {
|
||||
logging::ln();
|
||||
if (logging::same_or_higher(logging::error, logging::level)) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
logging::output_nocheckloglevel_ap(logging::error, msg, ap);
|
||||
va_end(ap);
|
||||
} else
|
||||
logging::last = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void log_trouble(const char *where, const char *what, int errnum) {
|
||||
@ -371,4 +379,7 @@ bool log_enabled(const logging::loglevel priority) {
|
||||
return logging::same_or_higher(priority, logging::level);
|
||||
}
|
||||
|
||||
void log_flush(void) { fflushall(); }
|
||||
void log_flush(void) {
|
||||
logging::ln();
|
||||
fflushall();
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ bool MDBX_PRINTF_ARGS(2, 3)
|
||||
output(const loglevel priority, const char *format, ...);
|
||||
bool feed_ap(const char *format, va_list ap);
|
||||
bool MDBX_PRINTF_ARGS(1, 2) feed(const char *format, ...);
|
||||
void ln();
|
||||
|
||||
void inline MDBX_PRINTF_ARGS(2, 3)
|
||||
output_nocheckloglevel(const loglevel priority, const char *format, ...) {
|
||||
|
@ -13,6 +13,7 @@ DB_UPTO_MB=17408
|
||||
PAGESIZE=min
|
||||
DONT_CHECK_RAM=no
|
||||
EXTRA=no
|
||||
TAILLOG=0
|
||||
|
||||
while [ -n "$1" ]
|
||||
do
|
||||
@ -35,9 +36,13 @@ do
|
||||
echo "--pagesize NN Use specified page size (256 is minimal and used by default)"
|
||||
echo "--dont-check-ram-size Don't check available RAM"
|
||||
echo "--extra Iterate extra modes/flags"
|
||||
echo "--taillog Dump tail of test log on failure"
|
||||
echo "--help Print this usage help and exit"
|
||||
exit -2
|
||||
;;
|
||||
--taillog)
|
||||
TAILLOG=3333
|
||||
;;
|
||||
--multi)
|
||||
LIST=basic
|
||||
;;
|
||||
@ -62,7 +67,7 @@ do
|
||||
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"
|
||||
MONITOR="valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --read-var-info=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt"
|
||||
rm -f valgrind-*.log
|
||||
;;
|
||||
--skip-make)
|
||||
@ -345,14 +350,38 @@ if which lz4 >/dev/null; then
|
||||
function logger {
|
||||
lz4 > ${TESTDB_DIR}/long.log.lz4
|
||||
}
|
||||
function taillog {
|
||||
if [ -s ${TESTDB_DIR}/long.log.lz4 ]; then
|
||||
echo "=============================================== last ${TAILLOG} lines"
|
||||
lz4 -d -c ${TESTDB_DIR}/long.log.lz4 | tail -n ${TAILLOG}
|
||||
else
|
||||
echo "=============================================== no test log"
|
||||
fi
|
||||
}
|
||||
elif which gzip >/dev/null; then
|
||||
function logger {
|
||||
gzip > ${TESTDB_DIR}/long.log.gz
|
||||
}
|
||||
function taillog {
|
||||
if [ -s ${TESTDB_DIR}/long.log.gz ]; then
|
||||
echo "=============================================== last ${TAILLOG} lines"
|
||||
gzip -d -c ${TESTDB_DIR}/long.log.gz | tail -n ${TAILLOG}
|
||||
else
|
||||
echo "=============================================== no test log"
|
||||
fi
|
||||
}
|
||||
else
|
||||
function logger {
|
||||
cat > ${TESTDB_DIR}/long.log
|
||||
}
|
||||
function taillog {
|
||||
if [ -s ${TESTDB_DIR}/long.log ]; then
|
||||
echo "=============================================== last ${TAILLOG} lines"
|
||||
tail -n ${TAILLOG} ${TESTDB_DIR}/long.log
|
||||
else
|
||||
echo "=============================================== no test log"
|
||||
fi
|
||||
}
|
||||
fi
|
||||
|
||||
if [ "$EXTRA" != "no" ]; then
|
||||
@ -375,6 +404,9 @@ function bits2options {
|
||||
|
||||
function failed {
|
||||
echo "FAILED" >&2
|
||||
if [ ${TAILLOG} -gt 0 ]; then
|
||||
taillog
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
@ -421,120 +453,102 @@ 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=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=9 \
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
|
||||
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=11 \
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
|
||||
|
||||
split=24
|
||||
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=10 \
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
|
||||
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=12 \
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
|
||||
|
||||
|
||||
split=16
|
||||
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=9 \
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
|
||||
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=11 \
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
|
||||
|
||||
if [ "$EXTRA" != "no" ]; then
|
||||
split=10
|
||||
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=13 \
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
|
||||
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=16 \
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
|
||||
fi
|
||||
|
||||
split=4
|
||||
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--pagesize=$PAGESIZE --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%4]} \
|
||||
--keygen.seed=${seed}
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --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%4]}
|
||||
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=21 \
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
|
||||
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
|
||||
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=32 \
|
||||
--prng-seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=rnd \
|
||||
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
|
||||
done # options
|
||||
loop=$((loop + 1))
|
||||
|
@ -37,6 +37,7 @@ MDBX_NORETURN void usage(void) {
|
||||
" --console[=yes/no] Enable/disable console-like output\n"
|
||||
" --cleanup-before[=YES/no] Cleanup/remove and re-create database\n"
|
||||
" --cleanup-after[=YES/no] Cleanup/remove database after completion\n"
|
||||
" --prng-seed=N Seed PRNG\n"
|
||||
"Database size control:\n"
|
||||
" --pagesize=... Database page size: min, max, 256..65536\n"
|
||||
" --size-lower=N[K|M|G|T] Lower-bound of size in Kb/Mb/Gb/Tb\n"
|
||||
@ -60,6 +61,10 @@ MDBX_NORETURN void usage(void) {
|
||||
" --append Append-mode insertions\n"
|
||||
" --dead.reader Dead-reader simulator\n"
|
||||
" --dead.writer Dead-writer simulator\n"
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
" --fork.reader After-fork reader\n"
|
||||
" --fork.writer After-fork writer\n"
|
||||
#endif /* Windows */
|
||||
"Actor options:\n"
|
||||
" --batch.read=N Read-operations batch size\n"
|
||||
" --batch.write=N Write-operations batch size\n"
|
||||
@ -84,7 +89,6 @@ MDBX_NORETURN void usage(void) {
|
||||
" --datalen=N Set both min/max for data length\n"
|
||||
" --keygen.width=N TBD (see the source code)\n"
|
||||
" --keygen.mesh=N TBD (see the source code)\n"
|
||||
" --keygen.seed=N TBD (see the source code)\n"
|
||||
" --keygen.zerofill=yes|NO TBD (see the source code)\n"
|
||||
" --keygen.split=N TBD (see the source code)\n"
|
||||
" --keygen.rotate=N TBD (see the source code)\n"
|
||||
@ -140,7 +144,7 @@ void actor_params::set_defaults(const std::string &tmpdir) {
|
||||
growth_step = -1;
|
||||
pagesize = -1;
|
||||
|
||||
keygen.seed = 1;
|
||||
prng_seed = 0;
|
||||
keygen.zero_fill = false;
|
||||
keygen.keycase = kc_random;
|
||||
keygen.width = (table_flags & MDBX_DUPSORT) ? 32 : 64;
|
||||
@ -263,8 +267,19 @@ static void fixup4qemu(actor_params ¶ms) {
|
||||
(void)params;
|
||||
}
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
static void set_linebuf_append(FILE *out) {
|
||||
setvbuf(out, NULL, _IOLBF, 65536);
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
int fd = fileno(out);
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if (flags != -1)
|
||||
(void)fcntl(fd, F_SETFD, O_APPEND | flags);
|
||||
#endif /* !Windows */
|
||||
}
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
set_linebuf_append(stdout);
|
||||
set_linebuf_append(stderr);
|
||||
#ifdef _DEBUG
|
||||
log_trace("#argc = %d", argc);
|
||||
for (int i = 0; i < argc; ++i)
|
||||
@ -434,9 +449,11 @@ int main(int argc, char *const argv[]) {
|
||||
if (config::parse_option(argc, argv, narg, "keygen.mesh",
|
||||
params.keygen.mesh, 0, 64))
|
||||
continue;
|
||||
if (config::parse_option(argc, argv, narg, "keygen.seed",
|
||||
params.keygen.seed, config::no_scale))
|
||||
if (config::parse_option(argc, argv, narg, "prng-seed", params.prng_seed,
|
||||
config::no_scale)) {
|
||||
prng_seed(params.prng_seed);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "keygen.zerofill",
|
||||
params.keygen.zero_fill))
|
||||
continue;
|
||||
@ -453,49 +470,59 @@ int main(int argc, char *const argv[]) {
|
||||
keycase_setup(value, params);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "keylen.min", params.keylen_min,
|
||||
config::no_scale, params.mdbx_keylen_min(),
|
||||
params.mdbx_keylen_max())) {
|
||||
if (config::parse_option(
|
||||
argc, argv, narg, "keylen.min", params.keylen_min,
|
||||
(params.table_flags & MDBX_INTEGERKEY) ? config::intkey
|
||||
: config::no_scale,
|
||||
params.mdbx_keylen_min(), params.mdbx_keylen_max())) {
|
||||
if ((params.table_flags & MDBX_INTEGERKEY) ||
|
||||
params.keylen_max < params.keylen_min)
|
||||
params.keylen_max = params.keylen_min;
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "keylen.max", params.keylen_max,
|
||||
config::no_scale, params.mdbx_keylen_min(),
|
||||
params.mdbx_keylen_max())) {
|
||||
if (config::parse_option(
|
||||
argc, argv, narg, "keylen.max", params.keylen_max,
|
||||
(params.table_flags & MDBX_INTEGERKEY) ? config::intkey
|
||||
: config::no_scale,
|
||||
params.mdbx_keylen_min(), params.mdbx_keylen_max())) {
|
||||
if ((params.table_flags & MDBX_INTEGERKEY) ||
|
||||
params.keylen_min > params.keylen_max)
|
||||
params.keylen_min = params.keylen_max;
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "keylen", params.keylen_min,
|
||||
config::no_scale, params.mdbx_keylen_min(),
|
||||
params.mdbx_keylen_max())) {
|
||||
if (config::parse_option(
|
||||
argc, argv, narg, "keylen", params.keylen_min,
|
||||
(params.table_flags & MDBX_INTEGERKEY) ? config::intkey
|
||||
: config::no_scale,
|
||||
params.mdbx_keylen_min(), params.mdbx_keylen_max())) {
|
||||
params.keylen_max = params.keylen_min;
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "datalen.min",
|
||||
params.datalen_min, config::no_scale,
|
||||
params.mdbx_datalen_min(),
|
||||
params.mdbx_datalen_max())) {
|
||||
if (config::parse_option(
|
||||
argc, argv, narg, "datalen.min", params.datalen_min,
|
||||
(params.table_flags & MDBX_INTEGERDUP) ? config::intkey
|
||||
: config::no_scale,
|
||||
params.mdbx_datalen_min(), params.mdbx_datalen_max())) {
|
||||
if ((params.table_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) ||
|
||||
params.datalen_max < params.datalen_min)
|
||||
params.datalen_max = params.datalen_min;
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "datalen.max",
|
||||
params.datalen_max, config::no_scale,
|
||||
params.mdbx_datalen_min(),
|
||||
params.mdbx_datalen_max())) {
|
||||
if (config::parse_option(
|
||||
argc, argv, narg, "datalen.max", params.datalen_max,
|
||||
(params.table_flags & MDBX_INTEGERDUP) ? config::intkey
|
||||
: config::no_scale,
|
||||
params.mdbx_datalen_min(), params.mdbx_datalen_max())) {
|
||||
if ((params.table_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) ||
|
||||
params.datalen_min > params.datalen_max)
|
||||
params.datalen_min = params.datalen_max;
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "datalen", params.datalen_min,
|
||||
config::no_scale, params.mdbx_datalen_min(),
|
||||
params.mdbx_datalen_max())) {
|
||||
if (config::parse_option(
|
||||
argc, argv, narg, "datalen", params.datalen_min,
|
||||
(params.table_flags & MDBX_INTEGERDUP) ? config::intkey
|
||||
: config::no_scale,
|
||||
params.mdbx_datalen_min(), params.mdbx_datalen_max())) {
|
||||
params.datalen_max = params.datalen_min;
|
||||
continue;
|
||||
}
|
||||
@ -591,6 +618,18 @@ int main(int argc, char *const argv[]) {
|
||||
configure_actor(last_space_id, ac_nested, value, params);
|
||||
continue;
|
||||
}
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
if (config::parse_option(argc, argv, narg, "fork.reader", nullptr)) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_forkread, value, params);
|
||||
continue;
|
||||
}
|
||||
if (config::parse_option(argc, argv, narg, "fork.writer", nullptr)) {
|
||||
fixup4qemu(params);
|
||||
configure_actor(last_space_id, ac_forkwrite, value, params);
|
||||
continue;
|
||||
}
|
||||
#endif /* Windows */
|
||||
|
||||
if (*argv[narg] != '-') {
|
||||
fixup4qemu(params);
|
||||
@ -704,6 +743,14 @@ int main(int argc, char *const argv[]) {
|
||||
log_trace("=== done...");
|
||||
}
|
||||
|
||||
if (!failed) {
|
||||
MDBX_envinfo info;
|
||||
int err =
|
||||
mdbx_preopen_snapinfo(params.pathname_db.c_str(), &info, sizeof(info));
|
||||
if (err != MDBX_SUCCESS)
|
||||
failure_perror("mdbx_preopen_snapinfo()", err);
|
||||
}
|
||||
|
||||
log_notice("RESULT: %s\n", failed ? "Failed" : "Successful");
|
||||
if (global::config::cleanup_after) {
|
||||
if (failed)
|
||||
|
@ -74,7 +74,7 @@ bool testcase_nested::setup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
|
||||
keyvalue_maker.setup(config.params, 0 /* thread_number */);
|
||||
key = keygen::alloc(config.params.keylen_max);
|
||||
data = keygen::alloc(config.params.datalen_max);
|
||||
serial = 0;
|
||||
@ -292,8 +292,7 @@ retry:
|
||||
}
|
||||
|
||||
bool testcase_nested::run() {
|
||||
uint64_t seed =
|
||||
prng64_map2_white(config.params.keygen.seed) + config.actor_id;
|
||||
uint64_t seed = prng64_map2_white(prng_state) + config.space_id;
|
||||
|
||||
clear_wholetable_passed = 0;
|
||||
clear_stepbystep_passed = 0;
|
||||
|
@ -356,6 +356,7 @@ mdbx_pid_t osal_getpid(void) { return getpid(); }
|
||||
int osal_delay(unsigned seconds) { return sleep(seconds) ? errno : 0; }
|
||||
|
||||
int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) {
|
||||
static sigset_t mask;
|
||||
if (children.empty()) {
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
@ -366,7 +367,6 @@ int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) {
|
||||
sigaction(SIGUSR1, &act, nullptr);
|
||||
sigaction(SIGUSR2, &act, nullptr);
|
||||
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
@ -377,6 +377,7 @@ int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) {
|
||||
pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
sigprocmask(SIG_BLOCK, &mask, nullptr);
|
||||
overlord_pid = getppid();
|
||||
const bool result = test_execute(config);
|
||||
exit(result ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
@ -400,7 +401,7 @@ void osal_killall_actors(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char *signal_name(const int sig) {
|
||||
const char *signal_name(const int sig) {
|
||||
if (sig == SIGHUP)
|
||||
return "HUP";
|
||||
if (sig == SIGINT)
|
||||
@ -532,24 +533,25 @@ int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
|
||||
children[pid] =
|
||||
(WEXITSTATUS(status) == EXIT_SUCCESS) ? as_successful : as_failed;
|
||||
else if (WIFSIGNALED(status)) {
|
||||
int sig = WTERMSIG(status);
|
||||
#ifdef WCOREDUMP
|
||||
if (WCOREDUMP(status))
|
||||
children[pid] = as_coredump;
|
||||
else
|
||||
#endif /* WCOREDUMP */
|
||||
switch (WTERMSIG(status)) {
|
||||
switch (sig) {
|
||||
case SIGABRT:
|
||||
case SIGBUS:
|
||||
case SIGFPE:
|
||||
case SIGILL:
|
||||
case SIGSEGV:
|
||||
log_notice("child pid %lu terminated by SIG%s", (long)pid,
|
||||
signal_name(WTERMSIG(status)));
|
||||
log_notice("child pid %lu %s by SIG%s", (long)pid, "terminated",
|
||||
signal_name(sig));
|
||||
children[pid] = as_coredump;
|
||||
break;
|
||||
default:
|
||||
log_notice("child pid %lu killed by SIG%s", (long)pid,
|
||||
signal_name(WTERMSIG(status)));
|
||||
log_notice("child pid %lu %s by SIG%s", (long)pid, "killed",
|
||||
signal_name(sig));
|
||||
children[pid] = as_killed;
|
||||
}
|
||||
} else if (WIFSTOPPED(status))
|
||||
|
@ -46,3 +46,7 @@ std::string osal_tempdir(void);
|
||||
#define STDERR_FILENO _fileno(stderr)
|
||||
#endif
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
const char *signal_name(const int sig);
|
||||
#endif /* Windows */
|
||||
|
@ -60,7 +60,7 @@ do
|
||||
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"
|
||||
MONITOR="valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --read-var-info=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt"
|
||||
rm -f valgrind-*.log
|
||||
;;
|
||||
--skip-make)
|
||||
|
107
test/test.c++
107
test/test.c++
@ -39,6 +39,12 @@ const char *testcase2str(const actor_testcase testcase) {
|
||||
return "ttl";
|
||||
case ac_nested:
|
||||
return "nested";
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
case ac_forkread:
|
||||
return "fork.reader";
|
||||
case ac_forkwrite:
|
||||
return "fork.writer";
|
||||
#endif /* Windows */
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,29 +543,43 @@ int testcase::db_open__begin__table_create_open_clean(MDBX_dbi &handle) {
|
||||
return err;
|
||||
}
|
||||
|
||||
MDBX_dbi testcase::db_table_open(bool create) {
|
||||
log_trace(">> testcase::db_table_create");
|
||||
|
||||
char tablename_buf[16];
|
||||
const char *testcase::db_tablename(tablename_buf &buffer,
|
||||
const char *suffix) const {
|
||||
const char *tablename = nullptr;
|
||||
if (config.space_id) {
|
||||
int rc = snprintf(tablename_buf, sizeof(tablename_buf), "TBL%04u",
|
||||
config.space_id);
|
||||
int rc =
|
||||
snprintf(buffer, sizeof(buffer), "TBL%04u%s", config.space_id, suffix);
|
||||
if (rc < 4 || rc >= (int)sizeof(tablename_buf) - 1)
|
||||
failure("snprintf(tablename): %d", rc);
|
||||
tablename = tablename_buf;
|
||||
tablename = buffer;
|
||||
}
|
||||
log_debug("use %s table", tablename ? tablename : "MAINDB");
|
||||
return tablename;
|
||||
}
|
||||
|
||||
MDBX_dbi testcase::db_table_open(bool create, bool expect_failure) {
|
||||
log_trace(">> testcase::db_table_%s%s", create ? "create" : "open",
|
||||
expect_failure ? "(expect_failure)" : "");
|
||||
|
||||
tablename_buf buffer;
|
||||
const char *tablename = db_tablename(buffer);
|
||||
|
||||
MDBX_dbi handle = 0;
|
||||
int rc = mdbx_dbi_open(txn_guard.get(), tablename,
|
||||
(create ? MDBX_CREATE : MDBX_DB_DEFAULTS) |
|
||||
config.params.table_flags,
|
||||
&handle);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
failure_perror("mdbx_dbi_open()", rc);
|
||||
int rc = mdbx_dbi_open(
|
||||
txn_guard.get(), tablename,
|
||||
create ? (MDBX_CREATE | config.params.table_flags)
|
||||
: (flipcoin() ? MDBX_DB_ACCEDE
|
||||
: MDBX_DB_DEFAULTS | config.params.table_flags),
|
||||
&handle);
|
||||
if (unlikely(expect_failure != (rc != MDBX_SUCCESS))) {
|
||||
char act[64];
|
||||
snprintf(act, sizeof(act), "mdbx_dbi_open(create=%s,expect_failure=%s)",
|
||||
create ? "true" : "false", expect_failure ? "true" : "false");
|
||||
failure_perror(act, rc);
|
||||
}
|
||||
|
||||
log_trace("<< testcase::db_table_create, handle %u", handle);
|
||||
log_trace("<< testcase::db_table_%s%s, handle %u", create ? "create" : "open",
|
||||
expect_failure ? "(expect_failure)" : "", handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -579,9 +599,9 @@ void testcase::db_table_drop(MDBX_dbi handle) {
|
||||
|
||||
void testcase::db_table_clear(MDBX_dbi handle, MDBX_txn *txn) {
|
||||
log_trace(">> testcase::db_table_clear, handle %u", handle);
|
||||
int rc = mdbx_drop(txn ? txn : txn_guard.get(), handle, false);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
failure_perror("mdbx_drop(delete=false)", rc);
|
||||
int err = mdbx_drop(txn ? txn : txn_guard.get(), handle, false);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure_perror("mdbx_drop(delete=false)", err);
|
||||
speculum.clear();
|
||||
log_trace("<< testcase::db_table_clear");
|
||||
}
|
||||
@ -589,21 +609,25 @@ void testcase::db_table_clear(MDBX_dbi handle, MDBX_txn *txn) {
|
||||
void testcase::db_table_close(MDBX_dbi handle) {
|
||||
log_trace(">> testcase::db_table_close, handle %u", handle);
|
||||
assert(!txn_guard);
|
||||
int rc = mdbx_dbi_close(db_guard.get(), handle);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
failure_perror("mdbx_dbi_close()", rc);
|
||||
int err = mdbx_dbi_close(db_guard.get(), handle);
|
||||
if (unlikely(err != MDBX_SUCCESS))
|
||||
failure_perror("mdbx_dbi_close()", err);
|
||||
log_trace("<< testcase::db_table_close");
|
||||
}
|
||||
|
||||
void testcase::checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
|
||||
bool testcase::checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
|
||||
MDBX_val expected_valued) {
|
||||
MDBX_val actual_value = expected_valued;
|
||||
int rc = mdbx_get_equal_or_great(txn_guard.get(), handle, &key2check,
|
||||
&actual_value);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
failure_perror(step, rc);
|
||||
int err = mdbx_get_equal_or_great(txn_guard.get(), handle, &key2check,
|
||||
&actual_value);
|
||||
if (unlikely(err != MDBX_SUCCESS)) {
|
||||
if (!config.params.speculum || err != MDBX_RESULT_TRUE)
|
||||
failure_perror(step, (err == MDBX_RESULT_TRUE) ? MDBX_NOTFOUND : err);
|
||||
return false;
|
||||
}
|
||||
if (!is_samedata(&actual_value, &expected_valued))
|
||||
failure("%s data mismatch", step);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -648,8 +672,8 @@ bool test_execute(const actor_config &config_const) {
|
||||
size_t(config.params.nrepeat));
|
||||
else
|
||||
log_verbose("test successfully (iteration %zi)", iter);
|
||||
config.params.keygen.seed += INT32_C(0xA4F4D37B);
|
||||
log_verbose("turn keygen to %u", config.params.keygen.seed);
|
||||
prng_seed(config.params.prng_seed += INT32_C(0xA4F4D37B));
|
||||
log_verbose("turn PRNG to %u", config.params.prng_seed);
|
||||
}
|
||||
|
||||
} while (config.params.nrepeat == 0 || iter < config.params.nrepeat);
|
||||
@ -968,7 +992,9 @@ int testcase::insert(const keygen::buffer &akey, const keygen::buffer &adata,
|
||||
}
|
||||
|
||||
auto it_lowerbound = insertion_result.first;
|
||||
if (++it_lowerbound != speculum.end()) {
|
||||
if (insertion_result.second)
|
||||
++it_lowerbound;
|
||||
if (it_lowerbound != speculum.end()) {
|
||||
const auto cursor_lowerbound = speculum_cursors[lowerbound].get();
|
||||
speculum_check_cursor("after-insert", "lowerbound", it_lowerbound,
|
||||
cursor_lowerbound, MDBX_GET_CURRENT);
|
||||
@ -995,30 +1021,37 @@ int testcase::insert(const keygen::buffer &akey, const keygen::buffer &adata,
|
||||
|
||||
int testcase::replace(const keygen::buffer &akey,
|
||||
const keygen::buffer &new_data,
|
||||
const keygen::buffer &old_data, MDBX_put_flags_t flags) {
|
||||
const keygen::buffer &old_data, MDBX_put_flags_t flags,
|
||||
bool hush_keygen_mistakes) {
|
||||
int expected_err = MDBX_SUCCESS;
|
||||
if (config.params.speculum) {
|
||||
const auto S_key = iov2dataview(akey);
|
||||
const auto S_old = iov2dataview(old_data);
|
||||
const auto S_new = iov2dataview(new_data);
|
||||
const auto removed = speculum.erase(SET::key_type(S_key, S_old));
|
||||
if (unlikely(removed != 1)) {
|
||||
if (unlikely(!removed)) {
|
||||
char dump_key[128], dump_value[128];
|
||||
log_error(
|
||||
"speculum-%s: %s old value {%s, %s}", "replace",
|
||||
(removed > 1) ? "multi" : "no",
|
||||
"speculum-%s: no old pair {%s, %s} (keygen mistake)", "replace",
|
||||
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&old_data->value, dump_value, sizeof(dump_value)));
|
||||
}
|
||||
if (unlikely(!speculum.emplace(S_key, S_new).second)) {
|
||||
expected_err = MDBX_NOTFOUND;
|
||||
} else if (unlikely(!speculum.emplace(S_key, S_new).second)) {
|
||||
char dump_key[128], dump_value[128];
|
||||
log_error(
|
||||
"speculum-replace: new pair not inserted {%s, %s}",
|
||||
"speculum-%s: %s {%s, %s}", "replace", "new pair not inserted",
|
||||
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
|
||||
mdbx_dump_val(&new_data->value, dump_value, sizeof(dump_value)));
|
||||
expected_err = MDBX_KEYEXIST;
|
||||
}
|
||||
}
|
||||
return mdbx_replace(txn_guard.get(), dbi, &akey->value, &new_data->value,
|
||||
&old_data->value, flags);
|
||||
int err = mdbx_replace(txn_guard.get(), dbi, &akey->value, &new_data->value,
|
||||
&old_data->value, flags);
|
||||
if (err && err == expected_err && hush_keygen_mistakes) {
|
||||
log_notice("speculum-%s: %s %d", "replace", "hust keygen mistake", err);
|
||||
err = MDBX_SUCCESS;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int testcase::remove(const keygen::buffer &akey, const keygen::buffer &adata) {
|
||||
|
@ -103,7 +103,7 @@ class registry {
|
||||
struct record {
|
||||
actor_testcase id = ac_none;
|
||||
std::string name;
|
||||
bool (*review_params)(actor_params &) = nullptr;
|
||||
bool (*review_params)(actor_params &, unsigned space_id) = nullptr;
|
||||
testcase *(*constructor)(const actor_config &, const mdbx_pid_t) = nullptr;
|
||||
};
|
||||
std::unordered_map<std::string, const record *> name2id;
|
||||
@ -124,8 +124,8 @@ public:
|
||||
add(this);
|
||||
}
|
||||
};
|
||||
static bool review_actor_params(const actor_testcase id,
|
||||
actor_params ¶ms);
|
||||
static bool review_actor_params(const actor_testcase id, actor_params ¶ms,
|
||||
const unsigned space_id);
|
||||
static testcase *create_actor(const actor_config &config,
|
||||
const mdbx_pid_t pid);
|
||||
};
|
||||
@ -232,7 +232,8 @@ protected:
|
||||
int insert(const keygen::buffer &akey, const keygen::buffer &adata,
|
||||
MDBX_put_flags_t flags);
|
||||
int replace(const keygen::buffer &akey, const keygen::buffer &new_value,
|
||||
const keygen::buffer &old_value, MDBX_put_flags_t flags);
|
||||
const keygen::buffer &old_value, MDBX_put_flags_t flags,
|
||||
bool hush_keygen_mistakes = true);
|
||||
int remove(const keygen::buffer &akey, const keygen::buffer &adata);
|
||||
|
||||
static int hsr_callback(const MDBX_env *env, const MDBX_txn *txn,
|
||||
@ -248,9 +249,10 @@ protected:
|
||||
void db_prepare();
|
||||
void db_open();
|
||||
void db_close();
|
||||
void txn_begin(bool readonly, MDBX_txn_flags_t flags = MDBX_TXN_READWRITE);
|
||||
virtual void txn_begin(bool readonly,
|
||||
MDBX_txn_flags_t flags = MDBX_TXN_READWRITE);
|
||||
int breakable_commit();
|
||||
void txn_end(bool abort);
|
||||
virtual void txn_end(bool abort);
|
||||
int breakable_restart();
|
||||
void txn_restart(bool abort, bool readonly,
|
||||
MDBX_txn_flags_t flags = MDBX_TXN_READWRITE);
|
||||
@ -261,11 +263,14 @@ protected:
|
||||
void txn_inject_writefault(MDBX_txn *txn);
|
||||
void fetch_canary();
|
||||
void update_canary(uint64_t increment);
|
||||
void checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
|
||||
bool checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
|
||||
MDBX_val expected_valued);
|
||||
unsigned txn_underutilization_x256(MDBX_txn *txn) const;
|
||||
|
||||
MDBX_dbi db_table_open(bool create);
|
||||
using tablename_buf = char[32];
|
||||
const char *db_tablename(tablename_buf &buffer,
|
||||
const char *suffix = "") const;
|
||||
MDBX_dbi db_table_open(bool create, bool expect_failure = false);
|
||||
void db_table_drop(MDBX_dbi handle);
|
||||
void db_table_clear(MDBX_dbi handle, MDBX_txn *txn = nullptr);
|
||||
void db_table_close(MDBX_dbi handle);
|
||||
@ -298,8 +303,9 @@ public:
|
||||
memset(&last, 0, sizeof(last));
|
||||
}
|
||||
|
||||
static bool review_params(actor_params ¶ms) {
|
||||
static bool review_params(actor_params ¶ms, unsigned space_id) {
|
||||
// silently fix key/data length for fixed-length modes
|
||||
params.prng_seed += bleach32(space_id);
|
||||
if ((params.table_flags & MDBX_INTEGERKEY) &&
|
||||
params.keylen_min != params.keylen_max)
|
||||
params.keylen_min = params.keylen_max;
|
||||
|
@ -119,9 +119,8 @@ bool testcase_ttl::run() {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t seed =
|
||||
prng64_map2_white(config.params.keygen.seed) + config.actor_id;
|
||||
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
|
||||
uint64_t seed = prng64_map2_white(prng_state) + config.space_id;
|
||||
keyvalue_maker.setup(config.params, 0 /* thread_number */);
|
||||
key = keygen::alloc(config.params.keylen_max);
|
||||
data = keygen::alloc(config.params.datalen_max);
|
||||
const MDBX_put_flags_t insert_flags =
|
||||
|
@ -107,18 +107,22 @@ uint64_t prng64_white(uint64_t &state) {
|
||||
return bleach64(state);
|
||||
}
|
||||
|
||||
uint32_t prng32(uint64_t &state) {
|
||||
return (uint32_t)(prng64_careless(state) >> 32);
|
||||
uint32_t prng32_fast(uint64_t &state) {
|
||||
return uint32_t(prng64_careless(state) >> 32);
|
||||
}
|
||||
|
||||
uint32_t prng32_white(uint64_t &state) {
|
||||
return bleach32(uint32_t(prng64_careless(state) >> 32));
|
||||
}
|
||||
|
||||
void prng_fill(uint64_t &state, void *ptr, size_t bytes) {
|
||||
uint32_t u32 = prng32(state);
|
||||
uint32_t u32 = prng32_fast(state);
|
||||
|
||||
while (bytes >= 4) {
|
||||
memcpy(ptr, &u32, 4);
|
||||
ptr = (uint32_t *)ptr + 1;
|
||||
bytes -= 4;
|
||||
u32 = prng32(state);
|
||||
u32 = prng32_fast(state);
|
||||
}
|
||||
|
||||
switch (bytes & 3) {
|
||||
@ -136,11 +140,11 @@ void prng_fill(uint64_t &state, void *ptr, size_t bytes) {
|
||||
}
|
||||
}
|
||||
|
||||
static __thread uint64_t prng_state;
|
||||
/* __thread */ uint64_t prng_state;
|
||||
|
||||
void prng_seed(uint64_t seed) { prng_state = bleach64(seed); }
|
||||
|
||||
uint32_t prng32(void) { return prng32(prng_state); }
|
||||
uint32_t prng32(void) { return prng32_white(prng_state); }
|
||||
|
||||
uint64_t prng64(void) { return prng64_white(prng_state); }
|
||||
|
||||
|
@ -288,24 +288,26 @@ inline bool is_samedata(const MDBX_val &a, const MDBX_val &b) {
|
||||
}
|
||||
std::string format(const char *fmt, ...);
|
||||
|
||||
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
|
||||
v ^= rot64(v, 25) ^ rot64(v, 50);
|
||||
v *= UINT64_C(0xA24BAED4963EE407);
|
||||
v ^= rot64(v, 24) ^ rot64(v, 49);
|
||||
v *= UINT64_C(0x9FB21C651E98DF25);
|
||||
return v ^ v >> 28;
|
||||
static inline uint64_t bleach64(uint64_t x) {
|
||||
// NASAM from Tommy Ettinger,
|
||||
// https://www.blogger.com/profile/04953541827437796598
|
||||
// http://mostlymangling.blogspot.com/2020/01/nasam-not-another-strange-acronym-mixer.html
|
||||
x ^= rot64(x, 25) ^ rot64(x, 47);
|
||||
x *= UINT64_C(0x9E6C63D0676A9A99);
|
||||
x ^= x >> 23 ^ x >> 51;
|
||||
x *= UINT64_C(0x9E6D62D06F6A9A9B);
|
||||
x ^= x >> 23 ^ x >> 51;
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline uint32_t bleach32(uint32_t x) {
|
||||
// https://github.com/skeeto/hash-prospector
|
||||
// exact bias: 0.17353355999581582
|
||||
// exact bias: 0.10760229515479501
|
||||
x ^= x >> 16;
|
||||
x *= UINT32_C(0x7feb352d);
|
||||
x *= UINT32_C(0x21f0aaad);
|
||||
x ^= 0x3027C563 ^ (x >> 15);
|
||||
x *= UINT32_C(0x846ca68b);
|
||||
x ^= x >> 16;
|
||||
x *= UINT32_C(0x0d35a2d97);
|
||||
x ^= x >> 15;
|
||||
return x;
|
||||
}
|
||||
|
||||
@ -343,9 +345,11 @@ static inline double u64_to_double1(uint64_t v) {
|
||||
}
|
||||
|
||||
uint64_t prng64_white(uint64_t &state);
|
||||
uint32_t prng32(uint64_t &state);
|
||||
uint32_t prng32_white(uint64_t &state);
|
||||
uint32_t prng32_fast(uint64_t &state);
|
||||
void prng_fill(uint64_t &state, void *ptr, size_t bytes);
|
||||
|
||||
extern uint64_t prng_state;
|
||||
void prng_seed(uint64_t seed);
|
||||
uint32_t prng32(void);
|
||||
uint64_t prng64(void);
|
||||
|
@ -2,7 +2,6 @@
|
||||
msync-whole-mmap-1
|
||||
Memcheck:Param
|
||||
msync(start)
|
||||
fun:msync
|
||||
...
|
||||
fun:sync_locked*
|
||||
}
|
||||
@ -10,7 +9,6 @@
|
||||
msync-whole-mmap-2
|
||||
Memcheck:Param
|
||||
msync(start)
|
||||
fun:msync
|
||||
...
|
||||
fun:env_sync*
|
||||
}
|
||||
@ -18,7 +16,6 @@
|
||||
msync-whole-mmap-3
|
||||
Memcheck:Param
|
||||
msync(start)
|
||||
fun:msync
|
||||
...
|
||||
fun:map_resize*
|
||||
}
|
||||
@ -26,7 +23,6 @@
|
||||
msync-wipe-steady
|
||||
Memcheck:Param
|
||||
msync(start)
|
||||
fun:msync
|
||||
...
|
||||
fun:wipe_steady*
|
||||
}
|
||||
@ -34,7 +30,6 @@
|
||||
msync-meta
|
||||
Memcheck:Param
|
||||
msync(start)
|
||||
fun:msync
|
||||
...
|
||||
fun:meta_sync*
|
||||
}
|
||||
@ -42,7 +37,6 @@
|
||||
msync-spill
|
||||
Memcheck:Param
|
||||
msync(start)
|
||||
fun:msync
|
||||
...
|
||||
fun:txn_spill*
|
||||
}
|
||||
@ -72,7 +66,6 @@
|
||||
pwrite-page-flush
|
||||
Memcheck:Param
|
||||
pwrite(buf)
|
||||
fun:pwrite
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -80,7 +73,6 @@
|
||||
pwrite64-page-flush
|
||||
Memcheck:Param
|
||||
pwrite64(buf)
|
||||
fun:pwrite
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -90,16 +82,14 @@
|
||||
# pwritev-page-flush
|
||||
# Memcheck:Param
|
||||
# pwritev(vector[...])
|
||||
# fun:pwritev
|
||||
# ...
|
||||
# fun: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:iov_write*\n}"; done >> valgrind_suppress.txt
|
||||
# for((i=0;i<64;++i)); do echo -e "{\n pwritev-page-flush-$i\n Memcheck:Param\n pwritev(vector[$i])\n ...\n fun:iov_write*\n}"; done >> valgrind_suppress.txt
|
||||
{
|
||||
pwritev-page-flush-0
|
||||
Memcheck:Param
|
||||
pwritev(vector[0])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -107,7 +97,6 @@
|
||||
pwritev-page-flush-1
|
||||
Memcheck:Param
|
||||
pwritev(vector[1])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -115,7 +104,6 @@
|
||||
pwritev-page-flush-2
|
||||
Memcheck:Param
|
||||
pwritev(vector[2])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -123,7 +111,6 @@
|
||||
pwritev-page-flush-3
|
||||
Memcheck:Param
|
||||
pwritev(vector[3])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -131,7 +118,6 @@
|
||||
pwritev-page-flush-4
|
||||
Memcheck:Param
|
||||
pwritev(vector[4])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -139,7 +125,6 @@
|
||||
pwritev-page-flush-5
|
||||
Memcheck:Param
|
||||
pwritev(vector[5])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -147,7 +132,6 @@
|
||||
pwritev-page-flush-6
|
||||
Memcheck:Param
|
||||
pwritev(vector[6])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -155,7 +139,6 @@
|
||||
pwritev-page-flush-7
|
||||
Memcheck:Param
|
||||
pwritev(vector[7])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -163,7 +146,6 @@
|
||||
pwritev-page-flush-8
|
||||
Memcheck:Param
|
||||
pwritev(vector[8])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -171,7 +153,6 @@
|
||||
pwritev-page-flush-9
|
||||
Memcheck:Param
|
||||
pwritev(vector[9])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -179,7 +160,6 @@
|
||||
pwritev-page-flush-10
|
||||
Memcheck:Param
|
||||
pwritev(vector[10])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -187,7 +167,6 @@
|
||||
pwritev-page-flush-11
|
||||
Memcheck:Param
|
||||
pwritev(vector[11])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -195,7 +174,6 @@
|
||||
pwritev-page-flush-12
|
||||
Memcheck:Param
|
||||
pwritev(vector[12])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -203,7 +181,6 @@
|
||||
pwritev-page-flush-13
|
||||
Memcheck:Param
|
||||
pwritev(vector[13])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -211,7 +188,6 @@
|
||||
pwritev-page-flush-14
|
||||
Memcheck:Param
|
||||
pwritev(vector[14])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -219,7 +195,6 @@
|
||||
pwritev-page-flush-15
|
||||
Memcheck:Param
|
||||
pwritev(vector[15])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -227,7 +202,6 @@
|
||||
pwritev-page-flush-16
|
||||
Memcheck:Param
|
||||
pwritev(vector[16])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -235,7 +209,6 @@
|
||||
pwritev-page-flush-17
|
||||
Memcheck:Param
|
||||
pwritev(vector[17])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -243,7 +216,6 @@
|
||||
pwritev-page-flush-18
|
||||
Memcheck:Param
|
||||
pwritev(vector[18])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -251,7 +223,6 @@
|
||||
pwritev-page-flush-19
|
||||
Memcheck:Param
|
||||
pwritev(vector[19])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -259,7 +230,6 @@
|
||||
pwritev-page-flush-20
|
||||
Memcheck:Param
|
||||
pwritev(vector[20])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -267,7 +237,6 @@
|
||||
pwritev-page-flush-21
|
||||
Memcheck:Param
|
||||
pwritev(vector[21])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -275,7 +244,6 @@
|
||||
pwritev-page-flush-22
|
||||
Memcheck:Param
|
||||
pwritev(vector[22])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -283,7 +251,6 @@
|
||||
pwritev-page-flush-23
|
||||
Memcheck:Param
|
||||
pwritev(vector[23])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -291,7 +258,6 @@
|
||||
pwritev-page-flush-24
|
||||
Memcheck:Param
|
||||
pwritev(vector[24])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -299,7 +265,6 @@
|
||||
pwritev-page-flush-25
|
||||
Memcheck:Param
|
||||
pwritev(vector[25])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -307,7 +272,6 @@
|
||||
pwritev-page-flush-26
|
||||
Memcheck:Param
|
||||
pwritev(vector[26])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -315,7 +279,6 @@
|
||||
pwritev-page-flush-27
|
||||
Memcheck:Param
|
||||
pwritev(vector[27])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -323,7 +286,6 @@
|
||||
pwritev-page-flush-28
|
||||
Memcheck:Param
|
||||
pwritev(vector[28])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -331,7 +293,6 @@
|
||||
pwritev-page-flush-29
|
||||
Memcheck:Param
|
||||
pwritev(vector[29])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -339,7 +300,6 @@
|
||||
pwritev-page-flush-30
|
||||
Memcheck:Param
|
||||
pwritev(vector[30])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -347,7 +307,6 @@
|
||||
pwritev-page-flush-31
|
||||
Memcheck:Param
|
||||
pwritev(vector[31])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -355,7 +314,6 @@
|
||||
pwritev-page-flush-32
|
||||
Memcheck:Param
|
||||
pwritev(vector[32])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -363,7 +321,6 @@
|
||||
pwritev-page-flush-33
|
||||
Memcheck:Param
|
||||
pwritev(vector[33])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -371,7 +328,6 @@
|
||||
pwritev-page-flush-34
|
||||
Memcheck:Param
|
||||
pwritev(vector[34])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -379,7 +335,6 @@
|
||||
pwritev-page-flush-35
|
||||
Memcheck:Param
|
||||
pwritev(vector[35])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -387,7 +342,6 @@
|
||||
pwritev-page-flush-36
|
||||
Memcheck:Param
|
||||
pwritev(vector[36])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -395,7 +349,6 @@
|
||||
pwritev-page-flush-37
|
||||
Memcheck:Param
|
||||
pwritev(vector[37])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -403,7 +356,6 @@
|
||||
pwritev-page-flush-38
|
||||
Memcheck:Param
|
||||
pwritev(vector[38])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -411,7 +363,6 @@
|
||||
pwritev-page-flush-39
|
||||
Memcheck:Param
|
||||
pwritev(vector[39])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -419,7 +370,6 @@
|
||||
pwritev-page-flush-40
|
||||
Memcheck:Param
|
||||
pwritev(vector[40])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -427,7 +377,6 @@
|
||||
pwritev-page-flush-41
|
||||
Memcheck:Param
|
||||
pwritev(vector[41])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -435,7 +384,6 @@
|
||||
pwritev-page-flush-42
|
||||
Memcheck:Param
|
||||
pwritev(vector[42])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -443,7 +391,6 @@
|
||||
pwritev-page-flush-43
|
||||
Memcheck:Param
|
||||
pwritev(vector[43])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -451,7 +398,6 @@
|
||||
pwritev-page-flush-44
|
||||
Memcheck:Param
|
||||
pwritev(vector[44])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -459,7 +405,6 @@
|
||||
pwritev-page-flush-45
|
||||
Memcheck:Param
|
||||
pwritev(vector[45])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -467,7 +412,6 @@
|
||||
pwritev-page-flush-46
|
||||
Memcheck:Param
|
||||
pwritev(vector[46])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -475,7 +419,6 @@
|
||||
pwritev-page-flush-47
|
||||
Memcheck:Param
|
||||
pwritev(vector[47])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -483,7 +426,6 @@
|
||||
pwritev-page-flush-48
|
||||
Memcheck:Param
|
||||
pwritev(vector[48])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -491,7 +433,6 @@
|
||||
pwritev-page-flush-49
|
||||
Memcheck:Param
|
||||
pwritev(vector[49])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -499,7 +440,6 @@
|
||||
pwritev-page-flush-50
|
||||
Memcheck:Param
|
||||
pwritev(vector[50])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -507,7 +447,6 @@
|
||||
pwritev-page-flush-51
|
||||
Memcheck:Param
|
||||
pwritev(vector[51])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -515,7 +454,6 @@
|
||||
pwritev-page-flush-52
|
||||
Memcheck:Param
|
||||
pwritev(vector[52])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -523,7 +461,6 @@
|
||||
pwritev-page-flush-53
|
||||
Memcheck:Param
|
||||
pwritev(vector[53])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -531,7 +468,6 @@
|
||||
pwritev-page-flush-54
|
||||
Memcheck:Param
|
||||
pwritev(vector[54])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -539,7 +475,6 @@
|
||||
pwritev-page-flush-55
|
||||
Memcheck:Param
|
||||
pwritev(vector[55])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -547,7 +482,6 @@
|
||||
pwritev-page-flush-56
|
||||
Memcheck:Param
|
||||
pwritev(vector[56])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -555,7 +489,6 @@
|
||||
pwritev-page-flush-57
|
||||
Memcheck:Param
|
||||
pwritev(vector[57])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -563,7 +496,6 @@
|
||||
pwritev-page-flush-58
|
||||
Memcheck:Param
|
||||
pwritev(vector[58])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -571,7 +503,6 @@
|
||||
pwritev-page-flush-59
|
||||
Memcheck:Param
|
||||
pwritev(vector[59])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -579,7 +510,6 @@
|
||||
pwritev-page-flush-60
|
||||
Memcheck:Param
|
||||
pwritev(vector[60])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -587,7 +517,6 @@
|
||||
pwritev-page-flush-61
|
||||
Memcheck:Param
|
||||
pwritev(vector[61])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -595,7 +524,6 @@
|
||||
pwritev-page-flush-62
|
||||
Memcheck:Param
|
||||
pwritev(vector[62])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
@ -603,7 +531,6 @@
|
||||
pwritev-page-flush-63
|
||||
Memcheck:Param
|
||||
pwritev(vector[63])
|
||||
fun:pwritev
|
||||
...
|
||||
fun:iov_write*
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user