mdbx: вливание ветки devel в master.

56 files changed, 8997 insertions(+), 4507 deletions(-)
This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2024-03-21 11:53:38 +03:00
commit abca22e32d
No known key found for this signature in database
GPG Key ID: 518BD10B927E8686
56 changed files with 9069 additions and 4579 deletions

View File

@ -305,7 +305,7 @@ else()
"${CMAKE_CURRENT_SOURCE_DIR}/test/valgrind_suppress.txt" "${CMAKE_CURRENT_SOURCE_DIR}/test/valgrind_suppress.txt"
CACHE FILEPATH "Suppressions file for Valgrind" FORCE) CACHE FILEPATH "Suppressions file for Valgrind" FORCE)
set(MEMORYCHECK_COMMAND_OPTIONS 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) CACHE STRING "Valgrind options" FORCE)
set(VALGRIND_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS}" CACHE STRING "Valgrind options" FORCE) set(VALGRIND_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS}" CACHE STRING "Valgrind options" FORCE)
endif() 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) macro(add_mdbx_option NAME DESCRIPTION DEFAULT)
list(APPEND MDBX_BUILD_OPTIONS ${NAME}) list(APPEND MDBX_BUILD_OPTIONS ${NAME})
if(NOT ${DEFAULT} STREQUAL "AUTO") 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_PGOP_STAT "Gathering statistics for page operations" ON)
add_mdbx_option(MDBX_ENABLE_PROFGC "Profiling of GC search and updates" OFF) add_mdbx_option(MDBX_ENABLE_PROFGC "Profiling of GC search and updates" OFF)
mark_as_advanced(MDBX_ENABLE_PROFGC) 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(NOT MDBX_AMALGAMATED_SOURCE)
if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE_UPPERCASE STREQUAL "DEBUG") if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE_UPPERCASE STREQUAL "DEBUG")

View File

@ -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) 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). 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 ## v0.12.10 "СЭМ" от 2024-03-12
Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов
@ -152,6 +213,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
## v0.12.8 "Владимир Уткин" от 2023-10-17 ## v0.12.8 "Владимир Уткин" от 2023-10-17
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов, Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
в день 100-летия со дня рождения выдающегося советского и российского ученого и конструктора [Влади́мира Фёдоровича У́ткина](https://ru.wikipedia.org/wiki/Уткин,_Владимир_Фёдорович). в день 100-летия со дня рождения выдающегося советского и российского ученого и конструктора [Влади́мира Фёдоровича У́ткина](https://ru.wikipedia.org/wiki/Уткин,_Владимир_Фёдорович).

View File

@ -172,22 +172,22 @@ help:
@echo " make bench-clean - remove temp database(s) after benchmark" @echo " make bench-clean - remove temp database(s) after benchmark"
#> dist-cutoff-begin #> dist-cutoff-begin
@echo "" @echo ""
@echo " make smoke - fast smoke test"
@echo " make test - basic test"
@echo " make check - smoke test with amalgamation and installation checking" @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 smoke - fast smoke test"
@echo " make memcheck - build with Valgrind's and smoke test with memcheck tool" @echo " make smoke-memcheck - build with Valgrind support and run smoke test under memcheck tool"
@echo " make test-valgrind - build with Valgrind's and basic test with memcheck tool" @echo " make smoke-fault - execute transaction owner failure smoke testcase"
@echo " make test-asan - build with AddressSanitizer and basic test" @echo " make smoke-singleprocess - execute single-process smoke test"
@echo " make test-leak - build with LeakSanitizer and basic test" @echo " make test - basic test"
@echo " make test-ubsan - build with UndefinedBehaviourSanitizer and 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-gcc - check cross-compilation without test execution"
@echo " make cross-qemu - run cross-compilation and execution basic test with QEMU" @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 gcc-analyzer - run gcc-analyzer (mostly useless for now)"
@echo " make build-test - build test executable(s)" @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 ""
@echo " make dist - build amalgamated source code" @echo " make dist - build amalgamated source code"
@echo " make doxygen - build HTML documentation" @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: 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: reformat release-assets tags smoke test test-asan smoke-fault test-leak
.PHONY: smoke-singleprocess test-singleprocess test-ubsan test-valgrind memcheck .PHONY: smoke-singleprocess test-singleprocess test-ubsan test-valgrind test-memcheck memcheck smoke-memcheck
.PHONY: smoke-assertion test-assertion long-test-assertion .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 define uname2osal
case "$(UNAME)" in case "$(UNAME)" in
@ -418,27 +424,30 @@ smoke-fault: build-test
test: build-test test: build-test
@echo ' RUNNING `test/long_stochastic.sh --loops 2`...' @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`...' @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 test-singleprocess: build-test
@echo ' RUNNING `test/long_stochastic.sh --single --loops 2`...' @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: test-memcheck
test-valgrind: build-test test-memcheck: CFLAGS_EXTRA=-Ofast -DENABLE_MEMCHECK
test-memcheck: build-test
@echo ' RUNNING `test/long_stochastic.sh --with-valgrind --loops 2`...' @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: smoke-memcheck
memcheck: CFLAGS_EXTRA=-Ofast -DMDBX_USE_VALGRIND 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
memcheck: build-test smoke-memcheck: CFLAGS_EXTRA=-Ofast -DENABLE_MEMCHECK
smoke-memcheck: build-test
@echo " SMOKE \`mdbx_test basic\` under Valgrind's memcheck..." @echo " SMOKE \`mdbx_test basic\` under Valgrind's memcheck..."
$(QUIET)rm -f valgrind-*.log $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; ( \ $(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 --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_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=4 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic && \
$(VALGRIND) ./mdbx_chk -vvn $(TEST_DB) && \ $(VALGRIND) ./mdbx_chk -vvn $(TEST_DB) && \

View File

@ -6,9 +6,10 @@ bench bench-clean bench-couple bench-quartet bench-triplet re-bench \
lib libs lib-static lib-shared tools-static \ lib libs lib-static lib-shared tools-static \
libmdbx mdbx mdbx_chk mdbx_copy mdbx_drop mdbx_dump mdbx_load mdbx_stat \ 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 \ check dist memcheck cross-gcc cross-qemu doxygen gcc-analyzer reformat \
release-assets tags test build-test mdbx_test smoke smoke-fault smoke-singleprocess \ release-assets tags build-test mdbx_test \
smoke-assertion test-assertion long-test-assertion \ smoke smoke-fault smoke-singleprocess smoke-assertion smoke-memcheck \
test-asan test-leak test-singleprocess test-ubsan test-valgrind: 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) \ @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` \ 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;'` \ `which gmake || which gnumake || echo 'echo "GNU Make 3.80 or above is required"; exit 2;'` \

View File

@ -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. 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). - [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). - [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). - [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 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). - [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). - [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). - [Large/Overflow pages accounting for dirty-room](https://libmdbx.dqdkfa.ru/dead-github/issues/192).

View File

@ -24,6 +24,25 @@ endif()
cmake_policy(PUSH) cmake_policy(PUSH)
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION}) 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) include(CheckLibraryExists)
check_library_exists(gcov __gcov_flush "" HAVE_GCOV) check_library_exists(gcov __gcov_flush "" HAVE_GCOV)
@ -33,23 +52,23 @@ option(ENABLE_GCOV
option(ENABLE_GPROF option(ENABLE_GPROF
"Enable integration with gprof, a performance analyzing tool" OFF) "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 option(ENABLE_ASAN
"Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF) "Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF)
option(ENABLE_UBSAN option(ENABLE_UBSAN
"Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector based on compiler instrumentation" OFF) "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) cmake_policy(POP)

View File

@ -1,4 +1,4 @@
# Doxyfile 1.9.1 # Doxyfile 1.9.6
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project. # doxygen (www.doxygen.org) for a project.
@ -12,6 +12,16 @@
# For lists, items can also be appended using: # For lists, items can also be appended using:
# TAG += value [value, ...] # TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \"). # 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 # Project related configuration options
@ -60,16 +70,28 @@ PROJECT_LOGO =
OUTPUT_DIRECTORY = . OUTPUT_DIRECTORY = .
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# directories (in 2 levels) under the output directory of each output format and # sub-directories (in 2 levels) under the output directory of each output format
# will distribute the generated files over these directories. Enabling this # 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 # 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 # 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. # The default value is: NO.
CREATE_SUBDIRS = 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 # 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 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 # 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 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this # documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language. # information to generate all constant output in the proper language.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # English messages), Korean, Korean-en (Korean with English messages), Latvian,
# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
# Ukrainian and Vietnamese. # Swedish, Turkish, Ukrainian and Vietnamese.
# The default value is: English. # The default value is: English.
OUTPUT_LANGUAGE = 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 # 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 # descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this. # documentation (similar to Javadoc). Set to NO to disable this.
@ -258,16 +272,16 @@ TAB_SIZE = 4
# the documentation. An alias has the form: # the documentation. An alias has the form:
# name=value # name=value
# For example adding # For example adding
# "sideeffect=@par Side Effects:\n" # "sideeffect=@par Side Effects:^^"
# will allow you to put the command \sideeffect (or @sideeffect) in the # will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading # 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 # "Side Effects:". Note that you cannot put \n's in the value part of an alias
# newlines (in the resulting output). You can put ^^ in the value part of an # to insert newlines (in the resulting output). You can put ^^ in the value part
# alias to insert a newline as if a physical newline was in the original file. # of an alias to insert a newline as if a physical newline was in the original
# When you need a literal { or } or , in the value part of an alias you have to # file. When you need a literal { or } or , in the value part of an alias you
# escape them by means of a backslash (\), this can lead to conflicts with the # have to escape them by means of a backslash (\), this can lead to conflicts
# commands \{ and \} for these it is advised to use the version @{ and @} or use # with the commands \{ and \} for these it is advised to use the version @{ and
# a double escape (\\{ and \\}) # @} or use a double escape (\\{ and \\})
ALIASES = ALIASES =
@ -312,8 +326,8 @@ OPTIMIZE_OUTPUT_SLICE = NO
# extension. Doxygen has a built-in mapping, but you can override or extend it # 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 # 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, # 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, # Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # 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 # 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 # 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 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 # 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 # 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 # 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 # 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, # 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 # encounter. Generating dot graphs in parallel is controlled by the
# DOT_NUM_THREADS setting. # DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1. # 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 # 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 # 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 # 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. # The default value is: NO.
HIDE_UNDOC_CLASSES = 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 # 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 # 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 # 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 # 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 # 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 # references to files without having to specify the exact matching casing. On
# Windows (including Cygwin) and MacOS, users should typically set this option # 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 # to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES. # YES.
# The default value is: system dependent. # Possible values are: SYSTEM, NO and YES.
# The default value is: SYSTEM.
CASE_SENSE_NAMES = NO CASE_SENSE_NAMES = NO
@ -610,6 +626,12 @@ HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= 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 # 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 files that are included by a file in the documentation of that file.
# The default value is: YES. # 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 # 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 # 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 # 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 # 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 # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
@ -813,22 +836,38 @@ WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # 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 # potential errors in the documentation, such as documenting some parameters in
# in a documented function, or documenting parameters that don't exist or using # a documented function twice, or documenting parameters that don't exist or
# markup commands wrongly. # using markup commands wrongly.
# The default value is: YES. # The default value is: YES.
WARN_IF_DOC_ERROR = 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 # 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 # are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong or incomplete # value. If set to NO, doxygen will only warn about wrong parameter
# parameter documentation, but not about the absence of documentation. If # documentation, but not about the absence of documentation. If EXTRACT_ALL is
# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # set to YES then this flag will automatically be disabled. See also
# WARN_IF_INCOMPLETE_DOC
# The default value is: NO. # The default value is: NO.
WARN_NO_PARAMDOC = 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 # 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 # 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 # 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 # 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 # be replaced by the version of the file (if it could be obtained via
# FILE_VERSION_FILTER) # FILE_VERSION_FILTER)
# See also: WARN_LINE_FORMAT
# The default value is: $file:$line: $text. # The default value is: $file:$line: $text.
WARN_FORMAT = "$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 # 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 # 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 = WARN_LOGFILE =
@ -877,10 +930,21 @@ INPUT = overall.md \
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv # libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see: # documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8. # The default value is: UTF-8.
INPUT_ENCODING = 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 # 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 # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories. # *.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, # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), # *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, # comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.ucf, *.qsf and *.ice. # *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h FILE_PATTERNS = *.h
@ -936,20 +1000,40 @@ EXCLUDE_PATTERNS =
# (namespaces, classes, functions, etc.) that should be excluded from the # (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 # output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass, # 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 # Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/* # exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS = NOMINMAX __ORDER_BIG_ENDIAN__ __ORDER_LITTLE_ENDIAN__ \ EXCLUDE_SYMBOLS = NOMINMAX \
__has_include __has_attribute __has_builtin __has_cpp_attribute __has_extension __has_feature \ __ORDER_BIG_ENDIAN__ \
HAVE_STRUCT_IOVEC MDBX_STRINGIFY_HELPER MDBX_STRINGIFY \ __ORDER_LITTLE_ENDIAN__ \
MDBX_NOSANITIZE_ENUM MDBX_PRINTF_ARGS \ __has_include \
MDBX_HAVE_CXX20_CONCEPTS \ __has_attribute \
CONSTEXPR_ENUM_FLAGS_OPERATIONS DEFINE_ENUM_FLAG_OPERATORS \ __has_builtin \
bool false true __dll_export __dll_import \ __has_cpp_attribute \
MDBX_64BIT_ATOMIC_CONFIG MDBX_64BIT_CAS_CONFIG MDBX_ENV_CHECKPID_CONFIG MDBX_LOCKING_CONFIG \ __has_extension \
MDBX_TRUST_RTC_CONFIG MDBX_TXN_CHECKOWNER_CONFIG MDBX_USE_OFDLOCKS_CONFIG __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 # 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 # 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 # code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly. # 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 # 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 # need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen. # properly processed by doxygen.
@ -1033,6 +1122,15 @@ FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = 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 # Configuration options related to source browsing
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -1130,9 +1228,11 @@ VERBATIM_HEADERS = YES
CLANG_ASSISTED_PARSING = NO CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to # If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
# YES then doxygen will add the directory of each input to the include path. # tag is set to YES then doxygen will add the directory of each input to the
# include path.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_ADD_INC_PATHS = YES CLANG_ADD_INC_PATHS = YES
@ -1168,10 +1268,11 @@ CLANG_DATABASE_PATH =
ALPHABETICAL_INDEX = YES ALPHABETICAL_INDEX = YES
# In case all classes in a project start with a common prefix, all classes will # The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # that should be ignored while generating the index headers. The IGNORE_PREFIX
# can be used to specify a prefix (or a list of prefixes) that should be ignored # tag works for classes, function and member names. The entity will be placed in
# while generating the index headers. # 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. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX = IGNORE_PREFIX =
@ -1250,7 +1351,12 @@ HTML_STYLESHEET =
# Doxygen will copy the style sheet files to the output directory. # 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 # 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 # 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. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = HTML_EXTRA_STYLESHEET =
@ -1265,9 +1371,22 @@ HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES = 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 # 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 # 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 # 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 # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again. # purple, and 360 is red again.
@ -1277,7 +1396,7 @@ HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_HUE = 220
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # 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. # value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100. # Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
@ -1359,6 +1478,13 @@ GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs" 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 # This tag specifies a string that should uniquely identify the documentation
# set bundle. This should be a reverse domain-name style string, e.g. # set bundle. This should be a reverse domain-name style string, e.g.
# com.mycompany.MyDocSet. Doxygen will append .docset to the name. # 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 # 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 # 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 # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# (see: # on Windows. In the beginning of 2021 Microsoft took the original page, with
# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # 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 # 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 # 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 # 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 # (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 # 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 # further fine tune the look of the index (see "Fine-tuning the output"). As an
# sheet generated by doxygen has an example that shows how to put an image at # example, the default style sheet generated by doxygen has an example that
# the root of the tree instead of the PROJECT_NAME. Since the tree basically has # shows how to put an image at the root of the tree instead of the PROJECT_NAME.
# the same information as the tab index, you could consider setting # Since the tree basically has the same information as the tab index, you could
# DISABLE_INDEX to YES when enabling this option. # consider setting DISABLE_INDEX to YES when enabling this option.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = 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 # 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. # doxygen will group on one line in the generated HTML documentation.
# #
@ -1578,6 +1720,13 @@ TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO 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 # 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 # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for # https://inkscape.org) to generate formulas as SVG images instead of PNGs for
@ -1598,17 +1747,6 @@ HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10 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 # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See # to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details. # the section "Including formulas" for details.
@ -1626,11 +1764,29 @@ FORMULA_MACROFILE =
USE_MATHJAX = YES 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 # When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see: # the MathJax output. For more details about the output format see MathJax
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # 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 # 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. # The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES. # 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 # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing # 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. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment. # MathJax from https://www.mathjax.org before deployment. The default value is:
# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # - 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. # This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ 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 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example # 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 # 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. # This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS = MATHJAX_EXTENSIONS =
@ -1831,29 +1993,31 @@ PAPER_TYPE = a4
EXTRA_PACKAGES = EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the # The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
# generated LaTeX document. The header should contain everything until the first # the generated LaTeX document. The header should contain everything until the
# chapter. If it is left blank doxygen will generate a standard header. See # first chapter. If it is left blank doxygen will generate a standard header. It
# section "Doxygen usage" for information on how to let doxygen write the # is highly recommended to start with a default header using
# default header to a separate file. # 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 # Note: Only use a user-defined header if you know what you are doing!
# following commands have a special meaning inside the header: $title, # Note: The header is subject to change so you typically have to regenerate the
# $datetime, $date, $doxygenversion, $projectname, $projectnumber, # default header when upgrading to a newer version of doxygen. The following
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty # commands have a special meaning inside the header (and footer): For a
# string, for the replacement values of the other commands the user is referred # description of the possible markers and block names see the documentation.
# to HTML_HEADER.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER = LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
# generated LaTeX document. The footer should contain everything after the last # the generated LaTeX document. The footer should contain everything after the
# chapter. If it is left blank doxygen will generate a standard footer. See # 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 # LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer. # special commands can be used inside the footer. See also section "Doxygen
# # usage" for information on how to generate the default footer that doxygen
# Note: Only use a user-defined footer if you know what you are doing! # 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. # This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER = LATEX_FOOTER =
@ -1898,8 +2062,7 @@ USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode # 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 # 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 # if errors occur, instead of asking the user for help.
# when generating formulas in HTML.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
@ -1912,16 +2075,6 @@ LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = 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 # The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See # bibliography, e.g. plainnat, or ieeetr. See
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
@ -2002,16 +2155,6 @@ RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_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 # Configuration options related to the man page output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -2108,15 +2251,6 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook 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 # 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 # 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 # 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. # This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH = INCLUDE_PATH =
@ -2224,24 +2359,30 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator. # recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = DOXYGEN \ PREDEFINED = DOXYGEN \
MDBX_CXX20_CONCEPT(CONCEPT,NAME)="CONCEPT NAME" \ "MDBX_CXX20_CONCEPT(CONCEPT,NAME)=CONCEPT NAME" \
MDBX_STD_FILESYSTEM_PATH=::mdbx::filesystem::path \ MDBX_STD_FILESYSTEM_PATH=::mdbx::filesystem::path \
MDBX_U128_TYPE=uint128_t MDBX_I128_TYPE=int128_t \ MDBX_U128_TYPE=uint128_t \
MDBX_DECLARE_EXCEPTION(NAME)="struct LIBMDBX_API_TYPE NAME : public exception{NAME(const ::mdbx::error &); virtual ~NAME() noexcept; }" \ MDBX_I128_TYPE=int128_t \
MDBX_PURE_FUNCTION=[[gnu::pure]] \ "MDBX_DECLARE_EXCEPTION(NAME)=struct LIBMDBX_API_TYPE NAME : public exception{NAME(const ::mdbx::error &); virtual ~NAME() noexcept; }" \
MDBX_NOTHROW_PURE_FUNCTION="[[gnu::pure, gnu::nothrow]]" \ MDBX_PURE_FUNCTION=[[gnu::pure]] \
MDBX_CONST_FUNCTION=[[gnu::const]] \ "MDBX_NOTHROW_PURE_FUNCTION=[[gnu::pure, gnu::nothrow]]" \
MDBX_NOTHROW_CONST_FUNCTION="[[gnu::const, gnu::nothrow]]" \ MDBX_CONST_FUNCTION=[[gnu::const]] \
MDBX_CXX01_CONSTEXPR=constexpr MDBX_CXX01_CONSTEXPR_VAR=constexpr \ "MDBX_NOTHROW_CONST_FUNCTION=[[gnu::const, gnu::nothrow]]" \
MDBX_CXX11_CONSTEXPR=constexpr MDBX_CXX11_CONSTEXPR_VAR=constexpr \ MDBX_CXX01_CONSTEXPR=constexpr \
MDBX_CXX14_CONSTEXPR=constexpr MDBX_CXX14_CONSTEXPR_VAR=constexpr \ MDBX_CXX01_CONSTEXPR_VAR=constexpr \
MDBX_CXX17_CONSTEXPR=constexpr MDBX_CXX20_CONSTEXPR=constexpr \ MDBX_CXX11_CONSTEXPR=constexpr \
MDBX_CXX17_NOEXCEPT=noexcept MDBX_IF_CONSTEXPR=constexpr \ MDBX_CXX11_CONSTEXPR_VAR=constexpr \
MDBX_CXX20_LIKELY=[[likely]] MDBX_CXX20_UNLIKELY=[[unlikely]] \ MDBX_CXX14_CONSTEXPR=constexpr \
MDBX_MAYBE_UNUSED=[[maybe_unused]] \ MDBX_CXX14_CONSTEXPR_VAR=constexpr \
MDBX_DEPRECATED=[[deprecated]] 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 # 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 # 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 # 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 # 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 # 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. # 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 # 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 # Bell Labs. The other options in this section have no effect if this option is
# set to NO # set to NO
# The default value is: YES. # The default value is: NO.
HAVE_DOT = NO HAVE_DOT = NO
@ -2353,37 +2485,52 @@ HAVE_DOT = NO
DOT_NUM_THREADS = 0 DOT_NUM_THREADS = 0
# When you want a differently looking font in the dot files that doxygen # DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
# generates you can specify the font name using DOT_FONTNAME. You need to make # subgraphs. When you want a differently looking font in the dot files that
# sure dot is able to find the font, which can be done by putting it in a # doxygen generates you can specify fontname, fontcolor and fontsize attributes.
# standard location or by setting the DOTFONTPATH environment variable or by # For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
# setting DOT_FONTPATH to the directory containing the font. # Edge and Graph Attributes specification</a> You need to make sure dot is able
# The default value is: Helvetica. # 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. # 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_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
# dot graphs. # add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
# Minimum value: 4, maximum value: 24, default value: 10. # 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. # 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_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set # around nodes set 'shape=plain' or 'shape=plaintext' <a
# the path where dot can find it using this tag. # 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. # This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH = DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for # If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# each documented class showing the direct and indirect inheritance relations. # graph for each documented class showing the direct and indirect inheritance
# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. # 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. # 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 # 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 # graph for each documented class showing the direct and indirect implementation
@ -2395,7 +2542,8 @@ CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for # 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. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@ -2510,6 +2658,13 @@ GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = 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 # 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 # generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see: # 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 # 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 # to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement). # requirement).
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, # Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# 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,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and # png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus. # png:gdiplus:gdiplus.
# The default value is: png. # The default value is: png.
@ -2565,10 +2718,10 @@ MSCFILE_DIRS =
DIAFILE_DIRS = DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the # 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 # path where java can find the plantuml.jar file or to the filename of jar file
# PlantUML is not used or called during a preprocessing step. Doxygen will # to be used. If left blank, it is assumed PlantUML is not used or called during
# generate a warning when it encounters a \startuml command in this case and # a preprocessing step. Doxygen will generate a warning when it encounters a
# will not generate output for the diagram. # \startuml command in this case and will not generate output for the diagram.
PLANTUML_JAR_PATH = PLANTUML_JAR_PATH =
@ -2606,18 +2759,6 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0 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 # 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 # 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 # 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 # 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 # explaining the meaning of the various boxes and arrows in the dot generated
# graphs. # 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. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to 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 # If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs. # files that are used to generate the various graphs.
# #
# Note: This setting is not only used for dot files but also for msc and # Note: This setting is not only used for dot files but also for msc temporary
# plantuml temporary files. # files.
# The default value is: YES. # The default value is: YES.
DOT_CLEANUP = YES DOT_CLEANUP = YES

View File

@ -106,6 +106,7 @@ reservation can deplete system resources (trigger ENOMEM error, etc)
when setting an inadequately large upper DB size using \ref when setting an inadequately large upper DB size using \ref
mdbx_env_set_geometry() or \ref mdbx::env::geometry. So just avoid this. mdbx_env_set_geometry() or \ref mdbx::env::geometry. So just avoid this.
## Remote filesystems ## Remote filesystems
Do not use MDBX databases on remote filesystems, even between processes Do not use MDBX databases on remote filesystems, even between processes
on the same host. This breaks file locks on some platforms, possibly 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 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. 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 ## Read-only mode
There is no pure read-only mode in a normal explicitly way, since There is no pure read-only mode in a normal explicitly way, since

526
mdbx.h
View File

@ -634,9 +634,9 @@ typedef mode_t mdbx_mode_t;
extern "C" { extern "C" {
#endif #endif
/* MDBX version 0.12.x */ /* MDBX version 0.13.x */
#define MDBX_VERSION_MAJOR 0 #define MDBX_VERSION_MAJOR 0
#define MDBX_VERSION_MINOR 12 #define MDBX_VERSION_MINOR 13
#ifndef LIBMDBX_API #ifndef LIBMDBX_API
#if defined(LIBMDBX_EXPORTS) #if defined(LIBMDBX_EXPORTS)
@ -816,7 +816,7 @@ typedef struct iovec MDBX_val;
#endif /* ! SunOS */ #endif /* ! SunOS */
enum MDBX_constants { enum MDBX_constants {
/** The hard limit for DBI handles */ /** The hard limit for DBI handles. */
MDBX_MAX_DBI = UINT32_C(32765), MDBX_MAX_DBI = UINT32_C(32765),
/** The maximum size of a data item. */ /** 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 * return both key and data, and the return code depends on whether a
* upper-bound was found. * 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 * but returns \ref MDBX_SUCCESS if the greater key was found or
* \ref MDBX_NOTFOUND otherwise. * \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. * 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 * Returns \ref MDBX_SUCCESS if the greater pair was returned or
* \ref MDBX_NOTFOUND otherwise. */ * \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 #ifndef __cplusplus
/** \ingroup c_cursors */ /** \ingroup c_cursors */
@ -1921,7 +1942,7 @@ enum MDBX_error_t {
MDBX_TOO_LARGE = -30417, MDBX_TOO_LARGE = -30417,
/** A thread has attempted to use a not owned object, /** 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, MDBX_THREAD_MISMATCH = -30416,
/** Overlapping read and write transactions for the current thread */ /** 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 */ /** Alternative/Duplicate LCK-file is exists and should be removed manually */
MDBX_DUPLICATED_CLK = -30413, 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 */ /* 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) #if defined(_WIN32) || defined(_WIN64)
MDBX_ENODATA = ERROR_HANDLE_EOF, MDBX_ENODATA = ERROR_HANDLE_EOF,
@ -1950,7 +1975,8 @@ enum MDBX_error_t {
MDBX_EPERM = ERROR_INVALID_FUNCTION, MDBX_EPERM = ERROR_INVALID_FUNCTION,
MDBX_EINTR = ERROR_CANCELLED, MDBX_EINTR = ERROR_CANCELLED,
MDBX_ENOFILE = ERROR_FILE_NOT_FOUND, 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 */ #else /* Windows */
#ifdef ENODATA #ifdef ENODATA
MDBX_ENODATA = ENODATA, MDBX_ENODATA = ENODATA,
@ -1966,7 +1992,8 @@ enum MDBX_error_t {
MDBX_EPERM = EPERM, MDBX_EPERM = EPERM,
MDBX_EINTR = EINTR, MDBX_EINTR = EINTR,
MDBX_ENOFILE = ENOENT, MDBX_ENOFILE = ENOENT,
MDBX_EREMOTE = ENOTBLK MDBX_EREMOTE = ENOTBLK,
MDBX_EDEADLK = EDEADLK
#endif /* !Windows */ #endif /* !Windows */
}; };
#ifndef __cplusplus #ifndef __cplusplus
@ -2106,6 +2133,7 @@ enum MDBX_option_t {
/** \brief Controls the in-process limit to grow a list of reclaimed/recycled /** \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 * page's numbers for finding a sequence of contiguous pages for large data
* items. * items.
* \see MDBX_opt_gc_time_limit
* *
* \details A long values requires allocation of contiguous database pages. * \details A long values requires allocation of contiguous database pages.
* To find such sequences, it may be necessary to accumulate very large lists, * 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 * in the \ref MDBX_WRITEMAP mode by clearing ones through file handle before
* touching. */ * touching. */
MDBX_opt_prefault_write_enable, 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 #ifndef __cplusplus
/** \ingroup c_settings */ /** \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_latter_reader_txnid; /**< ID of the last reader transaction */
uint64_t mi_self_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 */ of caller process */
uint64_t mi_meta0_txnid, mi_meta0_sign; uint64_t mi_meta_txnid[3], mi_meta_sign[3];
uint64_t mi_meta1_txnid, mi_meta1_sign;
uint64_t mi_meta2_txnid, mi_meta2_sign;
uint32_t mi_maxreaders; /**< Total reader slots in the environment */ uint32_t mi_maxreaders; /**< Total reader slots in the environment */
uint32_t mi_numreaders; /**< Max reader slots used in the environment */ uint32_t mi_numreaders; /**< Max reader slots used in the environment */
uint32_t mi_dxb_pagesize; /**< Database pagesize */ uint32_t mi_dxb_pagesize; /**< Database pagesize */
@ -2590,7 +2643,7 @@ struct MDBX_envinfo {
struct { struct {
struct { struct {
uint64_t x, y; uint64_t x, y;
} current, meta0, meta1, meta2; } current, meta[3];
} mi_bootid; } mi_bootid;
/** Bytes not explicitly synchronized to disk */ /** 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); 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 /** \brief Warming up options
* \ingroup c_settings * \ingroup c_settings
* \anchor warmup_flags * \anchor warmup_flags
@ -3310,6 +3440,12 @@ mdbx_limits_dbsize_max(intptr_t pagesize);
MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
mdbx_limits_keysize_max(intptr_t pagesize, MDBX_db_flags_t flags); 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 /** \brief Returns maximal data size in bytes for given page size
* and database flags, or -1 if pagesize is invalid. * and database flags, or -1 if pagesize is invalid.
* \ingroup c_statinfo * \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_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
mdbx_limits_valsize_max(intptr_t pagesize, MDBX_db_flags_t flags); 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 /** \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. * the given size and database flags, or -1 if pagesize is invalid.
* \ingroup c_statinfo * \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(). * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
* *
* \returns A transaction flags, valid if input is an valid transaction, * \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); MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int mdbx_txn_flags(const MDBX_txn *txn);
/** \brief Return the transaction's ID. /** \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_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); 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 /** \defgroup value2key Value-to-Key functions
* \brief Value-to-Key functions to * \brief Value-to-Key functions to
* \ref avoid_custom_comparators "avoid using custom comparators" * \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, LIBMDBX_API int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *cursor,
MDBX_dbi dbi); 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. /** \brief Create a cursor handle for the specified transaction and DBI handle.
* \ingroup c_cursors * \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(). */ * or \ref mdbx_cursor_create(). */
LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor); 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. /** \brief Renew a cursor handle for use within the given transaction.
* \ingroup c_cursors * \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. */ * \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); 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. /** \brief Retrieve by cursor.
* \ingroup c_crud * \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. */ * \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key, LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key,
MDBX_val *data, MDBX_cursor_op op); 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. /** \brief Retrieve multiple non-dupsort key/value pairs by cursor.
* \ingroup c_crud * \ingroup c_crud
@ -5076,6 +5286,10 @@ mdbx_cursor_eof(const MDBX_cursor *cursor);
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
mdbx_cursor_on_first(const MDBX_cursor *cursor); 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 /** \brief Determines whether the cursor is pointed to the last key-value pair
* or not. * or not.
* \ingroup c_cursors * \ingroup c_cursors
@ -5090,6 +5304,10 @@ mdbx_cursor_on_first(const MDBX_cursor *cursor);
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
mdbx_cursor_on_last(const MDBX_cursor *cursor); 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 /** \addtogroup c_rqest
* \details \note The estimation result varies greatly depending on the filling * \details \note The estimation result varies greatly depending on the filling
* of specific pages and the overall balance of the b-tree: * 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_NOTHROW_PURE_FUNCTION LIBMDBX_API MDBX_hsr_func *
mdbx_env_get_hsr(const MDBX_env *env); mdbx_env_get_hsr(const MDBX_env *env);
/** \defgroup btree_traversal B-tree Traversal /** \defgroup chk Checking and Recovery
* This is internal API for mdbx_chk tool. You should avoid to use it, except * Basically this is internal API for `mdbx_chk` tool, etc.
* some extremal special cases. * You should avoid to use it, except some extremal special cases.
* \ingroup c_extra * \ingroup c_extra
* @{ */ * @{ */
/** \brief Page types for traverse the b-tree. /** \brief Acquires write-transaction lock.
* \see mdbx_env_pgwalk() \see MDBX_pgvisitor_func */ * Provided for custom and/or complex locking scenarios.
enum MDBX_page_type_t { * \returns A non-zero error value on failure and 0 on success. */
MDBX_page_broken, LIBMDBX_API int mdbx_txn_lock(MDBX_env *env, bool dont_wait);
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 Pseudo-name for MainDB */ /** \brief Releases write-transaction lock.
#define MDBX_PGWALK_MAIN ((void *)((ptrdiff_t)0)) * Provided for custom and/or complex locking scenarios.
/** \brief Pseudo-name for GarbageCollectorDB */ * \returns A non-zero error value on failure and 0 on success. */
#define MDBX_PGWALK_GC ((void *)((ptrdiff_t)-1)) LIBMDBX_API int mdbx_txn_unlock(MDBX_env *env);
/** \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 Open an environment instance using specific meta-page /** \brief Open an environment instance using specific meta-page
* for checking and recovery. * for checking and recovery.
@ -5594,7 +5785,236 @@ LIBMDBX_API int mdbx_env_open_for_recoveryW(MDBX_env *env,
* leg(s). */ * leg(s). */
LIBMDBX_API int mdbx_env_turn_for_recovery(MDBX_env *env, unsigned target_meta); 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 @} */ /** end of c_api @} */

940
mdbx.h++

File diff suppressed because it is too large Load Diff

View File

@ -48,6 +48,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <stdio.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> #include <valgrind/memcheck.h>
#ifndef VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE #ifndef VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE
/* LY: available since Valgrind 3.10 */ /* 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_ADDRESSABLE(a, s) (0)
#define VALGRIND_CHECK_MEM_IS_DEFINED(a, s) (0) #define VALGRIND_CHECK_MEM_IS_DEFINED(a, s) (0)
#define RUNNING_ON_VALGRIND (0) #define RUNNING_ON_VALGRIND (0)
#endif /* MDBX_USE_VALGRIND */ #endif /* ENABLE_MEMCHECK */
#ifdef __SANITIZE_ADDRESS__ #ifdef __SANITIZE_ADDRESS__
#include <sanitizer/asan_interface.h> #include <sanitizer/asan_interface.h>

View File

@ -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| | 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 | | 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 | | 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 | | 5 |0000 0020| |TXN_DRAINED_GC|INTEGERDUP|NODUPDATA | | |P_LEAF2 | |
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP | | 6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_OLDEN | |P_SUBP | |
7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | | | | 7 |0000 0080| | | |ALLDUPS |DBI_LINDO | | | |
8 |0000 0100| _MAY_MOVE | | | | | | | <= | 8 |0000 0100| _MAY_MOVE | | | | | | | <= |
9 |0000 0200| _MAY_UNMAP| | | | | | | <= | 9 |0000 0200| _MAY_UNMAP| | | | | | | <= |
10|0000 0400| | | | | | | | | 10|0000 0400| | | | | | | | |

View File

@ -5,7 +5,7 @@
/* clang-format off */ /* clang-format off */
#cmakedefine LTO_ENABLED #cmakedefine LTO_ENABLED
#cmakedefine MDBX_USE_VALGRIND #cmakedefine ENABLE_MEMCHECK
#cmakedefine ENABLE_GPROF #cmakedefine ENABLE_GPROF
#cmakedefine ENABLE_GCOV #cmakedefine ENABLE_GCOV
#cmakedefine ENABLE_ASAN #cmakedefine ENABLE_ASAN
@ -33,6 +33,8 @@
#cmakedefine01 MDBX_ENABLE_BIGFOOT #cmakedefine01 MDBX_ENABLE_BIGFOOT
#cmakedefine01 MDBX_ENABLE_PGOP_STAT #cmakedefine01 MDBX_ENABLE_PGOP_STAT
#cmakedefine01 MDBX_ENABLE_PROFGC #cmakedefine01 MDBX_ENABLE_PROFGC
#cmakedefine01 MDBX_ENABLE_DBI_SPARSE
#cmakedefine01 MDBX_ENABLE_DBI_LOCKFREE
/* Windows */ /* Windows */
#cmakedefine01 MDBX_WITHOUT_MSVC_CRT #cmakedefine01 MDBX_WITHOUT_MSVC_CRT

7210
src/core.c

File diff suppressed because it is too large Load Diff

View File

@ -93,6 +93,10 @@
disable : 5105) /* winbase.h(9531): warning C5105: macro expansion \ disable : 5105) /* winbase.h(9531): warning C5105: macro expansion \
producing 'defined' has undefined behavior */ producing 'defined' has undefined behavior */
#endif #endif
#if _MSC_VER < 1920
/* avoid "error C2219: syntax error: type qualifier must be after '*'" */
#define __restrict
#endif
#if _MSC_VER > 1930 #if _MSC_VER > 1930
#pragma warning(disable : 6235) /* <expression> is always a constant */ #pragma warning(disable : 6235) /* <expression> is always a constant */
#pragma warning(disable : 6237) /* <expression> is never evaluated and might \ #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) #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) \ #define PAGETYPE_COMPAT(p) \
(unlikely(PAGETYPE_WHOLE(p) & P_SUBP) \ (unlikely(PAGETYPE_WHOLE(p) & P_SUBP) \
? PAGETYPE_WHOLE(p) & ~(P_SUBP | P_LEGACY_DIRTY) \ ? PAGETYPE_WHOLE(p) & ~(P_SUBP | P_LEGACY_DIRTY) \
@ -812,7 +817,7 @@ typedef sem_t osal_ipclock_t;
#endif /* MDBX_LOCKING */ #endif /* MDBX_LOCKING */
#if MDBX_LOCKING > MDBX_LOCKING_SYSV && !defined(__cplusplus) #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); MDBX_INTERNAL_FUNC int osal_ipclock_destroy(osal_ipclock_t *ipc);
#endif /* MDBX_LOCKING */ #endif /* MDBX_LOCKING */
@ -1136,10 +1141,10 @@ typedef struct troika {
#if MDBX_WORDBITS > 32 /* Workaround for false-positives from Valgrind */ #if MDBX_WORDBITS > 32 /* Workaround for false-positives from Valgrind */
uint32_t unused_pad; uint32_t unused_pad;
#endif #endif
#define TROIKA_HAVE_STEADY(troika) ((troika)->fsm & 7) #define TROIKA_HAVE_STEADY(troika) ((troika)->fsm & 7u)
#define TROIKA_STRICT_VALID(troika) ((troika)->tail_and_flags & 64) #define TROIKA_STRICT_VALID(troika) ((troika)->tail_and_flags & 64u)
#define TROIKA_VALID(troika) ((troika)->tail_and_flags & 128) #define TROIKA_VALID(troika) ((troika)->tail_and_flags & 128u)
#define TROIKA_TAIL(troika) ((troika)->tail_and_flags & 3) #define TROIKA_TAIL(troika) ((troika)->tail_and_flags & 3u)
txnid_t txnid[NUM_METAS]; txnid_t txnid[NUM_METAS];
} meta_troika_t; } meta_troika_t;
@ -1169,6 +1174,8 @@ struct MDBX_txn {
#error "Oops, some txn flags overlapped or wrong" #error "Oops, some txn flags overlapped or wrong"
#endif #endif
uint32_t mt_flags; 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 */ MDBX_txn *mt_parent; /* parent of a nested txn */
/* Nested txn under this txn, set together with flag MDBX_TXN_HAS_CHILD */ /* Nested txn under this txn, set together with flag MDBX_TXN_HAS_CHILD */
@ -1186,31 +1193,30 @@ struct MDBX_txn {
txnid_t mt_front; txnid_t mt_front;
MDBX_env *mt_env; /* the DB environment */ 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 */ /* Array of MDBX_db records for each known DB */
MDBX_db *mt_dbs; MDBX_db *mt_dbs;
/* Array of sequence numbers for each DB handle */
MDBX_atomic_uint32_t *mt_dbiseqs;
/* Transaction DBI Flags */ #if MDBX_ENABLE_DBI_SPARSE
#define DBI_DIRTY MDBX_DBI_DIRTY /* DB was written in this txn */ unsigned *__restrict mt_dbi_sparse;
#define DBI_STALE MDBX_DBI_STALE /* Named-DB record is older than txnID */ #endif /* MDBX_ENABLE_DBI_SPARSE */
#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 */ /* Non-shared DBI state flags inside transaction */
#define DBI_VALID 0x10 /* DB handle is valid, see also DB_VALID */ #define DBI_DIRTY 0x01 /* DB was written in this txn */
#define DBI_USRVALID 0x20 /* As DB_VALID, but not set for FREE_DBI */ #define DBI_STALE 0x02 /* Named-DB record is older than txnID */
#define DBI_AUDITED 0x40 /* Internal flag for accounting during audit */ #define DBI_FRESH 0x04 /* Named-DB handle opened in this txn */
/* Array of flags for each DB */ #define DBI_CREAT 0x08 /* Named-DB handle created in this txn */
uint8_t *mt_dbistate; #define DBI_VALID 0x10 /* Handle is valid, see also DB_VALID */
/* Number of DB records in use, or 0 when the txn is finished. #define DBI_OLDEN 0x40 /* Handle was closed/reopened outside txn */
* This number only ever increments until the txn finishes; we #define DBI_LINDO 0x80 /* Lazy initialization done for DBI-slot */
* don't decrement it when individual DB handles are closed. */ /* Array of non-shared txn's flags of DBI */
MDBX_dbi mt_numdbs; uint8_t *__restrict mt_dbi_state;
size_t mt_owner; /* thread ID that owns this transaction */
/* Array of sequence numbers for each DB handle. */
uint32_t *__restrict mt_dbi_seqs;
MDBX_cursor **mt_cursors;
MDBX_canary mt_canary; MDBX_canary mt_canary;
void *mt_userctx; /* User-settable context */ void *mt_userctx; /* User-settable context */
MDBX_cursor **mt_cursors;
union { union {
struct { struct {
@ -1220,8 +1226,8 @@ struct MDBX_txn {
struct { struct {
meta_troika_t troika; meta_troika_t troika;
/* In write txns, array of cursors for each DB */ /* In write txns, array of cursors for each DB */
MDBX_PNL relist; /* Reclaimed GC pages */ MDBX_PNL __restrict relist; /* Reclaimed GC pages */
txnid_t last_reclaimed; /* ID of last used record */ txnid_t last_reclaimed; /* ID of last used record */
#if MDBX_ENABLE_REFUND #if MDBX_ENABLE_REFUND
pgno_t loose_refund_wl /* FIXME: describe */; pgno_t loose_refund_wl /* FIXME: describe */;
#endif /* MDBX_ENABLE_REFUND */ #endif /* MDBX_ENABLE_REFUND */
@ -1233,14 +1239,14 @@ struct MDBX_txn {
* dirtylist into mt_parent after freeing hidden mt_parent pages. */ * dirtylist into mt_parent after freeing hidden mt_parent pages. */
size_t dirtyroom; size_t dirtyroom;
/* For write txns: Modified pages. Sorted when not MDBX_WRITEMAP. */ /* For write txns: Modified pages. Sorted when not MDBX_WRITEMAP. */
MDBX_dpl *dirtylist; MDBX_dpl *__restrict dirtylist;
/* The list of reclaimed txns from GC */ /* 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. */ /* 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 /* The list of loose pages that became unused and may be reused
* in this transaction, linked through `mp_next`. */ * in this transaction, linked through `mp_next`. */
MDBX_page *loose_pages; MDBX_page *__restrict loose_pages;
/* Number of loose pages (tw.loose_pages) */ /* Number of loose pages (tw.loose_pages) */
size_t loose_count; size_t loose_count;
union { union {
@ -1249,11 +1255,12 @@ struct MDBX_txn {
/* The sorted list of dirty pages we temporarily wrote to disk /* The sorted list of dirty pages we temporarily wrote to disk
* because the dirty list was full. page numbers in here are * because the dirty list was full. page numbers in here are
* shifted left by 1, deleted slots have the LSB set. */ * shifted left by 1, deleted slots have the LSB set. */
MDBX_PNL list; MDBX_PNL __restrict list;
} spilled; } spilled;
size_t writemap_dirty_npages; size_t writemap_dirty_npages;
size_t writemap_spilled_npages; size_t writemap_spilled_npages;
}; };
uint64_t gc_time_acc;
} tw; } tw;
}; };
}; };
@ -1292,8 +1299,8 @@ struct MDBX_cursor {
MDBX_db *mc_db; MDBX_db *mc_db;
/* The database auxiliary record for this cursor */ /* The database auxiliary record for this cursor */
MDBX_dbx *mc_dbx; MDBX_dbx *mc_dbx;
/* The mt_dbistate for this database */ /* The mt_dbi_state[] for this DBI */
uint8_t *mc_dbistate; uint8_t *__restrict mc_dbi_state;
uint8_t mc_snum; /* number of pushed pages */ uint8_t mc_snum; /* number of pushed pages */
uint8_t mc_top; /* index of top page, normally mc_snum-1 */ 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_xcursor inner;
} MDBX_cursor_couple; } MDBX_cursor_couple;
struct mdbx_defer_free_item {
struct mdbx_defer_free_item *next;
uint64_t timestamp;
};
/* The database environment. */ /* The database environment. */
struct MDBX_env { struct MDBX_env {
/* ----------------------------------------------------- mostly static part */ /* ----------------------------------------------------- mostly static part */
@ -1363,6 +1375,7 @@ struct MDBX_env {
#define MDBX_DEPRECATED_COALESCE UINT32_C(0x2000000) #define MDBX_DEPRECATED_COALESCE UINT32_C(0x2000000)
#define ENV_INTERNAL_FLAGS (MDBX_FATAL_ERROR | MDBX_ENV_ACTIVE | MDBX_ENV_TXKEY) #define ENV_INTERNAL_FLAGS (MDBX_FATAL_ERROR | MDBX_ENV_ACTIVE | MDBX_ENV_TXKEY)
uint32_t me_flags; 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 */ osal_mmap_t me_dxb_mmap; /* The main data file */
#define me_map me_dxb_mmap.base #define me_map me_dxb_mmap.base
#define me_lazy_fd me_dxb_mmap.fd #define me_lazy_fd me_dxb_mmap.fd
@ -1375,7 +1388,6 @@ struct MDBX_env {
#define me_lfd me_lck_mmap.fd #define me_lfd me_lck_mmap.fd
struct MDBX_lockinfo *me_lck; 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_leaf_nodemax; /* max size of a leaf-node */
uint16_t me_branch_nodemax; /* max size of a branch-node */ uint16_t me_branch_nodemax; /* max size of a branch-node */
uint16_t me_subpage_limit; uint16_t me_subpage_limit;
@ -1393,13 +1405,15 @@ struct MDBX_env {
MDBX_dbi me_maxdbs; /* size of the DB table */ MDBX_dbi me_maxdbs; /* size of the DB table */
uint32_t me_pid; /* process ID of this env */ uint32_t me_pid; /* process ID of this env */
osal_thread_key_t me_txkey; /* thread-key for readers */ osal_thread_key_t me_txkey; /* thread-key for readers */
pathchar_t *me_pathname; /* path to the DB files */ struct { /* path to the DB files */
void *me_pbuf; /* scratch area for DUPSORT put() */ pathchar_t *lck, *dxb, *specified;
MDBX_txn *me_txn0; /* preallocated write transaction */ void *buffer;
} me_pathname;
MDBX_dbx *me_dbxs; /* array of static DB info */ void *me_pbuf; /* scratch area for DUPSORT put() */
uint16_t *me_dbflags; /* array of flags from MDBX_db.md_flags */ MDBX_txn *me_txn0; /* preallocated write transaction */
MDBX_atomic_uint32_t *me_dbiseqs; /* array of dbi sequence numbers */ 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 unsigned
me_maxgc_ov1page; /* Number of pgno_t fit in a single overflow page */ me_maxgc_ov1page; /* Number of pgno_t fit in a single overflow page */
unsigned me_maxgc_per_branch; unsigned me_maxgc_per_branch;
@ -1413,6 +1427,7 @@ struct MDBX_env {
unsigned rp_augment_limit; unsigned rp_augment_limit;
unsigned dp_limit; unsigned dp_limit;
unsigned dp_initial; unsigned dp_initial;
uint64_t gc_time_limit;
uint8_t dp_loose_limit; uint8_t dp_loose_limit;
uint8_t spill_max_denominator; uint8_t spill_max_denominator;
uint8_t spill_min_denominator; uint8_t spill_min_denominator;
@ -1422,6 +1437,8 @@ struct MDBX_env {
unsigned writethrough_threshold; unsigned writethrough_threshold;
#endif /* Windows */ #endif /* Windows */
bool prefault_write; bool prefault_write;
bool prefer_waf_insteadof_balance; /* Strive to minimize WAF instead of
balancing pages fullment */
union { union {
unsigned all; unsigned all;
/* tracks options with non-auto values but tuned by user */ /* tracks options with non-auto values but tuned by user */
@ -1451,20 +1468,23 @@ struct MDBX_env {
} me_sysv_ipc; } me_sysv_ipc;
#endif /* MDBX_LOCKING == MDBX_LOCKING_SYSV */ #endif /* MDBX_LOCKING == MDBX_LOCKING_SYSV */
bool me_incore; 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 */ /* --------------------------------------------------- mostly volatile part */
MDBX_txn *me_txn; /* current write transaction */ MDBX_txn *me_txn; /* current write transaction */
osal_fastmutex_t me_dbi_lock; osal_fastmutex_t me_dbi_lock;
MDBX_dbi me_numdbs; /* number of DBs opened */ unsigned me_numdbs; /* number of DBs opened */
bool me_prefault_write;
MDBX_page *me_dp_reserve; /* list of malloc'ed blocks for re-use */
unsigned me_dp_reserve_len; 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 */ /* 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; osal_ioring_t me_ioring;
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
@ -1482,13 +1502,12 @@ struct MDBX_env {
#if MDBX_DEBUG #if MDBX_DEBUG
MDBX_assert_func *me_assert_func; /* Callback for assertion failures */ MDBX_assert_func *me_assert_func; /* Callback for assertion failures */
#endif #endif
#ifdef MDBX_USE_VALGRIND #ifdef ENABLE_MEMCHECK
int me_valgrind_handle; int me_valgrind_handle;
#endif #endif
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__) #if defined(ENABLE_MEMCHECK) || defined(__SANITIZE_ADDRESS__)
MDBX_atomic_uint32_t me_ignore_EDEADLK;
pgno_t me_poison_edge; pgno_t me_poison_edge;
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */ #endif /* ENABLE_MEMCHECK || __SANITIZE_ADDRESS__ */
#ifndef xMDBX_DEBUG_SPILLING #ifndef xMDBX_DEBUG_SPILLING
#define xMDBX_DEBUG_SPILLING 0 #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, MDBX_INTERNAL_FUNC int cleanup_dead_readers(MDBX_env *env, int rlocked,
int *dead); 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 global_ctor(void);
MDBX_INTERNAL_FUNC void osal_ctor(void); MDBX_INTERNAL_FUNC void osal_ctor(void);
MDBX_INTERNAL_FUNC void global_dtor(void); MDBX_INTERNAL_FUNC void global_dtor(void);
@ -1666,7 +1681,8 @@ typedef struct MDBX_node {
/* mdbx_dbi_open() flags */ /* mdbx_dbi_open() flags */
#define DB_USABLE_FLAGS (DB_PERSISTENT_FLAGS | MDBX_CREATE | MDBX_DB_ACCEDE) #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 #define DB_INTERNAL_FLAGS DB_VALID
#if DB_INTERNAL_FLAGS & DB_USABLE_FLAGS #if DB_INTERNAL_FLAGS & DB_USABLE_FLAGS
@ -1792,3 +1808,33 @@ MDBX_MAYBE_UNUSED static void static_checks(void) {
(size_t)(size), __LINE__); \ (size_t)(size), __LINE__); \
ASAN_UNPOISON_MEMORY_REGION(addr, size); \ ASAN_UNPOISON_MEMORY_REGION(addr, size); \
} while (0) } 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);

View File

@ -120,7 +120,7 @@ mdbx_global_destructor(void) {
* - Блокировка таблицы читателей для регистрации, * - Блокировка таблицы читателей для регистрации,
* т.е. функции osal_rdt_lock() и osal_rdt_unlock(). * т.е. функции osal_rdt_lock() и osal_rdt_unlock().
* - Блокировка БД для пишущих транзакций, * - Блокировка БД для пишущих транзакций,
* т.е. функции mdbx_txn_lock() и mdbx_txn_unlock(). * т.е. функции osal_txn_lock() и osal_txn_unlock().
* *
* Остальной функционал реализуется отдельно посредством файловых блокировок: * Остальной функционал реализуется отдельно посредством файловых блокировок:
* - Первоначальный захват БД в режиме exclusive/shared и последующий перевод * - Первоначальный захват БД в режиме 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 #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 #if MDBX_LOCKING == MDBX_LOCKING_POSIX1988
return sem_init(ipc, false, 1) ? errno : 0; return sem_init(ipc, false, 1) ? errno : 0;
#elif MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \ #elif MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
@ -527,15 +527,42 @@ MDBX_INTERNAL_FUNC int osal_lck_downgrade(MDBX_env *env) {
return rc; return rc;
} }
__cold MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env, MDBX_INTERNAL_FUNC int osal_lck_upgrade(MDBX_env *env, bool dont_wait) {
MDBX_env *inprocess_neighbor) { assert(env->me_lfd != INVALID_HANDLE_VALUE);
if (unlikely(osal_getpid() != env->me_pid)) if (unlikely(osal_getpid() != env->me_pid))
return MDBX_PANIC; 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; int rc = MDBX_SUCCESS;
struct stat lck_info; struct stat lck_info;
MDBX_lockinfo *lck = env->me_lck_mmap.lck; MDBX_lockinfo *lck = env->me_lck;
if (env->me_lfd != INVALID_HANDLE_VALUE && !inprocess_neighbor && lck && if (lck && lck == env->me_lck_mmap.lck && !inprocess_neighbor &&
/* try get exclusive access */ /* try get exclusive access */
lck_op(env->me_lfd, op_setlk, F_WRLCK, 0, OFF_T_MAX) == 0 && lck_op(env->me_lfd, op_setlk, F_WRLCK, 0, OFF_T_MAX) == 0 &&
/* if LCK was not removed */ /* 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, (env->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK, 0,
OFF_T_MAX) == 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 MDBX_LOCKING == MDBX_LOCKING_SYSV
if (env->me_sysv_ipc.semid != -1) if (env->me_sysv_ipc.semid != -1)
rc = semctl(env->me_sysv_ipc.semid, 2, IPC_RMID) ? errno : 0; 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) { if (rc == 0) {
const bool synced = lck->mti_unsynced_pages.weak == 0; const bool synced = lck->mti_unsynced_pages.weak == 0;
osal_munmap(&env->me_lck_mmap); osal_munmap(&env->me_lck_mmap);
if (synced) if (synced && env->me_lfd != INVALID_HANDLE_VALUE)
rc = ftruncate(env->me_lfd, 0) ? errno : 0; rc = ftruncate(env->me_lfd, 0) ? errno : 0;
} }
jitter4testing(false); 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 /* 1) POSIX's fcntl() locks (i.e. when op_setlk == F_SETLK) should be restored
* after file was closed. * after file was closed.
* *
@ -761,7 +796,7 @@ bailout:
#endif /* MDBX_LOCKING > 0 */ #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) { const int err) {
int rc = err; int rc = err;
#if MDBX_LOCKING == MDBX_LOCKING_POSIX2008 || MDBX_LOCKING == MDBX_LOCKING_SYSV #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" #error "FIXME"
#endif /* MDBX_LOCKING */ #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)); ERROR("mutex (un)lock failed, %s", mdbx_strerror(err));
if (rc != EDEADLK) if (rc != EDEADLK)
env->me_flags |= MDBX_FATAL_ERROR; env->me_flags |= MDBX_FATAL_ERROR;
@ -852,7 +882,7 @@ MDBX_INTERNAL_FUNC int osal_check_tid4bionic(void) {
} }
#endif /* __ANDROID_API__ || ANDROID) || BIONIC */ #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) { const bool dont_wait) {
#if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \ #if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
MDBX_LOCKING == MDBX_LOCKING_POSIX2008 MDBX_LOCKING == MDBX_LOCKING_POSIX2008
@ -888,63 +918,87 @@ static int mdbx_ipclock_lock(MDBX_env *env, osal_ipclock_t *ipc,
#endif /* MDBX_LOCKING */ #endif /* MDBX_LOCKING */
if (unlikely(rc != MDBX_SUCCESS && rc != MDBX_BUSY)) if (unlikely(rc != MDBX_SUCCESS && rc != MDBX_BUSY))
rc = mdbx_ipclock_failed(env, ipc, rc); rc = osal_ipclock_failed(env, ipc, rc);
return 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 || \ #if MDBX_LOCKING == MDBX_LOCKING_POSIX2001 || \
MDBX_LOCKING == MDBX_LOCKING_POSIX2008 MDBX_LOCKING == MDBX_LOCKING_POSIX2008
int rc = pthread_mutex_unlock(ipc); err = pthread_mutex_unlock(ipc);
(void)env;
#elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988 #elif MDBX_LOCKING == MDBX_LOCKING_POSIX1988
int rc = sem_post(ipc) ? errno : MDBX_SUCCESS; err = sem_post(ipc) ? errno : MDBX_SUCCESS;
(void)env;
#elif MDBX_LOCKING == MDBX_LOCKING_SYSV #elif MDBX_LOCKING == MDBX_LOCKING_SYSV
if (unlikely(*ipc != (pid_t)env->me_pid)) if (unlikely(*ipc != (pid_t)env->me_pid))
return EPERM; err = EPERM;
*ipc = 0; else {
struct sembuf op = {.sem_num = (ipc != &env->me_lck->mti_wlock), *ipc = 0;
.sem_op = 1, struct sembuf op = {.sem_num = (ipc != &env->me_lck->mti_wlock),
.sem_flg = SEM_UNDO}; .sem_op = 1,
int rc = semop(env->me_sysv_ipc.semid, &op, 1) ? errno : MDBX_SUCCESS; .sem_flg = SEM_UNDO};
err = semop(env->me_sysv_ipc.semid, &op, 1) ? errno : MDBX_SUCCESS;
}
#else #else
#error "FIXME" #error "FIXME"
#endif /* MDBX_LOCKING */ #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; return rc;
} }
MDBX_INTERNAL_FUNC int osal_rdt_lock(MDBX_env *env) { MDBX_INTERNAL_FUNC int osal_rdt_lock(MDBX_env *env) {
TRACE("%s", ">>"); TRACE("%s", ">>");
jitter4testing(true); 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); TRACE("<< rc %d", rc);
return rc; return rc;
} }
MDBX_INTERNAL_FUNC void osal_rdt_unlock(MDBX_env *env) { MDBX_INTERNAL_FUNC void osal_rdt_unlock(MDBX_env *env) {
TRACE("%s", ">>"); TRACE("%s", ">>");
int rc = mdbx_ipclock_unlock(env, &env->me_lck->mti_rlock); int err = osal_ipclock_unlock(env, &env->me_lck->mti_rlock);
TRACE("<< rc %d", rc); TRACE("<< err %d", err);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(err != MDBX_SUCCESS))
mdbx_panic("%s() failed: err %d\n", __func__, rc); mdbx_panic("%s() failed: err %d\n", __func__, err);
jitter4testing(true); 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-" : "", ">>"); TRACE("%swait %s", dont_wait ? "dont-" : "", ">>");
jitter4testing(true); jitter4testing(true);
int rc = mdbx_ipclock_lock(env, &env->me_lck->mti_wlock, dont_wait); const int err = osal_ipclock_lock(env, &env->me_lck->mti_wlock, dont_wait);
TRACE("<< rc %d", rc); int rc = err;
return MDBX_IS_ERROR(rc) ? rc : MDBX_SUCCESS; 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", ">>"); TRACE("%s", ">>");
int rc = mdbx_ipclock_unlock(env, &env->me_lck->mti_wlock); eASSERT(env, env->me_txn0->mt_owner == osal_thread_self());
TRACE("<< rc %d", rc); env->me_txn0->mt_owner = 0;
if (unlikely(rc != MDBX_SUCCESS)) int err = osal_ipclock_unlock(env, &env->me_lck->mti_wlock);
mdbx_panic("%s() failed: err %d\n", __func__, rc); TRACE("<< err %d", err);
if (unlikely(err != MDBX_SUCCESS))
mdbx_panic("%s() failed: err %d\n", __func__, err);
jitter4testing(true); jitter4testing(true);
} }

View File

@ -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_BODY (env->me_psize * (size_t)NUM_METAS), DXB_MAXLEN
#define DXB_WHOLE 0, 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 (dontwait) {
if (!TryEnterCriticalSection(&env->me_windowsbug_lock)) if (!TryEnterCriticalSection(&env->me_windowsbug_lock))
return MDBX_BUSY; return MDBX_BUSY;
@ -190,16 +190,13 @@ int mdbx_txn_lock(MDBX_env *env, bool dontwait) {
0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */) 0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
? EXCEPTION_EXECUTE_HANDLER ? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH) { : EXCEPTION_CONTINUE_SEARCH) {
return ERROR_POSSIBLE_DEADLOCK; return MDBX_EDEADLK;
} }
} }
if (env->me_flags & MDBX_EXCLUSIVE) { eASSERT(env, !env->me_txn0->mt_owner);
/* Zap: Failing to release lock 'env->me_windowsbug_lock' if (env->me_flags & MDBX_EXCLUSIVE)
* in function 'mdbx_txn_lock' */ goto done;
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
return MDBX_SUCCESS;
}
const HANDLE fd4data = const HANDLE fd4data =
env->me_overlapped_fd ? env->me_overlapped_fd : env->me_lazy_fd; 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) { if (rc == MDBX_SUCCESS) {
done:
/* Zap: Failing to release lock 'env->me_windowsbug_lock' /* Zap: Failing to release lock 'env->me_windowsbug_lock'
* in function 'mdbx_txn_lock' */ * in function 'mdbx_txn_lock' */
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115); MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
return rc; env->me_txn0->mt_owner = osal_thread_self();
return MDBX_SUCCESS;
} }
LeaveCriticalSection(&env->me_windowsbug_lock); LeaveCriticalSection(&env->me_windowsbug_lock);
return (!dontwait || rc != ERROR_LOCK_VIOLATION) ? rc : MDBX_BUSY; 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) { if ((env->me_flags & MDBX_EXCLUSIVE) == 0) {
const HANDLE fd4data = const HANDLE fd4data =
env->me_overlapped_fd ? env->me_overlapped_fd : env->me_lazy_fd; 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) if (err != MDBX_SUCCESS)
mdbx_panic("%s failed: err %u", __func__, err); mdbx_panic("%s failed: err %u", __func__, err);
} }
env->me_txn0->mt_owner = 0;
LeaveCriticalSection(&env->me_windowsbug_lock); 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" * The osal_lck_downgrade() moves the locking-FSM from "exclusive write"
* state to the "used" (i.e. shared) state. * 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. * 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 */; 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) */ /* Transite from used state (S-?) to exclusive-write (E-E) */
assert(env->me_lfd != INVALID_HANDLE_VALUE); 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) */ /* 1) now on S-? (used), try S-E (locked) */
jitter4testing(false); 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) { if (rc != MDBX_SUCCESS) {
/* 2) something went wrong, give up */; /* 2) something went wrong, give up */;
VERBOSE("%s, err %u", "S-?(used) >> S-E(locked)", rc); 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) */ /* 4) now on ?-E (middle), try E-E (exclusive-write) */
jitter4testing(false); 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) { if (rc != MDBX_SUCCESS) {
/* 5) something went wrong, give up */; /* 5) something went wrong, give up */;
VERBOSE("%s, err %u", "?-E(middle) >> E-E(exclusive-write)", rc); 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_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 /* LY: should unmap before releasing the locks to avoid race condition and
* STATUS_USER_MAPPED_FILE/ERROR_USER_MAPPED_FILE */ * STATUS_USER_MAPPED_FILE/ERROR_USER_MAPPED_FILE */
if (env->me_map) 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; const bool synced = env->me_lck_mmap.lck->mti_unsynced_pages.weak == 0;
osal_munmap(&env->me_lck_mmap); osal_munmap(&env->me_lck_mmap);
if (synced && !inprocess_neighbor && env->me_lfd != INVALID_HANDLE_VALUE && 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) */ /* this will fail if LCK is used/mmapped by other process(es) */
osal_ftruncate(env->me_lfd, 0); osal_ftruncate(env->me_lfd, 0);
} }

View File

@ -1,6 +1,6 @@
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>. .\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" 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 .SH NAME
mdbx_chk \- MDBX checking tool mdbx_chk \- MDBX checking tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" 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 .SH NAME
mdbx_copy \- MDBX environment copy tool mdbx_copy \- MDBX environment copy tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -1,7 +1,7 @@
.\" Copyright 2021-2024 Leonid Yuriev <leo@yuriev.ru>. .\" Copyright 2021-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" 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 .SH NAME
mdbx_drop \- MDBX database delete tool mdbx_drop \- MDBX database delete tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" 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 .SH NAME
mdbx_dump \- MDBX environment export tool mdbx_dump \- MDBX environment export tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" 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 .SH NAME
mdbx_load \- MDBX environment import tool mdbx_load \- MDBX environment import tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>. .\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved. .\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE. .\" 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 .SH NAME
mdbx_stat \- MDBX environment status tool mdbx_stat \- MDBX environment status tool
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -271,6 +271,11 @@ namespace mdbx {
"into an incompatible memory allocation scheme."); "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() { [[noreturn]] __cold void throw_bad_value_size() {
throw bad_value_size(MDBX_BAD_VALSIZE); throw bad_value_size(MDBX_BAD_VALSIZE);
} }
@ -324,6 +329,8 @@ DEFINE_EXCEPTION(thread_mismatch)
DEFINE_EXCEPTION(transaction_full) DEFINE_EXCEPTION(transaction_full)
DEFINE_EXCEPTION(transaction_overlapping) DEFINE_EXCEPTION(transaction_overlapping)
DEFINE_EXCEPTION(duplicated_lck_file) DEFINE_EXCEPTION(duplicated_lck_file)
DEFINE_EXCEPTION(dangling_map_id)
#undef DEFINE_EXCEPTION #undef DEFINE_EXCEPTION
__cold const char *error::what() const noexcept { __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_full, MDBX_TXN_FULL);
CASE_EXCEPTION(transaction_overlapping, MDBX_TXN_OVERLAPPING); CASE_EXCEPTION(transaction_overlapping, MDBX_TXN_OVERLAPPING);
CASE_EXCEPTION(duplicated_lck_file, MDBX_DUPLICATED_CLK); CASE_EXCEPTION(duplicated_lck_file, MDBX_DUPLICATED_CLK);
CASE_EXCEPTION(dangling_map_id, MDBX_DANGLING_DBI);
#undef CASE_EXCEPTION #undef CASE_EXCEPTION
default: default:
if (is_mdbx_error()) if (is_mdbx_error())
@ -527,48 +535,48 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
} }
#ifdef MDBX_U128_TYPE #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?"); static_assert(sizeof(MDBX_U128_TYPE) == 16, "WTF?");
if (size() == 16) { if (size() == 16) {
MDBX_U128_TYPE r; MDBX_U128_TYPE r;
memcpy(&r, data(), sizeof(r)); memcpy(&r, data(), sizeof(r));
return r; return r;
} else } else
return as_uint64(); return as_uint64_adapt();
} }
#endif /* MDBX_U128_TYPE */ #endif /* MDBX_U128_TYPE */
uint64_t slice::as_uint64() const { uint64_t slice::as_uint64_adapt() const {
static_assert(sizeof(uint64_t) == 8, "WTF?"); static_assert(sizeof(uint64_t) == 8, "WTF?");
if (size() == 8) { if (size() == 8) {
uint64_t r; uint64_t r;
memcpy(&r, data(), sizeof(r)); memcpy(&r, data(), sizeof(r));
return r; return r;
} else } 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?"); static_assert(sizeof(uint32_t) == 4, "WTF?");
if (size() == 4) { if (size() == 4) {
uint32_t r; uint32_t r;
memcpy(&r, data(), sizeof(r)); memcpy(&r, data(), sizeof(r));
return r; return r;
} else } 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?"); static_assert(sizeof(uint16_t) == 2, "WTF?");
if (size() == 2) { if (size() == 2) {
uint16_t r; uint16_t r;
memcpy(&r, data(), sizeof(r)); memcpy(&r, data(), sizeof(r));
return r; return r;
} else } 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?"); static_assert(sizeof(uint8_t) == 1, "WTF?");
if (size() == 1) if (size() == 1)
return *static_cast<const uint8_t *>(data()); return *static_cast<const uint8_t *>(data());
@ -579,48 +587,48 @@ uint8_t slice::as_uint8() const {
} }
#ifdef MDBX_I128_TYPE #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?"); static_assert(sizeof(MDBX_I128_TYPE) == 16, "WTF?");
if (size() == 16) { if (size() == 16) {
MDBX_I128_TYPE r; MDBX_I128_TYPE r;
memcpy(&r, data(), sizeof(r)); memcpy(&r, data(), sizeof(r));
return r; return r;
} else } else
return as_int64(); return as_int64_adapt();
} }
#endif /* MDBX_I128_TYPE */ #endif /* MDBX_I128_TYPE */
int64_t slice::as_int64() const { int64_t slice::as_int64_adapt() const {
static_assert(sizeof(int64_t) == 8, "WTF?"); static_assert(sizeof(int64_t) == 8, "WTF?");
if (size() == 8) { if (size() == 8) {
uint64_t r; uint64_t r;
memcpy(&r, data(), sizeof(r)); memcpy(&r, data(), sizeof(r));
return r; return r;
} else } 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?"); static_assert(sizeof(int32_t) == 4, "WTF?");
if (size() == 4) { if (size() == 4) {
int32_t r; int32_t r;
memcpy(&r, data(), sizeof(r)); memcpy(&r, data(), sizeof(r));
return r; return r;
} else } 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?"); static_assert(sizeof(int16_t) == 2, "WTF?");
if (size() == 2) { if (size() == 2) {
int16_t r; int16_t r;
memcpy(&r, data(), sizeof(r)); memcpy(&r, data(), sizeof(r));
return r; return r;
} else } else
return as_int8(); return as_int8_adapt();
} }
int8_t slice::as_int8() const { int8_t slice::as_int8_adapt() const {
if (size() == 1) if (size() == 1)
return *static_cast<const int8_t *>(data()); return *static_cast<const int8_t *>(data());
else if (size() == 0) else if (size() == 0)
@ -1535,6 +1543,13 @@ void txn_managed::commit(commit_latency *latency) {
MDBX_CXX20_UNLIKELY err.throw_exception(); 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) { bool txn::drop_map(const char *name, bool throw_if_absent) {

File diff suppressed because it is too large Load Diff

View File

@ -505,7 +505,7 @@ static int equal_or_greater(const MDBX_val *a, const MDBX_val *b) {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
int i, rc; int i, err;
MDBX_env *env = nullptr; MDBX_env *env = nullptr;
MDBX_txn *txn = nullptr; MDBX_txn *txn = nullptr;
MDBX_cursor *mc = nullptr; MDBX_cursor *mc = nullptr;
@ -608,40 +608,45 @@ int main(int argc, char *argv[]) {
dbuf.iov_len = 4096; dbuf.iov_len = 4096;
dbuf.iov_base = osal_malloc(dbuf.iov_len); dbuf.iov_base = osal_malloc(dbuf.iov_len);
if (!dbuf.iov_base) { if (!dbuf.iov_base) {
rc = MDBX_ENOMEM; err = MDBX_ENOMEM;
error("value-buffer", rc); error("value-buffer", err);
goto env_close; goto bailout;
} }
/* read first header for mapsize= */ /* read first header for mapsize= */
if (!(mode & NOHDR)) { if (!(mode & NOHDR)) {
rc = readhdr(); err = readhdr();
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
if (rc == EOF) if (err == EOF)
rc = MDBX_ENODATA; err = MDBX_ENODATA;
error("readheader", rc); error("readheader", err);
goto env_close; goto bailout;
} }
} }
rc = mdbx_env_create(&env); err = mdbx_env_create(&env);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_env_create", rc); error("mdbx_env_create", err);
return EXIT_FAILURE; 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) { if (envinfo.mi_maxreaders) {
rc = mdbx_env_set_maxreaders(env, envinfo.mi_maxreaders); err = mdbx_env_set_maxreaders(env, envinfo.mi_maxreaders);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_env_set_maxreaders", rc); error("mdbx_env_set_maxreaders", err);
goto env_close; goto bailout;
} }
} }
if (envinfo.mi_geo.current | envinfo.mi_mapsize) { if (envinfo.mi_geo.current | envinfo.mi_mapsize) {
if (envinfo.mi_geo.current) { 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, 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.upper, (intptr_t)envinfo.mi_geo.shrink,
(intptr_t)envinfo.mi_geo.grow, (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 "Database size is too large for current system (mapsize=%" PRIu64
" is great than system-limit %zu)\n", " is great than system-limit %zu)\n",
envinfo.mi_mapsize, (size_t)MAX_MAPSIZE); 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, env, (intptr_t)envinfo.mi_mapsize, (intptr_t)envinfo.mi_mapsize,
(intptr_t)envinfo.mi_mapsize, 0, 0, (intptr_t)envinfo.mi_mapsize, 0, 0,
envinfo.mi_dxb_pagesize ? (intptr_t)envinfo.mi_dxb_pagesize : -1); envinfo.mi_dxb_pagesize ? (intptr_t)envinfo.mi_dxb_pagesize : -1);
} }
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_env_set_geometry", rc); error("mdbx_env_set_geometry", err);
goto env_close; goto bailout;
} }
} }
rc = mdbx_env_open(env, envname, envflags, 0664); err = mdbx_env_open(env, envname, envflags, 0664);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_env_open", rc); error("mdbx_env_open", err);
goto env_close; goto bailout;
} }
kbuf.iov_len = mdbx_env_get_maxvalsize_ex(env, 0) + (size_t)1; 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) if (!quiet)
fprintf(stderr, "mdbx_env_get_maxkeysize() failed, returns %zu\n", fprintf(stderr, "mdbx_env_get_maxkeysize() failed, returns %zu\n",
kbuf.iov_len); kbuf.iov_len);
goto env_close; goto bailout;
} }
kbuf.iov_base = malloc(kbuf.iov_len); kbuf.iov_base = malloc(kbuf.iov_len);
if (!kbuf.iov_base) { if (!kbuf.iov_base) {
rc = MDBX_ENOMEM; err = MDBX_ENOMEM;
error("key-buffer", rc); error("key-buffer", err);
goto env_close; goto bailout;
} }
while (rc == MDBX_SUCCESS) { while (err == MDBX_SUCCESS) {
if (user_break) { if (user_break) {
rc = MDBX_EINTR; err = MDBX_EINTR;
break; break;
} }
rc = mdbx_txn_begin(env, nullptr, 0, &txn); err = mdbx_txn_begin(env, nullptr, 0, &txn);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_txn_begin", rc); error("mdbx_txn_begin", err);
goto env_close; goto bailout;
} }
if (mode & GLOBAL) { if (mode & GLOBAL) {
mode -= GLOBAL; mode -= GLOBAL;
if (canary.v | canary.x | canary.y | canary.z) { if (canary.v | canary.x | canary.y | canary.z) {
rc = mdbx_canary_put(txn, &canary); err = mdbx_canary_put(txn, &canary);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_canary_put", rc); error("mdbx_canary_put", err);
goto txn_abort; goto bailout;
} }
} }
} }
const char *const dbi_name = subname ? subname : "@MAIN"; const char *const dbi_name = subname ? subname : "@MAIN";
rc = err =
mdbx_dbi_open_ex(txn, subname, dbi_flags | MDBX_CREATE, &dbi, 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,
(putflags & MDBX_APPEND) ? equal_or_greater : nullptr); (putflags & MDBX_APPEND) ? equal_or_greater : nullptr);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_dbi_open_ex", rc); error("mdbx_dbi_open_ex", err);
goto txn_abort; goto bailout;
} }
uint64_t present_sequence; uint64_t present_sequence;
rc = mdbx_dbi_sequence(txn, dbi, &present_sequence, 0); err = mdbx_dbi_sequence(txn, dbi, &present_sequence, 0);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_dbi_sequence", rc); error("mdbx_dbi_sequence", err);
goto txn_abort; goto bailout;
} }
if (present_sequence > sequence) { if (present_sequence > sequence) {
if (!quiet) if (!quiet)
@ -733,22 +738,22 @@ int main(int argc, char *argv[]) {
"present sequence for '%s' value (%" PRIu64 "present sequence for '%s' value (%" PRIu64
") is greater than loaded (%" PRIu64 ")\n", ") is greater than loaded (%" PRIu64 ")\n",
dbi_name, present_sequence, sequence); dbi_name, present_sequence, sequence);
rc = MDBX_RESULT_TRUE; err = MDBX_RESULT_TRUE;
goto txn_abort; goto bailout;
} }
if (present_sequence < sequence) { if (present_sequence < sequence) {
rc = mdbx_dbi_sequence(txn, dbi, nullptr, sequence - present_sequence); err = mdbx_dbi_sequence(txn, dbi, nullptr, sequence - present_sequence);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_dbi_sequence", rc); error("mdbx_dbi_sequence", err);
goto txn_abort; goto bailout;
} }
} }
if (purge) { if (purge) {
rc = mdbx_drop(txn, dbi, false); err = mdbx_drop(txn, dbi, false);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_drop", rc); error("mdbx_drop", err);
goto txn_abort; goto bailout;
} }
} }
@ -756,85 +761,85 @@ int main(int argc, char *argv[]) {
putflags = (dbi_flags & MDBX_DUPSORT) ? putflags | MDBX_APPENDDUP putflags = (dbi_flags & MDBX_DUPSORT) ? putflags | MDBX_APPENDDUP
: putflags & ~MDBX_APPENDDUP; : putflags & ~MDBX_APPENDDUP;
rc = mdbx_cursor_open(txn, dbi, &mc); err = mdbx_cursor_open(txn, dbi, &mc);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_cursor_open", rc); error("mdbx_cursor_open", err);
goto txn_abort; goto bailout;
} }
int batch = 0; int batch = 0;
while (rc == MDBX_SUCCESS) { while (err == MDBX_SUCCESS) {
MDBX_val key, data; MDBX_val key, data;
rc = readline(&key, &kbuf); err = readline(&key, &kbuf);
if (rc == EOF) if (err == EOF)
break; break;
if (rc == MDBX_SUCCESS) if (err == MDBX_SUCCESS)
rc = readline(&data, &dbuf); err = readline(&data, &dbuf);
if (rc) { if (err) {
if (!quiet) if (!quiet)
fprintf(stderr, "%s: line %" PRIiSIZE ": failed to read key value\n", fprintf(stderr, "%s: line %" PRIiSIZE ": failed to read key value\n",
prog, lineno); prog, lineno);
goto txn_abort; goto bailout;
} }
rc = mdbx_cursor_put(mc, &key, &data, putflags); err = mdbx_cursor_put(mc, &key, &data, putflags);
if (rc == MDBX_KEYEXIST && putflags) if (err == MDBX_KEYEXIST && putflags)
continue; continue;
if (rc == MDBX_BAD_VALSIZE && rescue) { if (err == MDBX_BAD_VALSIZE && rescue) {
if (!quiet) if (!quiet)
fprintf(stderr, "%s: skip line %" PRIiSIZE ": due %s\n", prog, lineno, fprintf(stderr, "%s: skip line %" PRIiSIZE ": due %s\n", prog, lineno,
mdbx_strerror(rc)); mdbx_strerror(err));
continue; continue;
} }
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_cursor_put", rc); error("mdbx_cursor_put", err);
goto txn_abort; goto bailout;
} }
batch++; batch++;
MDBX_txn_info txn_info; MDBX_txn_info txn_info;
rc = mdbx_txn_info(txn, &txn_info, false); err = mdbx_txn_info(txn, &txn_info, false);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_txn_info", rc); error("mdbx_txn_info", err);
goto txn_abort; goto bailout;
} }
if (batch == 10000 || txn_info.txn_space_dirty > MEGABYTE * 256) { if (batch == 10000 || txn_info.txn_space_dirty > MEGABYTE * 256) {
rc = mdbx_txn_commit(txn); err = mdbx_txn_commit(txn);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_txn_commit", rc); error("mdbx_txn_commit", err);
goto env_close; goto bailout;
} }
batch = 0; batch = 0;
rc = mdbx_txn_begin(env, nullptr, 0, &txn); err = mdbx_txn_begin(env, nullptr, 0, &txn);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_txn_begin", rc); error("mdbx_txn_begin", err);
goto env_close; goto bailout;
} }
rc = mdbx_cursor_bind(txn, mc, dbi); err = mdbx_cursor_bind(txn, mc, dbi);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_cursor_bind", rc); error("mdbx_cursor_bind", err);
goto txn_abort; goto bailout;
} }
} }
} }
mdbx_cursor_close(mc); mdbx_cursor_close(mc);
mc = nullptr; mc = nullptr;
rc = mdbx_txn_commit(txn); err = mdbx_txn_commit(txn);
txn = nullptr; txn = nullptr;
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_txn_commit", rc); error("mdbx_txn_commit", err);
goto env_close; goto bailout;
} }
if (subname) { if (subname) {
assert(dbi != MAIN_DBI); assert(dbi != MAIN_DBI);
rc = mdbx_dbi_close(env, dbi); err = mdbx_dbi_close(env, dbi);
if (unlikely(rc != MDBX_SUCCESS)) { if (unlikely(err != MDBX_SUCCESS)) {
error("mdbx_dbi_close", rc); error("mdbx_dbi_close", err);
goto env_close; goto bailout;
} }
} else { } else {
assert(dbi == MAIN_DBI); assert(dbi == MAIN_DBI);
@ -842,14 +847,14 @@ int main(int argc, char *argv[]) {
/* try read next header */ /* try read next header */
if (!(mode & NOHDR)) if (!(mode & NOHDR))
rc = readhdr(); err = readhdr();
else if (ferror(stdin) || feof(stdin)) else if (ferror(stdin) || feof(stdin))
break; break;
} }
switch (rc) { switch (err) {
case EOF: case EOF:
rc = MDBX_SUCCESS; err = MDBX_SUCCESS;
case MDBX_SUCCESS: case MDBX_SUCCESS:
break; break;
case MDBX_EINTR: case MDBX_EINTR:
@ -857,17 +862,19 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Interrupted by signal/user\n"); fprintf(stderr, "Interrupted by signal/user\n");
break; break;
default: default:
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(err != MDBX_SUCCESS))
error("readline", rc); error("readline", err);
} }
txn_abort: bailout:
mdbx_cursor_close(mc); if (mc)
mdbx_txn_abort(txn); mdbx_cursor_close(mc);
env_close: if (txn)
mdbx_env_close(env); mdbx_txn_abort(txn);
if (env)
mdbx_env_close(env);
free(kbuf.iov_base); free(kbuf.iov_base);
free(dbuf.iov_base); free(dbuf.iov_base);
return rc ? EXIT_FAILURE : EXIT_SUCCESS; return err ? EXIT_FAILURE : EXIT_SUCCESS;
} }

View File

@ -164,6 +164,20 @@
#error MDBX_AVOID_MSYNC must be defined as 0 or 1 #error MDBX_AVOID_MSYNC must be defined as 0 or 1
#endif /* MDBX_AVOID_MSYNC */ #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. /** Controls sort order of internal page number lists.
* This mostly experimental/advanced option with not for regular MDBX users. * This mostly experimental/advanced option with not for regular MDBX users.
* \warning The database format depend on this option and libmdbx built with * \warning The database format depend on this option and libmdbx built with
@ -211,8 +225,8 @@
/** If defined then enables integration with Valgrind, /** If defined then enables integration with Valgrind,
* a memory analyzing tool. */ * a memory analyzing tool. */
#ifndef MDBX_USE_VALGRIND #ifndef ENABLE_MEMCHECK
#endif /* MDBX_USE_VALGRIND */ #endif /* ENABLE_MEMCHECK */
/** If defined then enables use C11 atomics, /** If defined then enables use C11 atomics,
* otherwise detects ones availability automatically. */ * otherwise detects ones availability automatically. */

View File

@ -503,8 +503,18 @@ MDBX_INTERNAL_FUNC int osal_fastmutex_init(osal_fastmutex_t *fastmutex) {
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
InitializeCriticalSection(fastmutex); InitializeCriticalSection(fastmutex);
return MDBX_SUCCESS; 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 #else
return pthread_mutex_init(fastmutex, NULL); return pthread_mutex_init(fastmutex, nullptr);
#endif #endif
} }
@ -526,7 +536,7 @@ MDBX_INTERNAL_FUNC int osal_fastmutex_acquire(osal_fastmutex_t *fastmutex) {
0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */) 0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
? EXCEPTION_EXECUTE_HANDLER ? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH) { : EXCEPTION_CONTINUE_SEARCH) {
return ERROR_POSSIBLE_DEADLOCK; return MDBX_EDEADLK;
} }
return MDBX_SUCCESS; return MDBX_SUCCESS;
#else #else

View File

@ -690,7 +690,8 @@ MDBX_INTERNAL_FUNC int osal_lck_init(MDBX_env *env,
/// restore POSIX-fcntl locks after the closing of file descriptors. /// restore POSIX-fcntl locks after the closing of file descriptors.
/// \return Error code (MDBX_PANIC) or zero on success. /// \return Error code (MDBX_PANIC) or zero on success.
MDBX_INTERNAL_FUNC int osal_lck_destroy(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);
/// \brief Connects to shared interprocess locking objects and tries to acquire /// \brief Connects to shared interprocess locking objects and tries to acquire
/// the maximum lock level (shared if exclusive is not available) /// 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. /// operational lock.
/// \return Error code or zero on success /// \return Error code or zero on success
MDBX_INTERNAL_FUNC int osal_lck_downgrade(MDBX_env *env); 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. /// \brief Locks LCK-file or/and table of readers for (de)registering.
/// \return Error code or zero on success /// \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. /// \brief Unlocks LCK-file or/and table of readers after (de)registering.
MDBX_INTERNAL_FUNC void osal_rdt_unlock(MDBX_env *env); MDBX_INTERNAL_FUNC void osal_rdt_unlock(MDBX_env *env);
/// \brief Acquires lock for DB change (on writing transaction start) /// \brief Acquires write-transaction lock.
/// Reading transactions will not be blocked.
/// Declared as LIBMDBX_API because it is used in mdbx_chk.
/// \return Error code or zero on success /// \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 /// \brief Releases write-transaction lock..
/// has finished). MDBX_INTERNAL_FUNC void osal_txn_unlock(MDBX_env *env);
/// Declared as LIBMDBX_API because it is used in mdbx_chk.
LIBMDBX_API void mdbx_txn_unlock(MDBX_env *env);
/// \brief Sets alive-flag of reader presence (indicative lock) for PID of /// \brief Sets alive-flag of reader presence (indicative lock) for PID of
/// the current process. The function does no more than needed for /// the current process. The function does no more than needed for

View File

@ -28,6 +28,7 @@ set(LIBMDBX_TEST_SOURCES
append.c++ append.c++
ttl.c++ ttl.c++
nested.c++ nested.c++
fork.c++
) )
if(NOT MDBX_BUILD_CXX) if(NOT MDBX_BUILD_CXX)
@ -91,6 +92,20 @@ if(UNIX AND NOT SUBPROJECT)
set_target_properties(test_extra_dupfixed_multiple PROPERTIES set_target_properties(test_extra_dupfixed_multiple PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON) CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
endif() 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()
endif() endif()
@ -107,7 +122,7 @@ else()
add_test(NAME smoke COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test add_test(NAME smoke COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test
--loglevel=verbose --loglevel=verbose
--keygen.seed=${test_seed} --prng-seed=${test_seed}
--progress --console=no --pathname=smoke.db --dont-cleanup-after basic) --progress --console=no --pathname=smoke.db --dont-cleanup-after basic)
set_tests_properties(smoke PROPERTIES set_tests_properties(smoke PROPERTIES
TIMEOUT 600 TIMEOUT 600
@ -129,11 +144,11 @@ else()
add_test(NAME dupsort_writemap COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test add_test(NAME dupsort_writemap COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test
--loglevel=notice --loglevel=notice
--keygen.seed=${test_seed} --prng-seed=${test_seed}
--table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --table=+data.fixed --keygen.split=29 --datalen=rnd --progress --console=no
--repeat=2 --pathname=dupsort_writemap.db --dont-cleanup-after basic) --repeat=2 --pathname=dupsort_writemap.db --dont-cleanup-after basic)
set_tests_properties(dupsort_writemap PROPERTIES set_tests_properties(dupsort_writemap PROPERTIES
TIMEOUT 600 TIMEOUT 3600
RUN_SERIAL OFF) RUN_SERIAL OFF)
if(MDBX_BUILD_TOOLS) if(MDBX_BUILD_TOOLS)
add_test(NAME dupsort_writemap_chk COMMAND ${MDBX_OUTPUT_DIR}/mdbx_chk -nvvwc dupsort_writemap.db) 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) if(MDBX_BUILD_CXX)
add_test(NAME extra_maindb_ordinal COMMAND test_extra_maindb_ordinal) 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_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()
endif() endif()

View File

@ -20,8 +20,8 @@ public:
: testcase(config, pid) {} : testcase(config, pid) {}
bool run() override; bool run() override;
static bool review_params(actor_params &params) { static bool review_params(actor_params &params, unsigned space_id) {
if (!testcase::review_params(params)) if (!testcase::review_params(params, space_id))
return false; return false;
const bool ordered = !flipcoin_x3(); const bool ordered = !flipcoin_x3();
log_notice("the '%s' key-generation mode is selected", log_notice("the '%s' key-generation mode is selected",
@ -45,7 +45,7 @@ bool testcase_append::run() {
} }
cursor_open(dbi); cursor_open(dbi);
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */); keyvalue_maker.setup(config.params, 0 /* thread_number */);
/* LY: тест наполнения таблиц в append-режиме, /* LY: тест наполнения таблиц в append-режиме,
* при котором записи добавляются строго в конец (в порядке сортировки) */ * при котором записи добавляются строго в конец (в порядке сортировки) */
const MDBX_put_flags_t flags = const MDBX_put_flags_t flags =

View File

@ -41,8 +41,9 @@ testcase *registry::create_actor(const actor_config &config,
} }
bool registry::review_actor_params(const actor_testcase id, bool registry::review_actor_params(const actor_testcase id,
actor_params &params) { actor_params &params,
return instance()->id2record.at(id)->review_params(params); 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); 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) if (space_id > ACTOR_ID_MAX)
failure("Invalid space-id %lu\n", space_id); 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, log_trace("configure_actor: space %lu for %s", space_id,
testcase2str(testcase)); testcase2str(testcase));
global::actors.emplace_back( global::actors.emplace_back(
@ -105,6 +106,10 @@ void testcase_setup(const char *casename, const actor_params &params,
configure_actor(last_space_id, ac_try, nullptr, params); configure_actor(last_space_id, ac_try, nullptr, params);
configure_actor(last_space_id, ac_jitter, nullptr, params); configure_actor(last_space_id, ac_jitter, nullptr, params);
configure_actor(last_space_id, ac_try, 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); log_notice("<<< testcase_setup(%s): done", casename);
} else { } else {
failure("unknown testcase `%s`", casename); failure("unknown testcase `%s`", casename);

View File

@ -145,6 +145,16 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
return true; 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; char *suffix = nullptr;
errno = 0; errno = 0;
unsigned long long raw = strtoull(value_cstr, &suffix, 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; uint64_t multiplier = 1;
if (suffix && *suffix) { 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", failure("Option '--%s' doesn't accepts suffixes, so '%s' is unexpected\n",
option, suffix); option, suffix);
if (strcmp(suffix, "K") == 0 || strcasecmp(suffix, "Kilo") == 0) 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) if (value < minval)
failure("The minimal value for option '--%s' is %" PRIu64 "\n", option, failure("The minimal value for option '--%s' is %" PRIu64 "\n", option,
minval); minval);
if (scale == intkey)
value &= ~3u;
return true; return true;
} }
@ -422,6 +434,7 @@ void dump(const char *title) {
log_verbose("#%u, testcase %s, space_id/table %u\n", i->actor_id, log_verbose("#%u, testcase %s, space_id/table %u\n", i->actor_id,
testcase2str(i->testcase), i->space_id); testcase2str(i->testcase), i->space_id);
indent.push(); indent.push();
log_verbose("prng-seed: %u\n", i->params.prng_seed);
if (i->params.loglevel) { if (i->params.loglevel) {
log_verbose("log: level %u, %s\n", 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.mesh, i->params.keygen.rotate, i->params.keygen.offset,
i->params.keygen.split, i->params.keygen.split,
i->params.keygen.width - 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", log_verbose("keygen.zerofill: %s\n",
i->params.keygen.zero_fill ? "Yes" : "No"); i->params.keygen.zero_fill ? "Yes" : "No");
log_verbose("key: minlen %u, maxlen %u\n", i->params.keylen_min, 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 { 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 { 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 { 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 { unsigned actor_params::mdbx_datalen_max() const {

View File

@ -25,6 +25,10 @@ enum actor_testcase {
ac_hill, ac_hill,
ac_deadread, ac_deadread,
ac_deadwrite, ac_deadwrite,
#if !defined(_WIN32) && !defined(_WIN64)
ac_forkread,
ac_forkwrite,
#endif /* Windows */
ac_jitter, ac_jitter,
ac_try, ac_try,
ac_copy, ac_copy,
@ -59,7 +63,7 @@ const char *keygencase2str(const keygen_case);
namespace config { 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, bool parse_option(int argc, char *const argv[], int &narg, const char *option,
const char **value, const char *default_value = nullptr); const char **value, const char *default_value = nullptr);
@ -270,6 +274,7 @@ struct actor_params_pod {
unsigned batch_read{0}; unsigned batch_read{0};
unsigned batch_write{0}; unsigned batch_write{0};
unsigned prng_seed{0};
unsigned delaystart{0}; unsigned delaystart{0};
unsigned waitfor_nops{0}; unsigned waitfor_nops{0};
unsigned inject_writefaultn{0}; unsigned inject_writefaultn{0};

View 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;
}

View 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;
}

View File

@ -34,7 +34,7 @@
#define IP_PRINTF_ARG_HOST(addr) \ #define IP_PRINTF_ARG_HOST(addr) \
(int)((addr) >> 24), (int)((addr) >> 16 & 0xff), (int)((addr) >> 8 & 0xff), \ (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"; char opt_db_path[PATH_MAX] = "./mdbx_bench2";
static MDBX_env *env; static MDBX_env *env;

297
test/fork.c++ Normal file
View 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 */

View File

@ -52,7 +52,7 @@ bool testcase_hill::run() {
speculum_committed.clear(); speculum_committed.clear();
/* TODO: работа в несколько потоков */ /* 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_key = keygen::alloc(config.params.keylen_max);
keygen::buffer a_data_0 = keygen::alloc(config.params.datalen_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); 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, log_trace("uphill: insert-a (age %" PRIu64 ") %" PRIu64, age_shift,
a_serial); a_serial);
generate_pair(a_serial, a_key, a_data_1, age_shift); generate_pair(a_serial, a_key, a_data_1, age_shift);
@ -302,7 +302,7 @@ bool testcase_hill::run() {
assert(b_serial > a_serial); 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, log_trace("downhill: update-a (age 0->%" PRIu64 ") %" PRIu64, age_shift,
a_serial); a_serial);
generate_pair(a_serial, a_key, a_data_0, 0); generate_pair(a_serial, a_key, a_data_0, 0);

View File

@ -39,6 +39,12 @@ bool testcase_jitter::run() {
if (upper_limit < 1) if (upper_limit < 1)
upper_limit = config.params.size_now * 2; 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()) { while (should_continue()) {
jitter_delay(); jitter_delay();
db_open(); db_open();
@ -48,6 +54,15 @@ bool testcase_jitter::run() {
txn_begin(false); txn_begin(false);
dbi = db_table_open(true); dbi = db_table_open(true);
check_dbi_error(MDBX_SUCCESS, "created-uncommitted"); 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 // note: here and below the 4-byte length keys and value are used
// to be compatible with any Db-flags given from command line. // to be compatible with any Db-flags given from command line.
MDBX_val k = {(void *)"k000", 4}, v = {(void *)"v001", 4}; MDBX_val k = {(void *)"k000", 4}, v = {(void *)"v001", 4};
@ -75,7 +90,17 @@ bool testcase_jitter::run() {
failure_perror("jitter.put-2", err); failure_perror("jitter.put-2", err);
check_dbi_error(MDBX_BAD_DBI, "dropped-recreated-aborted"); check_dbi_error(MDBX_BAD_DBI, "dropped-recreated-aborted");
// restore DBI // 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"); check_dbi_error(MDBX_SUCCESS, "dropped-recreated-aborted+reopened");
v = {(void *)"v003", 4}; v = {(void *)"v003", 4};
err = mdbx_put(txn_guard.get(), dbi, &k, &v, MDBX_UPSERT); err = mdbx_put(txn_guard.get(), dbi, &k, &v, MDBX_UPSERT);

View File

@ -14,6 +14,39 @@
#include "test.h++" #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 { namespace keygen {
/* LY: https://en.wikipedia.org/wiki/Injective_function */ /* 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, 10, 14, 22, 19, 3, 21, 18, 19, 26, 24, 2, 21, 25, 29, 24, 10, 11, 14, 20,
19}; 19};
const auto mask = actor_params::serial_mask(bits);
const auto mult = m[bits - 8]; const auto mult = m[bits - 8];
const auto shift = s[bits - 8]; const auto shift = s[bits - 8];
serial_t result = serial * mult; serial_t result = serial * mult;
if (salt) { if (salt) {
const unsigned left = bits / 2; const unsigned left = bits / 2;
const unsigned right = bits - left; const unsigned right = bits - left;
result = (result << left) | result = (result << left) | ((result & mask) >> right);
((result & actor_params::serial_mask(bits)) >> right);
result = (result ^ salt) * mult; result = (result ^ salt) * mult;
} }
result ^= result << shift; result ^= (result & mask) >> shift;
result &= actor_params::serial_mask(bits); result &= mask;
log_trace("keygen-injective: serial %" PRIu64 "/%u @%" PRIx64 ",%u,%" PRIu64 log_trace("keygen-injective: serial %" PRIu64 "/%u @%" PRIx64 ",%u,%" PRIu64
" => %" PRIu64 "/%u", " => %" PRIu64 "/%u",
serial, bits, mult, shift, salt, result, bits); 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)))); unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT))));
assert(!(value_essentials.flags & assert(!(value_essentials.flags &
~(essentials::prng_fill_flag | ~(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, log_trace("keygen-pair: serial %" PRIu64 ", data-age %" PRIu64, serial,
value_age); 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 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 (mapping.split) {
if (MDBX_db_flags_t(key_essentials.flags) & MDBX_DUPSORT) { if (MDBX_db_flags_t(key_essentials.flags) & MDBX_DUPSORT) {
key_serial >>= mapping.split; 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); actor_params::serial_mask(mapping.split);
} }
value_serial |= value_age << mapping.split;
log_trace("keygen-pair: split@%u => k%" PRIu64 ", v%" PRIu64, mapping.split, log_trace("keygen-pair: split@%u => k%" PRIu64 ", v%" PRIu64, mapping.split,
key_serial, value_serial); key_serial, value_serial);
} }
log_trace("keygen-pair: key %" PRIu64 ", value %" PRIu64, key_serial, log_trace("keygen-pair: key %" PRIu64 ", value %" PRIu64, key_serial,
value_serial); value_serial);
mk_begin(key_serial, key_essentials, *key); key_serial = mk_begin(key_serial, key_essentials, *key);
mk_begin(value_serial, value_essentials, *value); value_serial = mk_begin(value_serial, value_essentials, *value);
#if 0 /* unused for now */ #if 0 /* unused for now */
if (key->value.iov_len + value->value.iov_len > pair_maxlen) { 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); 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) { unsigned thread_number) {
#if CONSTEXPR_ENUM_FLAGS_OPERATIONS #if CONSTEXPR_ENUM_FLAGS_OPERATIONS
static_assert(unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT | static_assert(unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT |
MDBX_INTEGERDUP | MDBX_REVERSEDUP) < UINT16_MAX, MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP) <
UINT16_MAX,
"WTF?"); "WTF?");
#else #else
assert(unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT | assert(unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT |
MDBX_INTEGERDUP | MDBX_REVERSEDUP) < UINT16_MAX); MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP) <
UINT16_MAX);
#endif #endif
key_essentials.flags = uint16_t( key_essentials.flags = uint16_t(
actor.table_flags & actor.table_flags &
MDBX_db_flags_t(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT)); MDBX_db_flags_t(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT));
assert(actor.keylen_min <= UINT16_MAX); assert(actor.keylen_min <= UINT16_MAX);
key_essentials.minlen = uint16_t(actor.keylen_min); key_essentials.minlen = uint16_t(actor.keylen_min);
assert(actor.keylen_max <= UINT32_MAX); assert(actor.keylen_max <= UINT32_MAX);
key_essentials.maxlen = key_essentials.maxlen = std::min(
std::min(uint32_t(actor.keylen_max), uint32_t(actor.keylen_max),
uint32_t(mdbx_limits_keysize_max( uint32_t(mdbx_limits_keysize_max(actor.pagesize, actor.table_flags)));
actor.pagesize, MDBX_db_flags_t(key_essentials.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( 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); assert(actor.datalen_min <= UINT16_MAX);
value_essentials.minlen = uint16_t(actor.datalen_min); value_essentials.minlen = uint16_t(actor.datalen_min);
assert(actor.datalen_max <= UINT32_MAX); assert(actor.datalen_max <= UINT32_MAX);
value_essentials.maxlen = value_essentials.maxlen = std::min(
std::min(uint32_t(actor.datalen_max), uint32_t(actor.datalen_max),
uint32_t(mdbx_limits_valsize_max( uint32_t(mdbx_limits_valsize_max(actor.pagesize, actor.table_flags)));
actor.pagesize, MDBX_db_flags_t(key_essentials.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) { if (!actor.keygen.zero_fill) {
key_essentials.flags |= essentials::prng_fill_flag; key_essentials.flags |= essentials::prng_fill_flag;
value_essentials.flags |= essentials::prng_fill_flag; value_essentials.flags |= essentials::prng_fill_flag;
} }
(void)thread_number;
mapping = actor.keygen; mapping = actor.keygen;
salt = const auto split = mapping.split;
(actor.keygen.seed + uint64_t(actor_id)) * UINT64_C(14653293970879851569); 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(); base = actor.serial_base();
} }
@ -307,11 +374,24 @@ buffer alloc(size_t limit) {
return buffer(ptr); return buffer(ptr);
} }
void __hot maker::mk_begin(const serial_t serial, const essentials &params, serial_t __hot maker::mk_begin(serial_t serial, const essentials &params,
result &out) { result &out) {
assert(out.limit >= params.maxlen); assert(out.limit >= params.maxlen);
assert(params.maxlen >= params.minlen); 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)); out.value.iov_len = std::max(unsigned(params.minlen), length(serial));
const auto variation = params.maxlen - params.minlen; const auto variation = params.maxlen - params.minlen;
@ -328,6 +408,7 @@ void __hot maker::mk_begin(const serial_t serial, const essentials &params,
assert(length(serial) <= out.value.iov_len); assert(length(serial) <= out.value.iov_len);
assert(out.value.iov_len >= params.minlen); assert(out.value.iov_len >= params.minlen);
assert(out.value.iov_len <= params.maxlen); assert(out.value.iov_len <= params.maxlen);
return serial;
} }
void __hot maker::mk_continue(const serial_t serial, const essentials &params, void __hot maker::mk_continue(const serial_t serial, const essentials &params,

View File

@ -108,25 +108,24 @@ class maker {
struct essentials { struct essentials {
uint16_t minlen{0}; uint16_t minlen{0};
enum { prng_fill_flag = 1 }; enum { prng_fill_flag = 1, value_age_minwidth = 5 };
uint16_t flags{0}; uint16_t flags{0};
uint32_t maxlen{0}; uint32_t maxlen{0};
serial_t mask{0};
unsigned bits;
} key_essentials, value_essentials; } key_essentials, value_essentials;
unsigned value_age_bits;
serial_t value_age_mask{0};
static void mk_begin(const serial_t serial, const essentials &params, static serial_t mk_begin(serial_t serial, const essentials &params,
result &out); result &out);
static void mk_continue(const serial_t serial, const essentials &params, static void mk_continue(const serial_t serial, const essentials &params,
result &out); result &out);
static void mk(const serial_t serial, const essentials &params, result &out) {
mk_begin(serial, params, out);
mk_continue(serial, params, out);
}
public: public:
void pair(serial_t serial, const buffer &key, buffer &value, void pair(serial_t serial, const buffer &key, buffer &value,
serial_t value_age, const bool keylen_changeable); serial_t value_age, const bool keylen_changeable);
void setup(const config::actor_params_pod &actor, unsigned actor_id, void setup(const config::actor_params_pod &actor, unsigned thread_number);
unsigned thread_number);
bool is_unordered() const; bool is_unordered() const;
void seek2end(serial_t &serial) const; void seek2end(serial_t &serial) const;
@ -141,6 +140,11 @@ public:
} }
return increment(serial, int64_t(uint64_t(delta) << mapping.split)); 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, void log_pair(logging::loglevel level, const char *prefix, const buffer &key,

View File

@ -53,10 +53,18 @@ static void mdbx_logger(MDBX_log_level_t priority, const char *function,
namespace logging { 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 loglevel level;
static FILE *last; static FILE *flow;
void setlevel(loglevel priority) { void setlevel(loglevel priority) {
level = priority; level = priority;
@ -67,12 +75,15 @@ void setlevel(loglevel priority) {
log_trace("set mdbx debug-opts: 0x%02x", rc); log_trace("set mdbx debug-opts: 0x%02x", rc);
} }
void setup(loglevel priority, const std::string &_prefix) { void setup(const std::string &prefix) {
setlevel(priority); prefix_len = std::min(prefix.size(), sizeof(prefix_buf) - 1);
prefix = _prefix; 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) { const char *level2str(const loglevel alevel) {
switch (alevel) { switch (alevel) {
@ -108,18 +119,18 @@ bool output(const loglevel priority, const char *format, ...) {
return true; 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, void output_nocheckloglevel_ap(const logging::loglevel priority,
const char *format, va_list ap) { const char *format, va_list ap) {
if (last) { ln();
putc('\n', last);
fflush(last);
if (last == stderr) {
putc('\n', stdout);
fflush(stdout);
}
last = nullptr;
}
chrono::time now = chrono::now_realtime(); chrono::time now = chrono::now_realtime();
struct tm tm; struct tm tm;
#ifdef _MSC_VER #ifdef _MSC_VER
@ -134,30 +145,27 @@ void output_nocheckloglevel_ap(const logging::loglevel priority,
if (rc != MDBX_SUCCESS) if (rc != MDBX_SUCCESS)
failure_perror("localtime_r()", rc); failure_perror("localtime_r()", rc);
last = stdout; fprintf(stdout,
fprintf(last,
"[ %02d%02d%02d-%02d:%02d:%02d.%06d_%05lu %-10s %.4s ] %s" /* TODO */, "[ %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_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(), 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; va_list ones;
memset(&ones, 0, sizeof(ones)) /* zap MSVC and other goofy compilers */; memset(&ones, 0, sizeof(ones)) /* zap MSVC and other goofy compilers */;
if (same_or_higher(priority, error)) if (same_or_higher(priority, error))
va_copy(ones, ap); va_copy(ones, ap);
vfprintf(last, format, ap); vfprintf(stdout, format, ap);
size_t len = strlen(format); size_t len = strlen(format);
char end = len ? format[len - 1] : '\0'; char end = len ? format[len - 1] : '\0';
switch (end) { switch (end) {
default: default:
putc('\n', last); putc('\n', stdout);
MDBX_CXX17_FALLTHROUGH; // fall through break;
case '\n': case '\n':
fflush(last); break;
last = nullptr;
MDBX_CXX17_FALLTHROUGH; // fall through
case ' ': case ' ':
case '_': case '_':
case ':': case ':':
@ -167,46 +175,39 @@ void output_nocheckloglevel_ap(const logging::loglevel priority,
case '\b': case '\b':
case '\r': case '\r':
case '\0': case '\0':
flow = stdout;
break; break;
} }
if (same_or_higher(priority, error)) { if (same_or_higher(priority, error)) {
if (last != stderr) { if (flow)
fprintf(stderr, "[ %05lu %-10s %.4s ] %s", (long)osal_getpid(), flow = stderr;
prefix.c_str(), level2str(priority), suffix.c_str()); fprintf(stderr, "[ %05lu %-10s %.4s ] %s", (long)osal_getpid(), prefix_buf,
vfprintf(stderr, format, ones); level2str(priority), suffix_ptr);
if (end == '\n') vfprintf(stderr, format, ones);
fflush(stderr);
else
last = stderr;
}
va_end(ones); va_end(ones);
} }
} }
bool feed_ap(const char *format, va_list ap) { bool feed_ap(const char *format, va_list ap) {
if (!last) if (!flow)
return false; return false;
if (last == stderr) { if (flow == stderr) {
va_list ones; va_list ones;
va_copy(ones, ap); va_copy(ones, ap);
vfprintf(stdout, format, ones); vfprintf(stdout, format, ones);
va_end(ones); va_end(ones);
} }
vfprintf(last, format, ap); vfprintf(flow, format, ap);
size_t len = strlen(format); size_t len = strlen(format);
if (len && format[len - 1] == '\n') { if (len && format[len - 1] == '\n')
fflush(last); flow = nullptr;
if (last == stderr)
fflush(stdout);
last = nullptr;
}
return true; return true;
} }
bool feed(const char *format, ...) { bool feed(const char *format, ...) {
if (!last) if (!flow)
return false; return false;
va_list ap; va_list ap;
@ -217,29 +218,36 @@ bool feed(const char *format, ...) {
} }
local_suffix::local_suffix(const char *c_str) local_suffix::local_suffix(const char *c_str)
: trim_pos(suffix.size()), indent(0) { : trim_pos(suffix_buf.size()), indent(0) {
suffix.append(c_str); suffix_buf.append(c_str);
suffix_ptr = suffix_buf.c_str();
} }
local_suffix::local_suffix(const std::string &str) local_suffix::local_suffix(const std::string &str)
: trim_pos(suffix.size()), indent(0) { : trim_pos(suffix_buf.size()), indent(0) {
suffix.append(str); suffix_buf.append(str);
suffix_ptr = suffix_buf.c_str();
} }
void local_suffix::push() { void local_suffix::push() {
indent += 1; indent += 1;
suffix.push_back('\t'); suffix_buf.push_back('\t');
suffix_ptr = suffix_buf.c_str();
} }
void local_suffix::pop() { void local_suffix::pop() {
assert(indent > 0); assert(indent > 0);
if (indent > 0) { if (indent > 0) {
indent -= 1; 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) { void progress_canary(bool active) {
static chrono::time progress_timestamp; static chrono::time progress_timestamp;
@ -294,73 +302,73 @@ void progress_canary(bool active) {
} // namespace logging } // namespace logging
void log_extra(const char *msg, ...) { void log_extra(const char *msg, ...) {
logging::ln();
if (logging::same_or_higher(logging::extra, logging::level)) { if (logging::same_or_higher(logging::extra, logging::level)) {
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
logging::output_nocheckloglevel_ap(logging::extra, msg, ap); logging::output_nocheckloglevel_ap(logging::extra, msg, ap);
va_end(ap); va_end(ap);
} else }
logging::last = nullptr;
} }
void log_trace(const char *msg, ...) { void log_trace(const char *msg, ...) {
logging::ln();
if (logging::same_or_higher(logging::trace, logging::level)) { if (logging::same_or_higher(logging::trace, logging::level)) {
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
logging::output_nocheckloglevel_ap(logging::trace, msg, ap); logging::output_nocheckloglevel_ap(logging::trace, msg, ap);
va_end(ap); va_end(ap);
} else }
logging::last = nullptr;
} }
void log_debug(const char *msg, ...) { void log_debug(const char *msg, ...) {
logging::ln();
if (logging::same_or_higher(logging::debug, logging::level)) { if (logging::same_or_higher(logging::debug, logging::level)) {
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
logging::output_nocheckloglevel_ap(logging::debug, msg, ap); logging::output_nocheckloglevel_ap(logging::debug, msg, ap);
va_end(ap); va_end(ap);
} else }
logging::last = nullptr;
} }
void log_verbose(const char *msg, ...) { void log_verbose(const char *msg, ...) {
logging::ln();
if (logging::same_or_higher(logging::verbose, logging::level)) { if (logging::same_or_higher(logging::verbose, logging::level)) {
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
logging::output_nocheckloglevel_ap(logging::verbose, msg, ap); logging::output_nocheckloglevel_ap(logging::verbose, msg, ap);
va_end(ap); va_end(ap);
} else }
logging::last = nullptr;
} }
void log_notice(const char *msg, ...) { void log_notice(const char *msg, ...) {
logging::ln();
if (logging::same_or_higher(logging::notice, logging::level)) { if (logging::same_or_higher(logging::notice, logging::level)) {
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
logging::output_nocheckloglevel_ap(logging::notice, msg, ap); logging::output_nocheckloglevel_ap(logging::notice, msg, ap);
va_end(ap); va_end(ap);
} else }
logging::last = nullptr;
} }
void log_warning(const char *msg, ...) { void log_warning(const char *msg, ...) {
logging::ln();
if (logging::same_or_higher(logging::warning, logging::level)) { if (logging::same_or_higher(logging::warning, logging::level)) {
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
logging::output_nocheckloglevel_ap(logging::warning, msg, ap); logging::output_nocheckloglevel_ap(logging::warning, msg, ap);
va_end(ap); va_end(ap);
} else }
logging::last = nullptr;
} }
void log_error(const char *msg, ...) { void log_error(const char *msg, ...) {
logging::ln();
if (logging::same_or_higher(logging::error, logging::level)) { if (logging::same_or_higher(logging::error, logging::level)) {
va_list ap; va_list ap;
va_start(ap, msg); va_start(ap, msg);
logging::output_nocheckloglevel_ap(logging::error, msg, ap); logging::output_nocheckloglevel_ap(logging::error, msg, ap);
va_end(ap); va_end(ap);
} else }
logging::last = nullptr;
} }
void log_trouble(const char *where, const char *what, int errnum) { 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); return logging::same_or_higher(priority, logging::level);
} }
void log_flush(void) { fflushall(); } void log_flush(void) {
logging::ln();
fflushall();
}

View File

@ -55,6 +55,7 @@ bool MDBX_PRINTF_ARGS(2, 3)
output(const loglevel priority, const char *format, ...); output(const loglevel priority, const char *format, ...);
bool feed_ap(const char *format, va_list ap); bool feed_ap(const char *format, va_list ap);
bool MDBX_PRINTF_ARGS(1, 2) feed(const char *format, ...); bool MDBX_PRINTF_ARGS(1, 2) feed(const char *format, ...);
void ln();
void inline MDBX_PRINTF_ARGS(2, 3) void inline MDBX_PRINTF_ARGS(2, 3)
output_nocheckloglevel(const loglevel priority, const char *format, ...) { output_nocheckloglevel(const loglevel priority, const char *format, ...) {

View File

@ -13,6 +13,7 @@ DB_UPTO_MB=17408
PAGESIZE=min PAGESIZE=min
DONT_CHECK_RAM=no DONT_CHECK_RAM=no
EXTRA=no EXTRA=no
TAILLOG=0
while [ -n "$1" ] while [ -n "$1" ]
do do
@ -35,9 +36,13 @@ do
echo "--pagesize NN Use specified page size (256 is minimal and used by default)" 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 "--dont-check-ram-size Don't check available RAM"
echo "--extra Iterate extra modes/flags" echo "--extra Iterate extra modes/flags"
echo "--taillog Dump tail of test log on failure"
echo "--help Print this usage help and exit" echo "--help Print this usage help and exit"
exit -2 exit -2
;; ;;
--taillog)
TAILLOG=3333
;;
--multi) --multi)
LIST=basic LIST=basic
;; ;;
@ -62,7 +67,7 @@ do
echo " For instance, when the process 'A' explicitly marks a memory" echo " For instance, when the process 'A' explicitly marks a memory"
echo " region as 'undefined', the process 'B' fill it," echo " region as 'undefined', the process 'B' fill it,"
echo " and after this process 'A' read such region, etc." 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 rm -f valgrind-*.log
;; ;;
--skip-make) --skip-make)
@ -345,14 +350,38 @@ if which lz4 >/dev/null; then
function logger { function logger {
lz4 > ${TESTDB_DIR}/long.log.lz4 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 elif which gzip >/dev/null; then
function logger { function logger {
gzip > ${TESTDB_DIR}/long.log.gz 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 else
function logger { function logger {
cat > ${TESTDB_DIR}/long.log 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 fi
if [ "$EXTRA" != "no" ]; then if [ "$EXTRA" != "no" ]; then
@ -375,6 +404,9 @@ function bits2options {
function failed { function failed {
echo "FAILED" >&2 echo "FAILED" >&2
if [ ${TAILLOG} -gt 0 ]; then
taillog
fi
exit 1 exit 1
} }
@ -421,120 +453,102 @@ for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10
split=30 split=30
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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]} --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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]} --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
split=24 split=24
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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]} --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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]} --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
split=16 split=16
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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]} --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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]} --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
if [ "$EXTRA" != "no" ]; then if [ "$EXTRA" != "no" ]; then
split=10 split=10
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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]} --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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]} --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
fi fi
split=4 split=4
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,int-data, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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 \ --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]} \ --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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]} --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \ 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]} --nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
done # options done # options
loop=$((loop + 1)) loop=$((loop + 1))

View File

@ -37,6 +37,7 @@ MDBX_NORETURN void usage(void) {
" --console[=yes/no] Enable/disable console-like output\n" " --console[=yes/no] Enable/disable console-like output\n"
" --cleanup-before[=YES/no] Cleanup/remove and re-create database\n" " --cleanup-before[=YES/no] Cleanup/remove and re-create database\n"
" --cleanup-after[=YES/no] Cleanup/remove database after completion\n" " --cleanup-after[=YES/no] Cleanup/remove database after completion\n"
" --prng-seed=N Seed PRNG\n"
"Database size control:\n" "Database size control:\n"
" --pagesize=... Database page size: min, max, 256..65536\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" " --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" " --append Append-mode insertions\n"
" --dead.reader Dead-reader simulator\n" " --dead.reader Dead-reader simulator\n"
" --dead.writer Dead-writer 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" "Actor options:\n"
" --batch.read=N Read-operations batch size\n" " --batch.read=N Read-operations batch size\n"
" --batch.write=N Write-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" " --datalen=N Set both min/max for data length\n"
" --keygen.width=N TBD (see the source code)\n" " --keygen.width=N TBD (see the source code)\n"
" --keygen.mesh=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.zerofill=yes|NO TBD (see the source code)\n"
" --keygen.split=N TBD (see the source code)\n" " --keygen.split=N TBD (see the source code)\n"
" --keygen.rotate=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; growth_step = -1;
pagesize = -1; pagesize = -1;
keygen.seed = 1; prng_seed = 0;
keygen.zero_fill = false; keygen.zero_fill = false;
keygen.keycase = kc_random; keygen.keycase = kc_random;
keygen.width = (table_flags & MDBX_DUPSORT) ? 32 : 64; keygen.width = (table_flags & MDBX_DUPSORT) ? 32 : 64;
@ -263,8 +267,19 @@ static void fixup4qemu(actor_params &params) {
(void)params; (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 #ifdef _DEBUG
log_trace("#argc = %d", argc); log_trace("#argc = %d", argc);
for (int i = 0; i < argc; ++i) 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", if (config::parse_option(argc, argv, narg, "keygen.mesh",
params.keygen.mesh, 0, 64)) params.keygen.mesh, 0, 64))
continue; continue;
if (config::parse_option(argc, argv, narg, "keygen.seed", if (config::parse_option(argc, argv, narg, "prng-seed", params.prng_seed,
params.keygen.seed, config::no_scale)) config::no_scale)) {
prng_seed(params.prng_seed);
continue; continue;
}
if (config::parse_option(argc, argv, narg, "keygen.zerofill", if (config::parse_option(argc, argv, narg, "keygen.zerofill",
params.keygen.zero_fill)) params.keygen.zero_fill))
continue; continue;
@ -453,49 +470,59 @@ int main(int argc, char *const argv[]) {
keycase_setup(value, params); keycase_setup(value, params);
continue; continue;
} }
if (config::parse_option(argc, argv, narg, "keylen.min", params.keylen_min, if (config::parse_option(
config::no_scale, params.mdbx_keylen_min(), argc, argv, narg, "keylen.min", params.keylen_min,
params.mdbx_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) || if ((params.table_flags & MDBX_INTEGERKEY) ||
params.keylen_max < params.keylen_min) params.keylen_max < params.keylen_min)
params.keylen_max = params.keylen_min; params.keylen_max = params.keylen_min;
continue; continue;
} }
if (config::parse_option(argc, argv, narg, "keylen.max", params.keylen_max, if (config::parse_option(
config::no_scale, params.mdbx_keylen_min(), argc, argv, narg, "keylen.max", params.keylen_max,
params.mdbx_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) || if ((params.table_flags & MDBX_INTEGERKEY) ||
params.keylen_min > params.keylen_max) params.keylen_min > params.keylen_max)
params.keylen_min = params.keylen_max; params.keylen_min = params.keylen_max;
continue; continue;
} }
if (config::parse_option(argc, argv, narg, "keylen", params.keylen_min, if (config::parse_option(
config::no_scale, params.mdbx_keylen_min(), argc, argv, narg, "keylen", params.keylen_min,
params.mdbx_keylen_max())) { (params.table_flags & MDBX_INTEGERKEY) ? config::intkey
: config::no_scale,
params.mdbx_keylen_min(), params.mdbx_keylen_max())) {
params.keylen_max = params.keylen_min; params.keylen_max = params.keylen_min;
continue; continue;
} }
if (config::parse_option(argc, argv, narg, "datalen.min", if (config::parse_option(
params.datalen_min, config::no_scale, argc, argv, narg, "datalen.min", params.datalen_min,
params.mdbx_datalen_min(), (params.table_flags & MDBX_INTEGERDUP) ? config::intkey
params.mdbx_datalen_max())) { : config::no_scale,
params.mdbx_datalen_min(), params.mdbx_datalen_max())) {
if ((params.table_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) || if ((params.table_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) ||
params.datalen_max < params.datalen_min) params.datalen_max < params.datalen_min)
params.datalen_max = params.datalen_min; params.datalen_max = params.datalen_min;
continue; continue;
} }
if (config::parse_option(argc, argv, narg, "datalen.max", if (config::parse_option(
params.datalen_max, config::no_scale, argc, argv, narg, "datalen.max", params.datalen_max,
params.mdbx_datalen_min(), (params.table_flags & MDBX_INTEGERDUP) ? config::intkey
params.mdbx_datalen_max())) { : config::no_scale,
params.mdbx_datalen_min(), params.mdbx_datalen_max())) {
if ((params.table_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) || if ((params.table_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) ||
params.datalen_min > params.datalen_max) params.datalen_min > params.datalen_max)
params.datalen_min = params.datalen_max; params.datalen_min = params.datalen_max;
continue; continue;
} }
if (config::parse_option(argc, argv, narg, "datalen", params.datalen_min, if (config::parse_option(
config::no_scale, params.mdbx_datalen_min(), argc, argv, narg, "datalen", params.datalen_min,
params.mdbx_datalen_max())) { (params.table_flags & MDBX_INTEGERDUP) ? config::intkey
: config::no_scale,
params.mdbx_datalen_min(), params.mdbx_datalen_max())) {
params.datalen_max = params.datalen_min; params.datalen_max = params.datalen_min;
continue; continue;
} }
@ -591,6 +618,18 @@ int main(int argc, char *const argv[]) {
configure_actor(last_space_id, ac_nested, value, params); configure_actor(last_space_id, ac_nested, value, params);
continue; 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] != '-') { if (*argv[narg] != '-') {
fixup4qemu(params); fixup4qemu(params);
@ -704,6 +743,14 @@ int main(int argc, char *const argv[]) {
log_trace("=== done..."); 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"); log_notice("RESULT: %s\n", failed ? "Failed" : "Successful");
if (global::config::cleanup_after) { if (global::config::cleanup_after) {
if (failed) if (failed)

View File

@ -74,7 +74,7 @@ bool testcase_nested::setup() {
return false; 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); key = keygen::alloc(config.params.keylen_max);
data = keygen::alloc(config.params.datalen_max); data = keygen::alloc(config.params.datalen_max);
serial = 0; serial = 0;
@ -292,8 +292,7 @@ retry:
} }
bool testcase_nested::run() { bool testcase_nested::run() {
uint64_t seed = uint64_t seed = prng64_map2_white(prng_state) + config.space_id;
prng64_map2_white(config.params.keygen.seed) + config.actor_id;
clear_wholetable_passed = 0; clear_wholetable_passed = 0;
clear_stepbystep_passed = 0; clear_stepbystep_passed = 0;

View File

@ -356,6 +356,7 @@ mdbx_pid_t osal_getpid(void) { return getpid(); }
int osal_delay(unsigned seconds) { return sleep(seconds) ? errno : 0; } int osal_delay(unsigned seconds) { return sleep(seconds) ? errno : 0; }
int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) { int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) {
static sigset_t mask;
if (children.empty()) { if (children.empty()) {
struct sigaction act; struct sigaction act;
memset(&act, 0, sizeof(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(SIGUSR1, &act, nullptr);
sigaction(SIGUSR2, &act, nullptr); sigaction(SIGUSR2, &act, nullptr);
sigset_t mask;
sigemptyset(&mask); sigemptyset(&mask);
sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGUSR1); sigaddset(&mask, SIGUSR1);
@ -377,6 +377,7 @@ int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) {
pid = fork(); pid = fork();
if (pid == 0) { if (pid == 0) {
sigprocmask(SIG_BLOCK, &mask, nullptr);
overlord_pid = getppid(); overlord_pid = getppid();
const bool result = test_execute(config); const bool result = test_execute(config);
exit(result ? EXIT_SUCCESS : EXIT_FAILURE); 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) if (sig == SIGHUP)
return "HUP"; return "HUP";
if (sig == SIGINT) if (sig == SIGINT)
@ -532,24 +533,25 @@ int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
children[pid] = children[pid] =
(WEXITSTATUS(status) == EXIT_SUCCESS) ? as_successful : as_failed; (WEXITSTATUS(status) == EXIT_SUCCESS) ? as_successful : as_failed;
else if (WIFSIGNALED(status)) { else if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
#ifdef WCOREDUMP #ifdef WCOREDUMP
if (WCOREDUMP(status)) if (WCOREDUMP(status))
children[pid] = as_coredump; children[pid] = as_coredump;
else else
#endif /* WCOREDUMP */ #endif /* WCOREDUMP */
switch (WTERMSIG(status)) { switch (sig) {
case SIGABRT: case SIGABRT:
case SIGBUS: case SIGBUS:
case SIGFPE: case SIGFPE:
case SIGILL: case SIGILL:
case SIGSEGV: case SIGSEGV:
log_notice("child pid %lu terminated by SIG%s", (long)pid, log_notice("child pid %lu %s by SIG%s", (long)pid, "terminated",
signal_name(WTERMSIG(status))); signal_name(sig));
children[pid] = as_coredump; children[pid] = as_coredump;
break; break;
default: default:
log_notice("child pid %lu killed by SIG%s", (long)pid, log_notice("child pid %lu %s by SIG%s", (long)pid, "killed",
signal_name(WTERMSIG(status))); signal_name(sig));
children[pid] = as_killed; children[pid] = as_killed;
} }
} else if (WIFSTOPPED(status)) } else if (WIFSTOPPED(status))

View File

@ -46,3 +46,7 @@ std::string osal_tempdir(void);
#define STDERR_FILENO _fileno(stderr) #define STDERR_FILENO _fileno(stderr)
#endif #endif
#endif /* _MSC_VER */ #endif /* _MSC_VER */
#if !defined(_WIN32) && !defined(_WIN64)
const char *signal_name(const int sig);
#endif /* Windows */

View File

@ -60,7 +60,7 @@ do
echo " For instance, when the process 'A' explicitly marks a memory" echo " For instance, when the process 'A' explicitly marks a memory"
echo " region as 'undefined', the process 'B' fill it," echo " region as 'undefined', the process 'B' fill it,"
echo " and after this process 'A' read such region, etc." 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 rm -f valgrind-*.log
;; ;;
--skip-make) --skip-make)

View File

@ -39,6 +39,12 @@ const char *testcase2str(const actor_testcase testcase) {
return "ttl"; return "ttl";
case ac_nested: case ac_nested:
return "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; return err;
} }
MDBX_dbi testcase::db_table_open(bool create) { const char *testcase::db_tablename(tablename_buf &buffer,
log_trace(">> testcase::db_table_create"); const char *suffix) const {
char tablename_buf[16];
const char *tablename = nullptr; const char *tablename = nullptr;
if (config.space_id) { if (config.space_id) {
int rc = snprintf(tablename_buf, sizeof(tablename_buf), "TBL%04u", int rc =
config.space_id); snprintf(buffer, sizeof(buffer), "TBL%04u%s", config.space_id, suffix);
if (rc < 4 || rc >= (int)sizeof(tablename_buf) - 1) if (rc < 4 || rc >= (int)sizeof(tablename_buf) - 1)
failure("snprintf(tablename): %d", rc); failure("snprintf(tablename): %d", rc);
tablename = tablename_buf; tablename = buffer;
} }
log_debug("use %s table", tablename ? tablename : "MAINDB"); 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; MDBX_dbi handle = 0;
int rc = mdbx_dbi_open(txn_guard.get(), tablename, int rc = mdbx_dbi_open(
(create ? MDBX_CREATE : MDBX_DB_DEFAULTS) | txn_guard.get(), tablename,
config.params.table_flags, create ? (MDBX_CREATE | config.params.table_flags)
&handle); : (flipcoin() ? MDBX_DB_ACCEDE
if (unlikely(rc != MDBX_SUCCESS)) : MDBX_DB_DEFAULTS | config.params.table_flags),
failure_perror("mdbx_dbi_open()", rc); &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; 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) { void testcase::db_table_clear(MDBX_dbi handle, MDBX_txn *txn) {
log_trace(">> testcase::db_table_clear, handle %u", handle); log_trace(">> testcase::db_table_clear, handle %u", handle);
int rc = mdbx_drop(txn ? txn : txn_guard.get(), handle, false); int err = mdbx_drop(txn ? txn : txn_guard.get(), handle, false);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(err != MDBX_SUCCESS))
failure_perror("mdbx_drop(delete=false)", rc); failure_perror("mdbx_drop(delete=false)", err);
speculum.clear(); speculum.clear();
log_trace("<< testcase::db_table_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) { void testcase::db_table_close(MDBX_dbi handle) {
log_trace(">> testcase::db_table_close, handle %u", handle); log_trace(">> testcase::db_table_close, handle %u", handle);
assert(!txn_guard); assert(!txn_guard);
int rc = mdbx_dbi_close(db_guard.get(), handle); int err = mdbx_dbi_close(db_guard.get(), handle);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(err != MDBX_SUCCESS))
failure_perror("mdbx_dbi_close()", rc); failure_perror("mdbx_dbi_close()", err);
log_trace("<< testcase::db_table_close"); 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 expected_valued) {
MDBX_val actual_value = expected_valued; MDBX_val actual_value = expected_valued;
int rc = mdbx_get_equal_or_great(txn_guard.get(), handle, &key2check, int err = mdbx_get_equal_or_great(txn_guard.get(), handle, &key2check,
&actual_value); &actual_value);
if (unlikely(rc != MDBX_SUCCESS)) if (unlikely(err != MDBX_SUCCESS)) {
failure_perror(step, rc); 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)) if (!is_samedata(&actual_value, &expected_valued))
failure("%s data mismatch", step); failure("%s data mismatch", step);
return true;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -648,8 +672,8 @@ bool test_execute(const actor_config &config_const) {
size_t(config.params.nrepeat)); size_t(config.params.nrepeat));
else else
log_verbose("test successfully (iteration %zi)", iter); log_verbose("test successfully (iteration %zi)", iter);
config.params.keygen.seed += INT32_C(0xA4F4D37B); prng_seed(config.params.prng_seed += INT32_C(0xA4F4D37B));
log_verbose("turn keygen to %u", config.params.keygen.seed); log_verbose("turn PRNG to %u", config.params.prng_seed);
} }
} while (config.params.nrepeat == 0 || iter < config.params.nrepeat); } 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; 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(); const auto cursor_lowerbound = speculum_cursors[lowerbound].get();
speculum_check_cursor("after-insert", "lowerbound", it_lowerbound, speculum_check_cursor("after-insert", "lowerbound", it_lowerbound,
cursor_lowerbound, MDBX_GET_CURRENT); 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, int testcase::replace(const keygen::buffer &akey,
const keygen::buffer &new_data, 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) { if (config.params.speculum) {
const auto S_key = iov2dataview(akey); const auto S_key = iov2dataview(akey);
const auto S_old = iov2dataview(old_data); const auto S_old = iov2dataview(old_data);
const auto S_new = iov2dataview(new_data); const auto S_new = iov2dataview(new_data);
const auto removed = speculum.erase(SET::key_type(S_key, S_old)); 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]; char dump_key[128], dump_value[128];
log_error( log_error(
"speculum-%s: %s old value {%s, %s}", "replace", "speculum-%s: no old pair {%s, %s} (keygen mistake)", "replace",
(removed > 1) ? "multi" : "no",
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)), mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
mdbx_dump_val(&old_data->value, dump_value, sizeof(dump_value))); mdbx_dump_val(&old_data->value, dump_value, sizeof(dump_value)));
} expected_err = MDBX_NOTFOUND;
if (unlikely(!speculum.emplace(S_key, S_new).second)) { } else if (unlikely(!speculum.emplace(S_key, S_new).second)) {
char dump_key[128], dump_value[128]; char dump_key[128], dump_value[128];
log_error( 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(&akey->value, dump_key, sizeof(dump_key)),
mdbx_dump_val(&new_data->value, dump_value, sizeof(dump_value))); 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, int err = mdbx_replace(txn_guard.get(), dbi, &akey->value, &new_data->value,
&old_data->value, flags); &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) { int testcase::remove(const keygen::buffer &akey, const keygen::buffer &adata) {

View File

@ -103,7 +103,7 @@ class registry {
struct record { struct record {
actor_testcase id = ac_none; actor_testcase id = ac_none;
std::string name; 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; testcase *(*constructor)(const actor_config &, const mdbx_pid_t) = nullptr;
}; };
std::unordered_map<std::string, const record *> name2id; std::unordered_map<std::string, const record *> name2id;
@ -124,8 +124,8 @@ public:
add(this); add(this);
} }
}; };
static bool review_actor_params(const actor_testcase id, static bool review_actor_params(const actor_testcase id, actor_params &params,
actor_params &params); const unsigned space_id);
static testcase *create_actor(const actor_config &config, static testcase *create_actor(const actor_config &config,
const mdbx_pid_t pid); const mdbx_pid_t pid);
}; };
@ -232,7 +232,8 @@ protected:
int insert(const keygen::buffer &akey, const keygen::buffer &adata, int insert(const keygen::buffer &akey, const keygen::buffer &adata,
MDBX_put_flags_t flags); MDBX_put_flags_t flags);
int replace(const keygen::buffer &akey, const keygen::buffer &new_value, 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); int remove(const keygen::buffer &akey, const keygen::buffer &adata);
static int hsr_callback(const MDBX_env *env, const MDBX_txn *txn, static int hsr_callback(const MDBX_env *env, const MDBX_txn *txn,
@ -248,9 +249,10 @@ protected:
void db_prepare(); void db_prepare();
void db_open(); void db_open();
void db_close(); 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(); int breakable_commit();
void txn_end(bool abort); virtual void txn_end(bool abort);
int breakable_restart(); int breakable_restart();
void txn_restart(bool abort, bool readonly, void txn_restart(bool abort, bool readonly,
MDBX_txn_flags_t flags = MDBX_TXN_READWRITE); MDBX_txn_flags_t flags = MDBX_TXN_READWRITE);
@ -261,11 +263,14 @@ protected:
void txn_inject_writefault(MDBX_txn *txn); void txn_inject_writefault(MDBX_txn *txn);
void fetch_canary(); void fetch_canary();
void update_canary(uint64_t increment); 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); MDBX_val expected_valued);
unsigned txn_underutilization_x256(MDBX_txn *txn) const; 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_drop(MDBX_dbi handle);
void db_table_clear(MDBX_dbi handle, MDBX_txn *txn = nullptr); void db_table_clear(MDBX_dbi handle, MDBX_txn *txn = nullptr);
void db_table_close(MDBX_dbi handle); void db_table_close(MDBX_dbi handle);
@ -298,8 +303,9 @@ public:
memset(&last, 0, sizeof(last)); memset(&last, 0, sizeof(last));
} }
static bool review_params(actor_params &params) { static bool review_params(actor_params &params, unsigned space_id) {
// silently fix key/data length for fixed-length modes // silently fix key/data length for fixed-length modes
params.prng_seed += bleach32(space_id);
if ((params.table_flags & MDBX_INTEGERKEY) && if ((params.table_flags & MDBX_INTEGERKEY) &&
params.keylen_min != params.keylen_max) params.keylen_min != params.keylen_max)
params.keylen_min = params.keylen_max; params.keylen_min = params.keylen_max;

View File

@ -119,9 +119,8 @@ bool testcase_ttl::run() {
return false; return false;
} }
uint64_t seed = uint64_t seed = prng64_map2_white(prng_state) + config.space_id;
prng64_map2_white(config.params.keygen.seed) + config.actor_id; keyvalue_maker.setup(config.params, 0 /* thread_number */);
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
key = keygen::alloc(config.params.keylen_max); key = keygen::alloc(config.params.keylen_max);
data = keygen::alloc(config.params.datalen_max); data = keygen::alloc(config.params.datalen_max);
const MDBX_put_flags_t insert_flags = const MDBX_put_flags_t insert_flags =

View File

@ -107,18 +107,22 @@ uint64_t prng64_white(uint64_t &state) {
return bleach64(state); return bleach64(state);
} }
uint32_t prng32(uint64_t &state) { uint32_t prng32_fast(uint64_t &state) {
return (uint32_t)(prng64_careless(state) >> 32); 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) { 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) { while (bytes >= 4) {
memcpy(ptr, &u32, 4); memcpy(ptr, &u32, 4);
ptr = (uint32_t *)ptr + 1; ptr = (uint32_t *)ptr + 1;
bytes -= 4; bytes -= 4;
u32 = prng32(state); u32 = prng32_fast(state);
} }
switch (bytes & 3) { 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); } 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); } uint64_t prng64(void) { return prng64_white(prng_state); }

View File

@ -288,24 +288,26 @@ inline bool is_samedata(const MDBX_val &a, const MDBX_val &b) {
} }
std::string format(const char *fmt, ...); std::string format(const char *fmt, ...);
static inline uint64_t bleach64(uint64_t v) { static inline uint64_t bleach64(uint64_t x) {
// Tommy Ettinger, https://www.blogger.com/profile/04953541827437796598 // NASAM from Tommy Ettinger,
// http://mostlymangling.blogspot.com/2019/01/better-stronger-mixer-and-test-procedure.html // https://www.blogger.com/profile/04953541827437796598
v ^= rot64(v, 25) ^ rot64(v, 50); // http://mostlymangling.blogspot.com/2020/01/nasam-not-another-strange-acronym-mixer.html
v *= UINT64_C(0xA24BAED4963EE407); x ^= rot64(x, 25) ^ rot64(x, 47);
v ^= rot64(v, 24) ^ rot64(v, 49); x *= UINT64_C(0x9E6C63D0676A9A99);
v *= UINT64_C(0x9FB21C651E98DF25); x ^= x >> 23 ^ x >> 51;
return v ^ v >> 28; x *= UINT64_C(0x9E6D62D06F6A9A9B);
x ^= x >> 23 ^ x >> 51;
return x;
} }
static inline uint32_t bleach32(uint32_t x) { static inline uint32_t bleach32(uint32_t x) {
// https://github.com/skeeto/hash-prospector // https://github.com/skeeto/hash-prospector
// exact bias: 0.17353355999581582 // exact bias: 0.10760229515479501
x ^= x >> 16; x ^= x >> 16;
x *= UINT32_C(0x7feb352d); x *= UINT32_C(0x21f0aaad);
x ^= 0x3027C563 ^ (x >> 15); x ^= 0x3027C563 ^ (x >> 15);
x *= UINT32_C(0x846ca68b); x *= UINT32_C(0x0d35a2d97);
x ^= x >> 16; x ^= x >> 15;
return x; return x;
} }
@ -343,9 +345,11 @@ static inline double u64_to_double1(uint64_t v) {
} }
uint64_t prng64_white(uint64_t &state); 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); void prng_fill(uint64_t &state, void *ptr, size_t bytes);
extern uint64_t prng_state;
void prng_seed(uint64_t seed); void prng_seed(uint64_t seed);
uint32_t prng32(void); uint32_t prng32(void);
uint64_t prng64(void); uint64_t prng64(void);

View File

@ -2,7 +2,6 @@
msync-whole-mmap-1 msync-whole-mmap-1
Memcheck:Param Memcheck:Param
msync(start) msync(start)
fun:msync
... ...
fun:sync_locked* fun:sync_locked*
} }
@ -10,7 +9,6 @@
msync-whole-mmap-2 msync-whole-mmap-2
Memcheck:Param Memcheck:Param
msync(start) msync(start)
fun:msync
... ...
fun:env_sync* fun:env_sync*
} }
@ -18,7 +16,6 @@
msync-whole-mmap-3 msync-whole-mmap-3
Memcheck:Param Memcheck:Param
msync(start) msync(start)
fun:msync
... ...
fun:map_resize* fun:map_resize*
} }
@ -26,7 +23,6 @@
msync-wipe-steady msync-wipe-steady
Memcheck:Param Memcheck:Param
msync(start) msync(start)
fun:msync
... ...
fun:wipe_steady* fun:wipe_steady*
} }
@ -34,7 +30,6 @@
msync-meta msync-meta
Memcheck:Param Memcheck:Param
msync(start) msync(start)
fun:msync
... ...
fun:meta_sync* fun:meta_sync*
} }
@ -42,7 +37,6 @@
msync-spill msync-spill
Memcheck:Param Memcheck:Param
msync(start) msync(start)
fun:msync
... ...
fun:txn_spill* fun:txn_spill*
} }
@ -72,7 +66,6 @@
pwrite-page-flush pwrite-page-flush
Memcheck:Param Memcheck:Param
pwrite(buf) pwrite(buf)
fun:pwrite
... ...
fun:iov_write* fun:iov_write*
} }
@ -80,7 +73,6 @@
pwrite64-page-flush pwrite64-page-flush
Memcheck:Param Memcheck:Param
pwrite64(buf) pwrite64(buf)
fun:pwrite
... ...
fun:iov_write* fun:iov_write*
} }
@ -90,16 +82,14 @@
# pwritev-page-flush # pwritev-page-flush
# Memcheck:Param # Memcheck:Param
# pwritev(vector[...]) # pwritev(vector[...])
# fun:pwritev
# ... # ...
# fun:iov_write* # 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 pwritev-page-flush-0
Memcheck:Param Memcheck:Param
pwritev(vector[0]) pwritev(vector[0])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -107,7 +97,6 @@
pwritev-page-flush-1 pwritev-page-flush-1
Memcheck:Param Memcheck:Param
pwritev(vector[1]) pwritev(vector[1])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -115,7 +104,6 @@
pwritev-page-flush-2 pwritev-page-flush-2
Memcheck:Param Memcheck:Param
pwritev(vector[2]) pwritev(vector[2])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -123,7 +111,6 @@
pwritev-page-flush-3 pwritev-page-flush-3
Memcheck:Param Memcheck:Param
pwritev(vector[3]) pwritev(vector[3])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -131,7 +118,6 @@
pwritev-page-flush-4 pwritev-page-flush-4
Memcheck:Param Memcheck:Param
pwritev(vector[4]) pwritev(vector[4])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -139,7 +125,6 @@
pwritev-page-flush-5 pwritev-page-flush-5
Memcheck:Param Memcheck:Param
pwritev(vector[5]) pwritev(vector[5])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -147,7 +132,6 @@
pwritev-page-flush-6 pwritev-page-flush-6
Memcheck:Param Memcheck:Param
pwritev(vector[6]) pwritev(vector[6])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -155,7 +139,6 @@
pwritev-page-flush-7 pwritev-page-flush-7
Memcheck:Param Memcheck:Param
pwritev(vector[7]) pwritev(vector[7])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -163,7 +146,6 @@
pwritev-page-flush-8 pwritev-page-flush-8
Memcheck:Param Memcheck:Param
pwritev(vector[8]) pwritev(vector[8])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -171,7 +153,6 @@
pwritev-page-flush-9 pwritev-page-flush-9
Memcheck:Param Memcheck:Param
pwritev(vector[9]) pwritev(vector[9])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -179,7 +160,6 @@
pwritev-page-flush-10 pwritev-page-flush-10
Memcheck:Param Memcheck:Param
pwritev(vector[10]) pwritev(vector[10])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -187,7 +167,6 @@
pwritev-page-flush-11 pwritev-page-flush-11
Memcheck:Param Memcheck:Param
pwritev(vector[11]) pwritev(vector[11])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -195,7 +174,6 @@
pwritev-page-flush-12 pwritev-page-flush-12
Memcheck:Param Memcheck:Param
pwritev(vector[12]) pwritev(vector[12])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -203,7 +181,6 @@
pwritev-page-flush-13 pwritev-page-flush-13
Memcheck:Param Memcheck:Param
pwritev(vector[13]) pwritev(vector[13])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -211,7 +188,6 @@
pwritev-page-flush-14 pwritev-page-flush-14
Memcheck:Param Memcheck:Param
pwritev(vector[14]) pwritev(vector[14])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -219,7 +195,6 @@
pwritev-page-flush-15 pwritev-page-flush-15
Memcheck:Param Memcheck:Param
pwritev(vector[15]) pwritev(vector[15])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -227,7 +202,6 @@
pwritev-page-flush-16 pwritev-page-flush-16
Memcheck:Param Memcheck:Param
pwritev(vector[16]) pwritev(vector[16])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -235,7 +209,6 @@
pwritev-page-flush-17 pwritev-page-flush-17
Memcheck:Param Memcheck:Param
pwritev(vector[17]) pwritev(vector[17])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -243,7 +216,6 @@
pwritev-page-flush-18 pwritev-page-flush-18
Memcheck:Param Memcheck:Param
pwritev(vector[18]) pwritev(vector[18])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -251,7 +223,6 @@
pwritev-page-flush-19 pwritev-page-flush-19
Memcheck:Param Memcheck:Param
pwritev(vector[19]) pwritev(vector[19])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -259,7 +230,6 @@
pwritev-page-flush-20 pwritev-page-flush-20
Memcheck:Param Memcheck:Param
pwritev(vector[20]) pwritev(vector[20])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -267,7 +237,6 @@
pwritev-page-flush-21 pwritev-page-flush-21
Memcheck:Param Memcheck:Param
pwritev(vector[21]) pwritev(vector[21])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -275,7 +244,6 @@
pwritev-page-flush-22 pwritev-page-flush-22
Memcheck:Param Memcheck:Param
pwritev(vector[22]) pwritev(vector[22])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -283,7 +251,6 @@
pwritev-page-flush-23 pwritev-page-flush-23
Memcheck:Param Memcheck:Param
pwritev(vector[23]) pwritev(vector[23])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -291,7 +258,6 @@
pwritev-page-flush-24 pwritev-page-flush-24
Memcheck:Param Memcheck:Param
pwritev(vector[24]) pwritev(vector[24])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -299,7 +265,6 @@
pwritev-page-flush-25 pwritev-page-flush-25
Memcheck:Param Memcheck:Param
pwritev(vector[25]) pwritev(vector[25])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -307,7 +272,6 @@
pwritev-page-flush-26 pwritev-page-flush-26
Memcheck:Param Memcheck:Param
pwritev(vector[26]) pwritev(vector[26])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -315,7 +279,6 @@
pwritev-page-flush-27 pwritev-page-flush-27
Memcheck:Param Memcheck:Param
pwritev(vector[27]) pwritev(vector[27])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -323,7 +286,6 @@
pwritev-page-flush-28 pwritev-page-flush-28
Memcheck:Param Memcheck:Param
pwritev(vector[28]) pwritev(vector[28])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -331,7 +293,6 @@
pwritev-page-flush-29 pwritev-page-flush-29
Memcheck:Param Memcheck:Param
pwritev(vector[29]) pwritev(vector[29])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -339,7 +300,6 @@
pwritev-page-flush-30 pwritev-page-flush-30
Memcheck:Param Memcheck:Param
pwritev(vector[30]) pwritev(vector[30])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -347,7 +307,6 @@
pwritev-page-flush-31 pwritev-page-flush-31
Memcheck:Param Memcheck:Param
pwritev(vector[31]) pwritev(vector[31])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -355,7 +314,6 @@
pwritev-page-flush-32 pwritev-page-flush-32
Memcheck:Param Memcheck:Param
pwritev(vector[32]) pwritev(vector[32])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -363,7 +321,6 @@
pwritev-page-flush-33 pwritev-page-flush-33
Memcheck:Param Memcheck:Param
pwritev(vector[33]) pwritev(vector[33])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -371,7 +328,6 @@
pwritev-page-flush-34 pwritev-page-flush-34
Memcheck:Param Memcheck:Param
pwritev(vector[34]) pwritev(vector[34])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -379,7 +335,6 @@
pwritev-page-flush-35 pwritev-page-flush-35
Memcheck:Param Memcheck:Param
pwritev(vector[35]) pwritev(vector[35])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -387,7 +342,6 @@
pwritev-page-flush-36 pwritev-page-flush-36
Memcheck:Param Memcheck:Param
pwritev(vector[36]) pwritev(vector[36])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -395,7 +349,6 @@
pwritev-page-flush-37 pwritev-page-flush-37
Memcheck:Param Memcheck:Param
pwritev(vector[37]) pwritev(vector[37])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -403,7 +356,6 @@
pwritev-page-flush-38 pwritev-page-flush-38
Memcheck:Param Memcheck:Param
pwritev(vector[38]) pwritev(vector[38])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -411,7 +363,6 @@
pwritev-page-flush-39 pwritev-page-flush-39
Memcheck:Param Memcheck:Param
pwritev(vector[39]) pwritev(vector[39])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -419,7 +370,6 @@
pwritev-page-flush-40 pwritev-page-flush-40
Memcheck:Param Memcheck:Param
pwritev(vector[40]) pwritev(vector[40])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -427,7 +377,6 @@
pwritev-page-flush-41 pwritev-page-flush-41
Memcheck:Param Memcheck:Param
pwritev(vector[41]) pwritev(vector[41])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -435,7 +384,6 @@
pwritev-page-flush-42 pwritev-page-flush-42
Memcheck:Param Memcheck:Param
pwritev(vector[42]) pwritev(vector[42])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -443,7 +391,6 @@
pwritev-page-flush-43 pwritev-page-flush-43
Memcheck:Param Memcheck:Param
pwritev(vector[43]) pwritev(vector[43])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -451,7 +398,6 @@
pwritev-page-flush-44 pwritev-page-flush-44
Memcheck:Param Memcheck:Param
pwritev(vector[44]) pwritev(vector[44])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -459,7 +405,6 @@
pwritev-page-flush-45 pwritev-page-flush-45
Memcheck:Param Memcheck:Param
pwritev(vector[45]) pwritev(vector[45])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -467,7 +412,6 @@
pwritev-page-flush-46 pwritev-page-flush-46
Memcheck:Param Memcheck:Param
pwritev(vector[46]) pwritev(vector[46])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -475,7 +419,6 @@
pwritev-page-flush-47 pwritev-page-flush-47
Memcheck:Param Memcheck:Param
pwritev(vector[47]) pwritev(vector[47])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -483,7 +426,6 @@
pwritev-page-flush-48 pwritev-page-flush-48
Memcheck:Param Memcheck:Param
pwritev(vector[48]) pwritev(vector[48])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -491,7 +433,6 @@
pwritev-page-flush-49 pwritev-page-flush-49
Memcheck:Param Memcheck:Param
pwritev(vector[49]) pwritev(vector[49])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -499,7 +440,6 @@
pwritev-page-flush-50 pwritev-page-flush-50
Memcheck:Param Memcheck:Param
pwritev(vector[50]) pwritev(vector[50])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -507,7 +447,6 @@
pwritev-page-flush-51 pwritev-page-flush-51
Memcheck:Param Memcheck:Param
pwritev(vector[51]) pwritev(vector[51])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -515,7 +454,6 @@
pwritev-page-flush-52 pwritev-page-flush-52
Memcheck:Param Memcheck:Param
pwritev(vector[52]) pwritev(vector[52])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -523,7 +461,6 @@
pwritev-page-flush-53 pwritev-page-flush-53
Memcheck:Param Memcheck:Param
pwritev(vector[53]) pwritev(vector[53])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -531,7 +468,6 @@
pwritev-page-flush-54 pwritev-page-flush-54
Memcheck:Param Memcheck:Param
pwritev(vector[54]) pwritev(vector[54])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -539,7 +475,6 @@
pwritev-page-flush-55 pwritev-page-flush-55
Memcheck:Param Memcheck:Param
pwritev(vector[55]) pwritev(vector[55])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -547,7 +482,6 @@
pwritev-page-flush-56 pwritev-page-flush-56
Memcheck:Param Memcheck:Param
pwritev(vector[56]) pwritev(vector[56])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -555,7 +489,6 @@
pwritev-page-flush-57 pwritev-page-flush-57
Memcheck:Param Memcheck:Param
pwritev(vector[57]) pwritev(vector[57])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -563,7 +496,6 @@
pwritev-page-flush-58 pwritev-page-flush-58
Memcheck:Param Memcheck:Param
pwritev(vector[58]) pwritev(vector[58])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -571,7 +503,6 @@
pwritev-page-flush-59 pwritev-page-flush-59
Memcheck:Param Memcheck:Param
pwritev(vector[59]) pwritev(vector[59])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -579,7 +510,6 @@
pwritev-page-flush-60 pwritev-page-flush-60
Memcheck:Param Memcheck:Param
pwritev(vector[60]) pwritev(vector[60])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -587,7 +517,6 @@
pwritev-page-flush-61 pwritev-page-flush-61
Memcheck:Param Memcheck:Param
pwritev(vector[61]) pwritev(vector[61])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -595,7 +524,6 @@
pwritev-page-flush-62 pwritev-page-flush-62
Memcheck:Param Memcheck:Param
pwritev(vector[62]) pwritev(vector[62])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }
@ -603,7 +531,6 @@
pwritev-page-flush-63 pwritev-page-flush-63
Memcheck:Param Memcheck:Param
pwritev(vector[63]) pwritev(vector[63])
fun:pwritev
... ...
fun:iov_write* fun:iov_write*
} }