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"
CACHE FILEPATH "Suppressions file for Valgrind" FORCE)
set(MEMORYCHECK_COMMAND_OPTIONS
"--trace-children=yes --leak-check=full --track-origins=yes --error-exitcode=42 --error-markers=@ --errors-for-leak-kinds=definite --fair-sched=yes --suppressions=${MEMORYCHECK_SUPPRESSIONS_FILE}"
"--trace-children=yes --leak-check=full --track-origins=yes --track-origins=yes --error-exitcode=42 --error-markers=@ --errors-for-leak-kinds=definite --fair-sched=yes --suppressions=${MEMORYCHECK_SUPPRESSIONS_FILE}"
CACHE STRING "Valgrind options" FORCE)
set(VALGRIND_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS}" CACHE STRING "Valgrind options" FORCE)
endif()
@ -467,7 +467,7 @@ endif()
# #### # # # #### # # ####
#
set(MDBX_BUILD_OPTIONS ENABLE_UBSAN ENABLE_ASAN MDBX_USE_VALGRIND ENABLE_GPROF ENABLE_GCOV)
set(MDBX_BUILD_OPTIONS ENABLE_UBSAN ENABLE_ASAN ENABLE_MEMCHECK ENABLE_GPROF ENABLE_GCOV)
macro(add_mdbx_option NAME DESCRIPTION DEFAULT)
list(APPEND MDBX_BUILD_OPTIONS ${NAME})
if(NOT ${DEFAULT} STREQUAL "AUTO")
@ -531,6 +531,8 @@ add_mdbx_option(MDBX_ENABLE_BIGFOOT "Chunking long list of retired pages during
add_mdbx_option(MDBX_ENABLE_PGOP_STAT "Gathering statistics for page operations" ON)
add_mdbx_option(MDBX_ENABLE_PROFGC "Profiling of GC search and updates" OFF)
mark_as_advanced(MDBX_ENABLE_PROFGC)
add_mdbx_option(MDBX_ENABLE_DBI_SPARSE "FIXME" ON)
add_mdbx_option(MDBX_ENABLE_DBI_LOCKFREE "FIXME" ON)
if(NOT MDBX_AMALGAMATED_SOURCE)
if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE_UPPERCASE STREQUAL "DEBUG")

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)
and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md).
## v0.13.1 (в процессе подготовки релиза)
Новая версия с существенным расширением API и добавлением функционала.
Новое:
- Управление основной блокировкой lock/unlock/upgrade/downgrade для координации пишущих транзакций.
- `mdbx_env_chk() `для проверка целостности структуры БД, с переработкой и переносом функционала утилиты `mdbx_chk` внутрь библиотеки.
- `mdbx_dbi_rename()` и `mdbx_dbi_rename()` для переименования таблиц.
- `mdbx_cursor_unbind()` и `mdbx_txn_release_all_cursors()` для управления курсорами.
- `mdbx_env_resurrect_after_fork()` для восстановление открытой среды работы с БД в дочернем процессе после ветвления/расщепления процесса.
- `mdbx_cursor_compare()` для сравнения позиций курсоров.
- `mdbx_cursor_scan()` и `mdbx_cursor_scan_from()` для сканирования таблиц с использованием функционального предиката.
- `mdbx_cursor_on_first_dup()` и `mdbx_cursor_on_last_dup()` для проверки позиции курсора.
- `mdbx_preopen_snapinfo()` для получения информации о БД без её открытия.
- Расширение и доработка C++ API:
- добавлен тип `mdbx::cursor::estimation_result`, а поведение методов
`cursor::estimate()` унифицировано с `cursor::move()`;
- для предотвращения незаметного неверного использования API, для инициализации
возвращаемых по ссылке срезов, вместо пустых срезов задействован `slice::invalid()`;
- добавлены дополнительные C++ операторы преобразования к типам C API;
- для совместимости со старыми стандартами C++ и старыми версиями STL перенесены
в public классы `buffer::move_assign_alloc` и `buffer::copy_assign_alloc`;
- добавлен тип `mdbx::default_buffer`;
- для срезов и буферов добавлены методы `hex_decode()`, `base64_decode()`, `base58_decode()`;
- добавлен тип `mdbx::comparator` и функций `mdbx::default_comparator()`;
- добавлены статические методы `buffer::hex()`, `base64()`, `base58()`;
- для транзакций и курсоров добавлены методы `get_/set_context`;
- добавлен метод `cursor::clone()`;
- поддержка base58 переработана и приведена в соответствии с черновиком RFC, в текущем понимании теперь это одна из самых высокопроизводительных реализаций;
- переработка `to_hex()` и `from_hex()`.
## v0.13.0 от 2023-04-23
Не выпуск, а начало ветки `0.13` с новым функционалом и изменением API.
Новое:
- Расширение API функционалом проверки целостности структуры БД, с
переработкой и переноса функционала утилиты `mdbx_chk` внутрь библиотеки.
- Расширение API функциями lock/unlock/upgrade/downgrade основной блокировки.
- Добавление в API функций `mdbx_cursor_unbind()` и `mdbx_txn_release_all_cursors()`.
- Возвращение `MDBX_TXN_INVALID` (`INT32_MIN`) вместо `-1`
из `mdbx_txn_flags()` при передаче невалидной транзакции.
Мелочи:
- Обновление конфигурации Doxygen до 1.9.6.
- Добавление `--read-var-info=yes` для Valgrind.
- Вывод из `mdbx_chk` информации об уровне детализации/verbosity.
********************************************************************************
## v0.12.10 "СЭМ" от 2024-03-12
Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов
@ -152,6 +213,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
## v0.12.8 "Владимир Уткин" от 2023-10-17
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
в день 100-летия со дня рождения выдающегося советского и российского ученого и конструктора [Влади́мира Фёдоровича У́ткина](https://ru.wikipedia.org/wiki/Уткин,_Владимир_Фёдорович).

View File

@ -172,22 +172,22 @@ help:
@echo " make bench-clean - remove temp database(s) after benchmark"
#> dist-cutoff-begin
@echo ""
@echo " make smoke - fast smoke test"
@echo " make test - basic test"
@echo " make check - smoke test with amalgamation and installation checking"
@echo " make long-test - execute long test which runs for several weeks, or until you interrupt it"
@echo " make memcheck - build with Valgrind's and smoke test with memcheck tool"
@echo " make test-valgrind - build with Valgrind's and basic test with memcheck tool"
@echo " make test-asan - build with AddressSanitizer and basic test"
@echo " make test-leak - build with LeakSanitizer and basic test"
@echo " make test-ubsan - build with UndefinedBehaviourSanitizer and basic test"
@echo " make smoke - fast smoke test"
@echo " make smoke-memcheck - build with Valgrind support and run smoke test under memcheck tool"
@echo " make smoke-fault - execute transaction owner failure smoke testcase"
@echo " make smoke-singleprocess - execute single-process smoke test"
@echo " make test - basic test"
@echo " make test-memcheck - build with Valgrind support and run basic test under memcheck tool"
@echo " make test-long - execute long test which runs for several weeks, or until interruption"
@echo " make test-asan - build with AddressSanitizer and run basic test"
@echo " make test-leak - build with LeakSanitizer and run basic test"
@echo " make test-ubsan - build with UndefinedBehaviourSanitizer and run basic test"
@echo " make test-singleprocess - execute single-process basic test (also used by make cross-qemu)"
@echo " make cross-gcc - check cross-compilation without test execution"
@echo " make cross-qemu - run cross-compilation and execution basic test with QEMU"
@echo " make gcc-analyzer - run gcc-analyzer (mostly useless for now)"
@echo " make build-test - build test executable(s)"
@echo " make smoke-fault - execute transaction owner failure smoke testcase"
@echo " make smoke-singleprocess - execute single-process smoke test"
@echo " make test-singleprocess - execute single-process basic test (also used by make cross-qemu)"
@echo ""
@echo " make dist - build amalgamated source code"
@echo " make doxygen - build HTML documentation"
@ -328,8 +328,14 @@ else
.PHONY: build-test build-test-with-valgrind check cross-gcc cross-qemu dist doxygen gcc-analyzer long-test
.PHONY: reformat release-assets tags smoke test test-asan smoke-fault test-leak
.PHONY: smoke-singleprocess test-singleprocess test-ubsan test-valgrind memcheck
.PHONY: smoke-assertion test-assertion long-test-assertion
.PHONY: smoke-singleprocess test-singleprocess test-ubsan test-valgrind test-memcheck memcheck smoke-memcheck
.PHONY: smoke-assertion test-assertion long-test-assertion test-ci test-ci-extra
test-ci-extra: test-ci cross-gcc cross-qemu
test-ci: check \
smoke-singleprocess smoke-fault smoke-memcheck smoke \
test-leak test-asan test-ubsan test-singleprocess test test-memcheck
define uname2osal
case "$(UNAME)" in
@ -418,27 +424,30 @@ smoke-fault: build-test
test: build-test
@echo ' RUNNING `test/long_stochastic.sh --loops 2`...'
$(QUIET)test/long_stochastic.sh --dont-check-ram-size --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG) || (cat $(TEST_LOG) && false)
$(QUIET)test/long_stochastic.sh --dont-check-ram-size --loops 2 --db-upto-mb 256 --extra --skip-make --taillog >$(TEST_LOG) || (cat $(TEST_LOG) && false)
long-test: build-test
long-test: test-long
test-long: build-test
@echo ' RUNNING `test/long_stochastic.sh --loops 42`...'
$(QUIET)test/long_stochastic.sh --loops 42 --db-upto-mb 1024 --skip-make
$(QUIET)test/long_stochastic.sh --loops 42 --db-upto-mb 1024 --extra --skip-make --taillog
test-singleprocess: build-test
@echo ' RUNNING `test/long_stochastic.sh --single --loops 2`...'
$(QUIET)test/long_stochastic.sh --dont-check-ram-size --single --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG) || (cat $(TEST_LOG) && false)
$(QUIET)test/long_stochastic.sh --dont-check-ram-size --single --loops 2 --db-upto-mb 256 --skip-make --taillog >$(TEST_LOG) || (cat $(TEST_LOG) && false)
test-valgrind: CFLAGS_EXTRA=-Ofast -DMDBX_USE_VALGRIND
test-valgrind: build-test
test-valgrind: test-memcheck
test-memcheck: CFLAGS_EXTRA=-Ofast -DENABLE_MEMCHECK
test-memcheck: build-test
@echo ' RUNNING `test/long_stochastic.sh --with-valgrind --loops 2`...'
$(QUIET)test/long_stochastic.sh --with-valgrind --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG) || (cat $(TEST_LOG) && false)
$(QUIET)test/long_stochastic.sh --with-valgrind --extra --loops 2 --db-upto-mb 256 --skip-make >$(TEST_LOG) || (cat $(TEST_LOG) && false)
memcheck: VALGRIND=valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt
memcheck: CFLAGS_EXTRA=-Ofast -DMDBX_USE_VALGRIND
memcheck: build-test
memcheck: smoke-memcheck
smoke-memcheck: VALGRIND=valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --read-var-info=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt
smoke-memcheck: CFLAGS_EXTRA=-Ofast -DENABLE_MEMCHECK
smoke-memcheck: build-test
@echo " SMOKE \`mdbx_test basic\` under Valgrind's memcheck..."
$(QUIET)rm -f valgrind-*.log $(TEST_DB) $(TEST_LOG).gz && (set -o pipefail; ( \
$(VALGRIND) ./mdbx_test --table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic && \
$(VALGRIND) ./mdbx_test --table=+data.fixed --keygen.split=29 --datalen=35 --progress --console=no --repeat=2 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic && \
$(VALGRIND) ./mdbx_test --progress --console=no --pathname=$(TEST_DB) --dont-cleanup-before --dont-cleanup-after --copy && \
$(VALGRIND) ./mdbx_test --mode=-writemap,-nosync-safe,-lifo --progress --console=no --repeat=4 --pathname=$(TEST_DB) --dont-cleanup-after $(MDBX_SMOKE_EXTRA) basic && \
$(VALGRIND) ./mdbx_chk -vvn $(TEST_DB) && \

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 \
libmdbx mdbx mdbx_chk mdbx_copy mdbx_drop mdbx_dump mdbx_load mdbx_stat \
check dist memcheck cross-gcc cross-qemu doxygen gcc-analyzer reformat \
release-assets tags test build-test mdbx_test smoke smoke-fault smoke-singleprocess \
smoke-assertion test-assertion long-test-assertion \
test-asan test-leak test-singleprocess test-ubsan test-valgrind:
release-assets tags build-test mdbx_test \
smoke smoke-fault smoke-singleprocess smoke-assertion smoke-memcheck \
test test-assertion test-long test-long-assertion test-ci test-ci-extra \
test-asan test-leak test-singleprocess test-ubsan test-memcheck:
@CC=$(CC) \
CXX=`if test -n "$(CXX)" && which "$(CXX)" > /dev/null; then echo "$(CXX)"; elif test -n "$(CCC)" && which "$(CCC)" > /dev/null; then echo "$(CCC)"; else echo "c++"; fi` \
`which gmake || which gnumake || echo 'echo "GNU Make 3.80 or above is required"; exit 2;'` \

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.
- [Move most of `mdbx_chk` functional to the library API](https://libmdbx.dqdkfa.ru/dead-github/issues/204).
- [Replace SRW-lock on Windows to allow shrink DB with `MDBX_NOTLS` option](https://libmdbx.dqdkfa.ru/dead-github/issues/210).
- [More flexible support of asynchronous runtime/framework(s)](https://libmdbx.dqdkfa.ru/dead-github/issues/200).
- [Migration guide from LMDB to MDBX](https://libmdbx.dqdkfa.ru/dead-github/issues/199).
@ -23,6 +22,7 @@ So currently most of the links are broken due to noted malicious ~~Github~~ sabo
Done
----
- [Move most of `mdbx_chk` functional to the library API](https://libmdbx.dqdkfa.ru/dead-github/issues/204).
- [Simple careful mode for working with corrupted DB](https://libmdbx.dqdkfa.ru/dead-github/issues/223).
- [Engage an "overlapped I/O" on Windows](https://libmdbx.dqdkfa.ru/dead-github/issues/224).
- [Large/Overflow pages accounting for dirty-room](https://libmdbx.dqdkfa.ru/dead-github/issues/192).

View File

@ -24,6 +24,25 @@ endif()
cmake_policy(PUSH)
cmake_policy(VERSION ${CMAKE_MINIMUM_REQUIRED_VERSION})
unset(MEMCHECK_OPTION_NAME)
if(NOT DEFINED ENABLE_MEMCHECK)
if (DEFINED MDBX_USE_VALGRIND)
set(MEMCHECK_OPTION_NAME "MDBX_USE_VALGRIND")
elseif(DEFINED ENABLE_VALGRIND)
set(MEMCHECK_OPTION_NAME "ENABLE_VALGRIND")
else()
set(MEMCHECK_OPTION_NAME "ENABLE_MEMCHECK")
endif()
if(MEMCHECK_OPTION_NAME STREQUAL "ENABLE_MEMCHECK")
option(ENABLE_MEMCHECK
"Enable integration with valgrind, a memory analyzing tool" OFF)
elseif(${MEMCHECK_OPTION_NAME})
set(ENABLE_MEMCHECK ON)
else()
set(ENABLE_MEMCHECK OFF)
endif()
endif()
include(CheckLibraryExists)
check_library_exists(gcov __gcov_flush "" HAVE_GCOV)
@ -33,6 +52,13 @@ option(ENABLE_GCOV
option(ENABLE_GPROF
"Enable integration with gprof, a performance analyzing tool" OFF)
option(ENABLE_ASAN
"Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF)
option(ENABLE_UBSAN
"Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector based on compiler instrumentation" OFF)
if(ENABLE_MEMCHECK)
if(CMAKE_CXX_COMPILER_LOADED)
include(CheckIncludeFileCXX)
check_include_file_cxx(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
@ -40,16 +66,9 @@ 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")
if(NOT HAVE_VALGRIND_MEMCHECK_H)
message(FATAL_ERROR "${MEMCHECK_OPTION_NAME} option is set but valgrind/memcheck.h is not found")
endif()
endif()
option(ENABLE_ASAN
"Enable AddressSanitizer, a fast memory error detector based on compiler instrumentation" OFF)
option(ENABLE_UBSAN
"Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector based on compiler instrumentation" OFF)
cmake_policy(POP)

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
# doxygen (www.doxygen.org) for a project.
@ -12,6 +12,16 @@
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
#
# Note:
#
# Use doxygen to compare the used configuration file with the template
# configuration file:
# doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables or CMake type
# replacement variables:
# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
# Project related configuration options
@ -60,16 +70,28 @@ PROJECT_LOGO =
OUTPUT_DIRECTORY = .
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
# will distribute the generated files over these directories. Enabling this
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
# and will distribute the generated files over these directories. Enabling this
# option can be useful when feeding doxygen a huge amount of source files, where
# putting all generated files in the same directory would otherwise causes
# performance problems for the file system.
# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
# control the number of sub-directories.
# The default value is: NO.
CREATE_SUBDIRS = NO
# Controls the number of sub-directories that will be created when
# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
# level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed
# number of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
CREATE_SUBDIRS_LEVEL = 8
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
@ -81,26 +103,18 @@ ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
# Ukrainian and Vietnamese.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
# English messages), Korean, Korean-en (Korean with English messages), Latvian,
# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
# Swedish, Turkish, Ukrainian and Vietnamese.
# The default value is: English.
OUTPUT_LANGUAGE = English
# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all generated output in the proper direction.
# Possible values are: None, LTR, RTL and Context.
# The default value is: None.
OUTPUT_TEXT_DIRECTION = None
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
@ -258,16 +272,16 @@ TAB_SIZE = 4
# the documentation. An alias has the form:
# name=value
# For example adding
# "sideeffect=@par Side Effects:\n"
# "sideeffect=@par Side Effects:^^"
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
# "Side Effects:". You can put \n's in the value part of an alias to insert
# newlines (in the resulting output). You can put ^^ in the value part of an
# alias to insert a newline as if a physical newline was in the original file.
# When you need a literal { or } or , in the value part of an alias you have to
# escape them by means of a backslash (\), this can lead to conflicts with the
# commands \{ and \} for these it is advised to use the version @{ and @} or use
# a double escape (\\{ and \\})
# "Side Effects:". Note that you cannot put \n's in the value part of an alias
# to insert newlines (in the resulting output). You can put ^^ in the value part
# of an alias to insert a newline as if a physical newline was in the original
# file. When you need a literal { or } or , in the value part of an alias you
# have to escape them by means of a backslash (\), this can lead to conflicts
# with the commands \{ and \} for these it is advised to use the version @{ and
# @} or use a double escape (\\{ and \\})
ALIASES =
@ -312,8 +326,8 @@ OPTIMIZE_OUTPUT_SLICE = NO
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
# default for Fortran type files). For instance to make doxygen treat .inc files
@ -460,13 +474,13 @@ TYPEDEF_HIDES_STRUCT = YES
LOOKUP_CACHE_SIZE = 0
# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
# during processing. When set to 0 doxygen will based this on the number of
# cores available in the system. You can set it explicitly to a value larger
# than 0 to get more control over the balance between CPU load and processing
# speed. At this moment only the input processing can be done using multiple
# threads. Since this is still an experimental feature the default is set to 1,
# which efficively disables parallel processing. Please report any issues you
# which effectively disables parallel processing. Please report any issues you
# encounter. Generating dot graphs in parallel is controlled by the
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
@ -554,7 +568,8 @@ HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
# has no effect if EXTRACT_ALL is enabled.
# will also hide undocumented C++ concepts if enabled. This option has no effect
# if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
@ -585,14 +600,15 @@ INTERNAL_DOCS = NO
# filesystem is case sensitive (i.e. it supports files in the same directory
# whose names only differ in casing), the option must be set to YES to properly
# deal with such files in case they appear in the input. For filesystems that
# are not case sensitive the option should be be set to NO to properly deal with
# are not case sensitive the option should be set to NO to properly deal with
# output files written for symbols that only differ in casing, such as for two
# classes, one named CLASS and the other named Class, and to also support
# references to files without having to specify the exact matching casing. On
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# The default value is: system dependent.
# Possible values are: SYSTEM, NO and YES.
# The default value is: SYSTEM.
CASE_SENSE_NAMES = NO
@ -610,6 +626,12 @@ HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
# will show which file needs to be included to use the class.
# The default value is: YES.
SHOW_HEADERFILE = YES
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file.
# The default value is: YES.
@ -767,7 +789,8 @@ FILE_VERSION_FILTER =
# output files in an output format independent way. To create the layout file
# that represents doxygen's defaults, run doxygen with the -l option. You can
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
# will be used as the name of the layout file.
# will be used as the name of the layout file. See also section "Changing the
# layout of pages" for information.
#
# Note that if you run doxygen from a directory containing a file called
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
@ -813,22 +836,38 @@ WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some parameters
# in a documented function, or documenting parameters that don't exist or using
# markup commands wrongly.
# potential errors in the documentation, such as documenting some parameters in
# a documented function twice, or documenting parameters that don't exist or
# using markup commands wrongly.
# The default value is: YES.
WARN_IF_DOC_ERROR = YES
# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
# function parameter documentation. If set to NO, doxygen will accept that some
# parameters have no documentation without warning.
# The default value is: YES.
WARN_IF_INCOMPLETE_DOC = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong or incomplete
# parameter documentation, but not about the absence of documentation. If
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
# value. If set to NO, doxygen will only warn about wrong parameter
# documentation, but not about the absence of documentation. If EXTRACT_ALL is
# set to YES then this flag will automatically be disabled. See also
# WARN_IF_INCOMPLETE_DOC
# The default value is: NO.
WARN_NO_PARAMDOC = NO
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
# undocumented enumeration values. If set to NO, doxygen will accept
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: NO.
WARN_IF_UNDOC_ENUM_VAL = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
@ -844,13 +883,27 @@ WARN_AS_ERROR = NO
# and the warning text. Optionally the format may contain $version, which will
# be replaced by the version of the file (if it could be obtained via
# FILE_VERSION_FILTER)
# See also: WARN_LINE_FORMAT
# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
# In the $text part of the WARN_FORMAT command it is possible that a reference
# to a more specific place is given. To make it easier to jump to this place
# (outside of doxygen) the user can define a custom "cut" / "paste" string.
# Example:
# WARN_LINE_FORMAT = "'vi $file +$line'"
# See also: WARN_FORMAT
# The default value is: at line $line of file $file.
WARN_LINE_FORMAT = "at line $line of file $file"
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
# messages should be written. If left blank the output is written to standard
# error (stderr).
# error (stderr). In case the file specified cannot be opened for writing the
# warning and error messages are written to standard error. When as file - is
# specified the warning and error messages are written to standard output
# (stdout).
WARN_LOGFILE =
@ -877,10 +930,21 @@ INPUT = overall.md \
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# This tag can be used to specify the character encoding of the source files
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
# character encoding on a per file pattern basis. Doxygen will compare the file
# name with each pattern and apply the encoding instead of the default
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
# "INPUT_ENCODING" for further information on supported encodings.
INPUT_FILE_ENCODING =
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
@ -894,10 +958,10 @@ INPUT_ENCODING = UTF-8
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
# *.ucf, *.qsf and *.ice.
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h
@ -936,20 +1000,40 @@ EXCLUDE_PATTERNS =
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
# ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS = NOMINMAX __ORDER_BIG_ENDIAN__ __ORDER_LITTLE_ENDIAN__ \
__has_include __has_attribute __has_builtin __has_cpp_attribute __has_extension __has_feature \
HAVE_STRUCT_IOVEC MDBX_STRINGIFY_HELPER MDBX_STRINGIFY \
MDBX_NOSANITIZE_ENUM MDBX_PRINTF_ARGS \
EXCLUDE_SYMBOLS = NOMINMAX \
__ORDER_BIG_ENDIAN__ \
__ORDER_LITTLE_ENDIAN__ \
__has_include \
__has_attribute \
__has_builtin \
__has_cpp_attribute \
__has_extension \
__has_feature \
HAVE_STRUCT_IOVEC \
MDBX_STRINGIFY_HELPER \
MDBX_STRINGIFY \
MDBX_NOSANITIZE_ENUM \
MDBX_PRINTF_ARGS \
MDBX_HAVE_CXX20_CONCEPTS \
CONSTEXPR_ENUM_FLAGS_OPERATIONS DEFINE_ENUM_FLAG_OPERATORS \
bool false true __dll_export __dll_import \
MDBX_64BIT_ATOMIC_CONFIG MDBX_64BIT_CAS_CONFIG MDBX_ENV_CHECKPID_CONFIG MDBX_LOCKING_CONFIG \
MDBX_TRUST_RTC_CONFIG MDBX_TXN_CHECKOWNER_CONFIG MDBX_USE_OFDLOCKS_CONFIG
CONSTEXPR_ENUM_FLAGS_OPERATIONS \
DEFINE_ENUM_FLAG_OPERATORS \
bool \
false \
true \
__dll_export \
__dll_import \
MDBX_64BIT_ATOMIC_CONFIG \
MDBX_64BIT_CAS_CONFIG \
MDBX_ENV_CHECKPID_CONFIG \
MDBX_LOCKING_CONFIG \
MDBX_TRUST_RTC_CONFIG \
MDBX_TXN_CHECKOWNER_CONFIG \
MDBX_USE_OFDLOCKS_CONFIG
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
@ -992,6 +1076,11 @@ IMAGE_PATH =
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that doxygen will use the data processed and written to standard output
# for further processing, therefore nothing else, like debug statements or used
# commands (so in case of a Windows batch file always use @echo OFF), should be
# written to standard output.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
@ -1033,6 +1122,15 @@ FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE =
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
# extension is to allow longer lines before the automatic comment starts. The
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
# be processed before the automatic comment starts.
# Minimum value: 7, maximum value: 10000, default value: 72.
FORTRAN_COMMENT_AFTER = 72
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
@ -1130,9 +1228,11 @@ VERBATIM_HEADERS = YES
CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to
# YES then doxygen will add the directory of each input to the include path.
# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
# tag is set to YES then doxygen will add the directory of each input to the
# include path.
# The default value is: YES.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_ADD_INC_PATHS = YES
@ -1168,10 +1268,11 @@ CLANG_DATABASE_PATH =
ALPHABETICAL_INDEX = YES
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
# while generating the index headers.
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
# that should be ignored while generating the index headers. The IGNORE_PREFIX
# tag works for classes, function and member names. The entity will be placed in
# the alphabetical list under the first letter of the entity name that remains
# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
@ -1250,7 +1351,12 @@ HTML_STYLESHEET =
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list). For an example see the documentation.
# list).
# Note: Since the styling of scrollbars can currently not be overruled in
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
# one or more extra stylesheets have been specified. So if scrollbar
# customization is desired it has to be added explicitly. For an example see the
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
@ -1265,9 +1371,22 @@ HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme.
# Possible values are: LIGHT always generate light mode output, DARK always
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
# the user preference, use light mode if no preference is set (the default),
# AUTO_DARK automatically set the mode according to the user preference, use
# dark mode if no preference is set and TOGGLE allow to user to switch between
# light and dark mode via a button.
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE = AUTO_LIGHT
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a colorwheel, see
# this color. Hue is specified as an angle on a color-wheel, see
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
@ -1277,7 +1396,7 @@ HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use grayscales only. A
# in the HTML output. For a value of 0 the output will use gray-scales only. A
# value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
@ -1359,6 +1478,13 @@ GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
# This tag determines the URL of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDURL =
# This tag specifies a string that should uniquely identify the documentation
# set bundle. This should be a reverse domain-name style string, e.g.
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
@ -1384,8 +1510,12 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# (see:
# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
# on Windows. In the beginning of 2021 Microsoft took the original page, with
# a.o. the download links, offline the HTML help workshop was already many years
# in maintenance mode). You can download the HTML help workshop from the web
# archives at Installation executable (see:
# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
@ -1544,16 +1674,28 @@ DISABLE_INDEX = YES
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
# (i.e. any modern browser). Windows users are probably better off using the
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
# further fine-tune the look of the index. As an example, the default style
# sheet generated by doxygen has an example that shows how to put an image at
# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
# the same information as the tab index, you could consider setting
# DISABLE_INDEX to YES when enabling this option.
# further fine tune the look of the index (see "Fine-tuning the output"). As an
# example, the default style sheet generated by doxygen has an example that
# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
# Since the tree basically has the same information as the tab index, you could
# consider setting DISABLE_INDEX to YES when enabling this option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = YES
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
# area (value NO) or if it should extend to the full height of the window (value
# YES). Setting this to YES gives a layout similar to
# https://docs.readthedocs.io with more room for contents, but less room for the
# project logo, title, and description. If either GENERATE_TREEVIEW or
# DISABLE_INDEX is set to NO, this option has no effect.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
FULL_SIDEBAR = NO
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
#
@ -1578,6 +1720,13 @@ TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
# addresses.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
OBFUSCATE_EMAILS = YES
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
@ -1598,17 +1747,6 @@ HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
# Note that when changing this option you need to delete any form_*.png files in
# the HTML output directory before the changes have effect.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
@ -1626,11 +1764,29 @@ FORMULA_MACROFILE =
USE_MATHJAX = YES
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
# Note that the different versions of MathJax have different requirements with
# regards to the different settings, so it is possible that also other MathJax
# settings have to be changed when switching between the different MathJax
# versions.
# Possible values are: MathJax_2 and MathJax_3.
# The default value is: MathJax_2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_VERSION = MathJax_2
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
# the MathJax output. For more details about the output format see MathJax
# version 2 (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
# (see:
# http://docs.mathjax.org/en/latest/web/components/output.html).
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility), NativeMML (i.e. MathML) and SVG.
# compatibility. This is the name for Mathjax version 2, for MathJax version 3
# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
# is the name for Mathjax version 3, for MathJax version 2 this will be
# translated into HTML-CSS) and SVG.
# The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES.
@ -1643,15 +1799,21 @@ MATHJAX_FORMAT = HTML-CSS
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment.
# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
# MathJax from https://www.mathjax.org before deployment. The default value is:
# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
# for MathJax version 2 (see
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# For example for MathJax version 3 (see
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
# MATHJAX_EXTENSIONS = ams
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
@ -1831,29 +1993,31 @@ PAPER_TYPE = a4
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
# generated LaTeX document. The header should contain everything until the first
# chapter. If it is left blank doxygen will generate a standard header. See
# section "Doxygen usage" for information on how to let doxygen write the
# default header to a separate file.
# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
# the generated LaTeX document. The header should contain everything until the
# first chapter. If it is left blank doxygen will generate a standard header. It
# is highly recommended to start with a default header using
# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
# and then modify the file new_header.tex. See also section "Doxygen usage" for
# information on how to generate the default header that doxygen normally uses.
#
# Note: Only use a user-defined header if you know what you are doing! The
# following commands have a special meaning inside the header: $title,
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
# string, for the replacement values of the other commands the user is referred
# to HTML_HEADER.
# Note: Only use a user-defined header if you know what you are doing!
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. The following
# commands have a special meaning inside the header (and footer): For a
# description of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
# generated LaTeX document. The footer should contain everything after the last
# chapter. If it is left blank doxygen will generate a standard footer. See
# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
# the generated LaTeX document. The footer should contain everything after the
# last chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer.
#
# Note: Only use a user-defined footer if you know what you are doing!
# special commands can be used inside the footer. See also section "Doxygen
# usage" for information on how to generate the default footer that doxygen
# normally uses. Note: Only use a user-defined footer if you know what you are
# doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
@ -1898,8 +2062,7 @@ USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help. This option is also used
# when generating formulas in HTML.
# if errors occur, instead of asking the user for help.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@ -1912,16 +2075,6 @@ LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
# code with syntax highlighting in the LaTeX output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
@ -2002,16 +2155,6 @@ RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
# with syntax highlighting in the RTF output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
@ -2108,15 +2251,6 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
# program listings (including syntax highlighting and cross-referencing
# information) to the DOCBOOK output. Note that enabling this will significantly
# increase the size of the DOCBOOK output.
# The default value is: NO.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
@ -2203,7 +2337,8 @@ SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by the
# preprocessor.
# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
# RECURSIVE has no effect here.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
@ -2224,22 +2359,28 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = DOXYGEN \
MDBX_CXX20_CONCEPT(CONCEPT,NAME)="CONCEPT NAME" \
"MDBX_CXX20_CONCEPT(CONCEPT,NAME)=CONCEPT NAME" \
MDBX_STD_FILESYSTEM_PATH=::mdbx::filesystem::path \
MDBX_U128_TYPE=uint128_t MDBX_I128_TYPE=int128_t \
MDBX_DECLARE_EXCEPTION(NAME)="struct LIBMDBX_API_TYPE NAME : public exception{NAME(const ::mdbx::error &); virtual ~NAME() noexcept; }" \
MDBX_U128_TYPE=uint128_t \
MDBX_I128_TYPE=int128_t \
"MDBX_DECLARE_EXCEPTION(NAME)=struct LIBMDBX_API_TYPE NAME : public exception{NAME(const ::mdbx::error &); virtual ~NAME() noexcept; }" \
MDBX_PURE_FUNCTION=[[gnu::pure]] \
MDBX_NOTHROW_PURE_FUNCTION="[[gnu::pure, gnu::nothrow]]" \
"MDBX_NOTHROW_PURE_FUNCTION=[[gnu::pure, gnu::nothrow]]" \
MDBX_CONST_FUNCTION=[[gnu::const]] \
MDBX_NOTHROW_CONST_FUNCTION="[[gnu::const, gnu::nothrow]]" \
MDBX_CXX01_CONSTEXPR=constexpr MDBX_CXX01_CONSTEXPR_VAR=constexpr \
MDBX_CXX11_CONSTEXPR=constexpr MDBX_CXX11_CONSTEXPR_VAR=constexpr \
MDBX_CXX14_CONSTEXPR=constexpr MDBX_CXX14_CONSTEXPR_VAR=constexpr \
MDBX_CXX17_CONSTEXPR=constexpr MDBX_CXX20_CONSTEXPR=constexpr \
MDBX_CXX17_NOEXCEPT=noexcept MDBX_IF_CONSTEXPR=constexpr \
MDBX_CXX20_LIKELY=[[likely]] MDBX_CXX20_UNLIKELY=[[unlikely]] \
"MDBX_NOTHROW_CONST_FUNCTION=[[gnu::const, gnu::nothrow]]" \
MDBX_CXX01_CONSTEXPR=constexpr \
MDBX_CXX01_CONSTEXPR_VAR=constexpr \
MDBX_CXX11_CONSTEXPR=constexpr \
MDBX_CXX11_CONSTEXPR_VAR=constexpr \
MDBX_CXX14_CONSTEXPR=constexpr \
MDBX_CXX14_CONSTEXPR_VAR=constexpr \
MDBX_CXX17_CONSTEXPR=constexpr \
MDBX_CXX20_CONSTEXPR=constexpr \
MDBX_CXX17_NOEXCEPT=noexcept \
MDBX_IF_CONSTEXPR=constexpr \
MDBX_CXX20_LIKELY=[[likely]] \
MDBX_CXX20_UNLIKELY=[[unlikely]] \
MDBX_MAYBE_UNUSED=[[maybe_unused]] \
MDBX_DEPRECATED=[[deprecated]]
@ -2312,15 +2453,6 @@ EXTERNAL_PAGES = NO
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
# NO turns the diagrams off. Note that this option also works with HAVE_DOT
# disabled, but it is recommended to install and use dot, since it yields more
# powerful graphs.
# The default value is: YES.
CLASS_DIAGRAMS = NO
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
@ -2339,7 +2471,7 @@ HIDE_UNDOC_RELATIONS = YES
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: YES.
# The default value is: NO.
HAVE_DOT = NO
@ -2353,37 +2485,52 @@ HAVE_DOT = NO
DOT_NUM_THREADS = 0
# When you want a differently looking font in the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
# setting DOT_FONTPATH to the directory containing the font.
# The default value is: Helvetica.
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
# subgraphs. When you want a differently looking font in the dot files that
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
# Edge and Graph Attributes specification</a> You need to make sure dot is able
# to find the font, which can be done by putting it in a standard location or by
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font. Default graphviz fontsize is 14.
# The default value is: fontname=Helvetica,fontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTNAME = Helvetica
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
# dot graphs.
# Minimum value: 4, maximum value: 24, default value: 10.
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
# arrows shapes.</a>
# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTSIZE = 10
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
# By default doxygen will tell dot to use the default font as specified with
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
# the path where dot can find it using this tag.
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# around nodes set 'shape=plain' or 'shape=plaintext' <a
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
# The default value is: shape=box,height=0.2,width=0.4.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
# You can set the path where dot can find font specified with fontname in
# DOT_COMMON_ATTR and others dot attributes.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
# each documented class showing the direct and indirect inheritance relations.
# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# graph for each documented class showing the direct and indirect inheritance
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
# to TEXT the direct and indirect inheritance relations will be shown as texts /
# links.
# Possible values are: NO, YES, TEXT and GRAPH.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
CLASS_GRAPH = YES
CLASS_GRAPH = TEXT
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
@ -2395,7 +2542,8 @@ CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies.
# groups, showing the direct groups dependencies. See also the chapter Grouping
# in the manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@ -2510,6 +2658,13 @@ GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
# of child directories generated in directory dependency graphs by dot.
# Minimum value: 1, maximum value: 25, default value: 1.
# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
@ -2517,9 +2672,7 @@ DIRECTORY_GRAPH = YES
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
@ -2565,10 +2718,10 @@ MSCFILE_DIRS =
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file. If left blank, it is assumed
# PlantUML is not used or called during a preprocessing step. Doxygen will
# generate a warning when it encounters a \startuml command in this case and
# will not generate output for the diagram.
# path where java can find the plantuml.jar file or to the filename of jar file
# to be used. If left blank, it is assumed PlantUML is not used or called during
# a preprocessing step. Doxygen will generate a warning when it encounters a
# \startuml command in this case and will not generate output for the diagram.
PLANTUML_JAR_PATH =
@ -2606,18 +2759,6 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, because dot on Windows does not seem
# to support this out of the box.
#
# Warning: Depending on the platform used, enabling this option may lead to
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
# read).
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
@ -2630,6 +2771,8 @@ DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
# explaining the meaning of the various boxes and arrows in the dot generated
# graphs.
# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
# graphical representation for inheritance and collaboration diagrams is used.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@ -2638,8 +2781,8 @@ GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
#
# Note: This setting is not only used for dot files but also for msc and
# plantuml temporary files.
# Note: This setting is not only used for dot files but also for msc temporary
# files.
# The default value is: YES.
DOT_CLEANUP = YES

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
mdbx_env_set_geometry() or \ref mdbx::env::geometry. So just avoid this.
## Remote filesystems
Do not use MDBX databases on remote filesystems, even between processes
on the same host. This breaks file locks on some platforms, possibly
@ -132,6 +133,11 @@ corruption in such cases.
On the other hand, MDBX allow calling \ref mdbx_env_close() in such cases to
release resources, but no more and in general this is a wrong way.
#### Since v0.13.1 and later
Начиная с версии 0.13.1 в API доступна функция \ref mdbx_env_resurrect_after_fork(),
которая позволяет пере-использовать в дочерних процессах уже открытую среду БД,
но строго без наследования транзакций от родительского процесса.
## Read-only mode
There is no pure read-only mode in a normal explicitly way, since

526
mdbx.h
View File

@ -634,9 +634,9 @@ typedef mode_t mdbx_mode_t;
extern "C" {
#endif
/* MDBX version 0.12.x */
/* MDBX version 0.13.x */
#define MDBX_VERSION_MAJOR 0
#define MDBX_VERSION_MINOR 12
#define MDBX_VERSION_MINOR 13
#ifndef LIBMDBX_API
#if defined(LIBMDBX_EXPORTS)
@ -816,7 +816,7 @@ typedef struct iovec MDBX_val;
#endif /* ! SunOS */
enum MDBX_constants {
/** The hard limit for DBI handles */
/** The hard limit for DBI handles. */
MDBX_MAX_DBI = UINT32_C(32765),
/** The maximum size of a data item. */
@ -1778,7 +1778,7 @@ enum MDBX_cursor_op {
* return both key and data, and the return code depends on whether a
* upper-bound was found.
*
* For non DUPSORT-ed collections this work the same to \ref MDBX_SET_RANGE,
* For non DUPSORT-ed collections this work like \ref MDBX_SET_RANGE,
* but returns \ref MDBX_SUCCESS if the greater key was found or
* \ref MDBX_NOTFOUND otherwise.
*
@ -1786,7 +1786,28 @@ enum MDBX_cursor_op {
* i.e. for a pairs/tuples of a key and an each data value of duplicates.
* Returns \ref MDBX_SUCCESS if the greater pair was returned or
* \ref MDBX_NOTFOUND otherwise. */
MDBX_SET_UPPERBOUND
MDBX_SET_UPPERBOUND,
/* Doubtless cursor positioning at a specified key. */
MDBX_TO_KEY_LESSER_THAN,
MDBX_TO_KEY_LESSER_OR_EQUAL,
MDBX_TO_KEY_EQUAL,
MDBX_TO_KEY_GREATER_OR_EQUAL,
MDBX_TO_KEY_GREATER_THAN,
/* Doubtless cursor positioning at a specified key-value pair
* for dupsort/multi-value hives. */
MDBX_TO_EXACT_KEY_VALUE_LESSER_THAN,
MDBX_TO_EXACT_KEY_VALUE_LESSER_OR_EQUAL,
MDBX_TO_EXACT_KEY_VALUE_EQUAL,
MDBX_TO_EXACT_KEY_VALUE_GREATER_OR_EQUAL,
MDBX_TO_EXACT_KEY_VALUE_GREATER_THAN,
MDBX_TO_PAIR_LESSER_THAN,
MDBX_TO_PAIR_LESSER_OR_EQUAL,
MDBX_TO_PAIR_EQUAL,
MDBX_TO_PAIR_GREATER_OR_EQUAL,
MDBX_TO_PAIR_GREATER_THAN
};
#ifndef __cplusplus
/** \ingroup c_cursors */
@ -1921,7 +1942,7 @@ enum MDBX_error_t {
MDBX_TOO_LARGE = -30417,
/** A thread has attempted to use a not owned object,
* e.g. a transaction that started by another thread. */
* e.g. a transaction that started by another thread */
MDBX_THREAD_MISMATCH = -30416,
/** Overlapping read and write transactions for the current thread */
@ -1936,8 +1957,12 @@ enum MDBX_error_t {
/** Alternative/Duplicate LCK-file is exists and should be removed manually */
MDBX_DUPLICATED_CLK = -30413,
/** Some cursors and/or other resources should be closed before subDb or
* corresponding DBI-handle could be (re)used */
MDBX_DANGLING_DBI = -30412,
/* The last of MDBX-added error codes */
MDBX_LAST_ADDED_ERRCODE = MDBX_DUPLICATED_CLK,
MDBX_LAST_ADDED_ERRCODE = MDBX_DANGLING_DBI,
#if defined(_WIN32) || defined(_WIN64)
MDBX_ENODATA = ERROR_HANDLE_EOF,
@ -1950,7 +1975,8 @@ enum MDBX_error_t {
MDBX_EPERM = ERROR_INVALID_FUNCTION,
MDBX_EINTR = ERROR_CANCELLED,
MDBX_ENOFILE = ERROR_FILE_NOT_FOUND,
MDBX_EREMOTE = ERROR_REMOTE_STORAGE_MEDIA_ERROR
MDBX_EREMOTE = ERROR_REMOTE_STORAGE_MEDIA_ERROR,
MDBX_EDEADLK = ERROR_POSSIBLE_DEADLOCK
#else /* Windows */
#ifdef ENODATA
MDBX_ENODATA = ENODATA,
@ -1966,7 +1992,8 @@ enum MDBX_error_t {
MDBX_EPERM = EPERM,
MDBX_EINTR = EINTR,
MDBX_ENOFILE = ENOENT,
MDBX_EREMOTE = ENOTBLK
MDBX_EREMOTE = ENOTBLK,
MDBX_EDEADLK = EDEADLK
#endif /* !Windows */
};
#ifndef __cplusplus
@ -2106,6 +2133,7 @@ enum MDBX_option_t {
/** \brief Controls the in-process limit to grow a list of reclaimed/recycled
* page's numbers for finding a sequence of contiguous pages for large data
* items.
* \see MDBX_opt_gc_time_limit
*
* \details A long values requires allocation of contiguous database pages.
* To find such sequences, it may be necessary to accumulate very large lists,
@ -2266,6 +2294,33 @@ enum MDBX_option_t {
* in the \ref MDBX_WRITEMAP mode by clearing ones through file handle before
* touching. */
MDBX_opt_prefault_write_enable,
/** \brief Controls the in-process spending time limit of searching
* consecutive pages inside GC.
* \see MDBX_opt_rp_augment_limit
*
* \details Задаёт ограничение времени в 1/65536 долях секунды, которое может
* быть потрачено в ходе пишущей транзакции на поиск последовательностей
* страниц внутри GC/freelist после достижения ограничения задаваемого опцией
* \ref MDBX_opt_rp_augment_limit. Контроль по времени не выполняется при
* поиске/выделении одиночных страниц и выделении страниц под нужды GC (при
* обновлении GC в ходе фиксации транзакции).
*
* Задаваемый лимит времени исчисляется по "настенным часам" и контролируется
* в рамках транзакции, наследуется для вложенных транзакций и с
* аккумулированием в родительской при их фиксации. Контроль по времени
* производится только при достижении ограничения задаваемого опцией \ref
* MDBX_opt_rp_augment_limit. Это позволяет гибко управлять поведением
* используя обе опции.
*
* По умолчанию ограничение устанавливается в 0, что приводит к
* незамедлительной остановке поиска в GC при достижении \ref
* MDBX_opt_rp_augment_limit во внутреннем состоянии транзакции и
* соответствует поведению до появления опции `MDBX_opt_gc_time_limit`.
* С другой стороны, при минимальном значении (включая 0)
* `MDBX_opt_rp_augment_limit` переработка GC будет ограничиваться
* преимущественно затраченным временем. */
MDBX_opt_gc_time_limit
};
#ifndef __cplusplus
/** \ingroup c_settings */
@ -2571,9 +2626,7 @@ struct MDBX_envinfo {
uint64_t mi_latter_reader_txnid; /**< ID of the last reader transaction */
uint64_t mi_self_latter_reader_txnid; /**< ID of the last reader transaction
of caller process */
uint64_t mi_meta0_txnid, mi_meta0_sign;
uint64_t mi_meta1_txnid, mi_meta1_sign;
uint64_t mi_meta2_txnid, mi_meta2_sign;
uint64_t mi_meta_txnid[3], mi_meta_sign[3];
uint32_t mi_maxreaders; /**< Total reader slots in the environment */
uint32_t mi_numreaders; /**< Max reader slots used in the environment */
uint32_t mi_dxb_pagesize; /**< Database pagesize */
@ -2590,7 +2643,7 @@ struct MDBX_envinfo {
struct {
struct {
uint64_t x, y;
} current, meta0, meta1, meta2;
} current, meta[3];
} mi_bootid;
/** Bytes not explicitly synchronized to disk */
@ -2891,6 +2944,83 @@ LIBMDBX_INLINE_API(int, mdbx_env_close, (MDBX_env * env)) {
return mdbx_env_close_ex(env, false);
}
#if defined(DOXYGEN) || !(defined(_WIN32) || defined(_WIN64))
/** \brief Восстанавливает экземпляр среды в дочернем процессе после ветвления
* родительского процесса посредством `fork()` и родственных системных вызовов.
* \ingroup c_extra
*
* Без вызова \ref mdbx_env_resurrect_after_fork() использование открытого
* экземпляра среды в дочернем процессе не возможно, включая все выполняющиеся
* на момент ветвления транзакции.
*
* Выполняемые функцией действия можно рассматривать как повторное открытие БД
* в дочернем процессе, с сохранением заданных опций и адресов уже созданных
* экземпляров объектов связанных с API.
*
* \note Функция не доступна в ОС семейства Windows по причине отсутствия
* функционала ветвления процесса в API операционной системы.
*
* Ветвление не оказывает влияния на состояние MDBX-среды в родительском
* процессе. Все транзакции, которые были в родительском процессе на момент
* ветвления, после ветвления в родительском процессе продолжат выполняться без
* помех. Но в дочернем процессе все соответствующие транзакции безальтернативно
* перестают быть валидными, а попытка их использования приведет к возврату
* ошибки или отправке `SIGSEGV`.
*
* Использование экземпляра среды в дочернем процессе не возможно до вызова
* \ref mdbx_env_resurrect_after_fork(), так как в результате ветвления у
* процесса меняется PID, значение которого используется для организации
* совместно работы с БД, в том числе, для отслеживания процессов/потоков
* выполняющих читающие транзакции связанные с соответствующими снимками данных.
* Все активные на момент ветвления транзакции не могут продолжаться в дочернем
* процессе, так как не владеют какими-либо блокировками или каким-либо снимком
* данных и не удерживает его от переработки при сборке мусора.
*
* Функция \ref mdbx_env_resurrect_after_fork() восстанавливает переданный
* экземпляр среды в дочернем процессе после ветвления, а именно: обновляет
* используемые системные идентификаторы, повторно открывает дескрипторы файлов,
* производит захват необходимых блокировок связанных с LCK- и DXB-файлами БД,
* восстанавливает отображения в память страницы БД, таблицы читателей и
* служебных/вспомогательных данных в память. Однако унаследованные от
* родительского процесса транзакции не восстанавливаются, прием пишущие и
* читающие транзакции обрабатываются по-разному:
*
* - Пишущая транзакция, если таковая была на момент ветвления,
* прерывается в дочернем процессе с освобождение связанных с ней ресурсов,
* включая все вложенные транзакции.
*
* - Читающие же транзакции, если таковые были в родительском процессе,
* в дочернем процессе логически прерываются, но без освобождения ресурсов.
* Поэтому необходимо обеспечить вызов \ref mdbx_txn_abort() для каждой
* такой читающей транзакций в дочернем процессе, либо смириться с утечкой
* ресурсов до завершения дочернего процесса.
*
* Причина не-освобождения ресурсов читающих транзакций в том, что исторически
* MDBX не ведет какой-либо общий список экземпляров читающих, так как это не
* требуется для штатных режимов работы, но требует использования атомарных
* операций или дополнительных объектов синхронизации при создании/разрушении
* экземпляров \ref MDBX_txn.
*
* Вызов \ref mdbx_env_resurrect_after_fork() без ветвления, не в дочернем
* процессе, либо повторные вызовы не приводят к каким-либо действиям или
* изменениям.
*
* \returns Ненулевое значение ошибки при сбое и 0 при успешном выполнении,
* некоторые возможные ошибки таковы:
*
* \retval MDBX_BUSY В родительском процессе БД была открыта
* в режиме \ref MDBX_EXCLUSIVE.
*
* \retval MDBX_EBADSIGN При повреждении сигнатуры экземпляра объекта, а также
* в случае одновременного вызова \ref
* mdbx_env_resurrect_after_fork() из разных потоков.
*
* \retval MDBX_PANIC Произошла критическая ошибка при восстановлении
* экземпляра среды, либо такая ошибка уже была
* до вызова функции. */
LIBMDBX_API int mdbx_env_resurrect_after_fork(MDBX_env *env);
#endif /* Windows */
/** \brief Warming up options
* \ingroup c_settings
* \anchor warmup_flags
@ -3310,6 +3440,12 @@ mdbx_limits_dbsize_max(intptr_t pagesize);
MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
mdbx_limits_keysize_max(intptr_t pagesize, MDBX_db_flags_t flags);
/** \brief Returns minimal key size in bytes for given database flags.
* \ingroup c_statinfo
* \see db_flags */
MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
mdbx_limits_keysize_min(MDBX_db_flags_t flags);
/** \brief Returns maximal data size in bytes for given page size
* and database flags, or -1 if pagesize is invalid.
* \ingroup c_statinfo
@ -3317,6 +3453,12 @@ mdbx_limits_keysize_max(intptr_t pagesize, MDBX_db_flags_t flags);
MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
mdbx_limits_valsize_max(intptr_t pagesize, MDBX_db_flags_t flags);
/** \brief Returns minimal data size in bytes for given database flags.
* \ingroup c_statinfo
* \see db_flags */
MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t
mdbx_limits_valsize_min(MDBX_db_flags_t flags);
/** \brief Returns maximal size of key-value pair to fit in a single page with
* the given size and database flags, or -1 if pagesize is invalid.
* \ingroup c_statinfo
@ -3766,7 +3908,7 @@ mdbx_txn_env(const MDBX_txn *txn);
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
*
* \returns A transaction flags, valid if input is an valid transaction,
* otherwise -1. */
* otherwise \ref MDBX_TXN_INVALID. */
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int mdbx_txn_flags(const MDBX_txn *txn);
/** \brief Return the transaction's ID.
@ -4221,6 +4363,11 @@ MDBX_DEPRECATED LIBMDBX_API int
mdbx_dbi_open_ex2(MDBX_txn *txn, const MDBX_val *name, MDBX_db_flags_t flags,
MDBX_dbi *dbi, MDBX_cmp_func *keycmp, MDBX_cmp_func *datacmp);
/** FIXME */
LIBMDBX_API int mdbx_dbi_rename(MDBX_txn *txn, MDBX_dbi dbi, const char *name);
LIBMDBX_API int mdbx_dbi_rename2(MDBX_txn *txn, MDBX_dbi dbi,
const MDBX_val *name);
/** \defgroup value2key Value-to-Key functions
* \brief Value-to-Key functions to
* \ref avoid_custom_comparators "avoid using custom comparators"
@ -4734,6 +4881,28 @@ mdbx_cursor_get_userctx(const MDBX_cursor *cursor);
LIBMDBX_API int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *cursor,
MDBX_dbi dbi);
/** \brief Unbind cursor from a transaction.
* \ingroup c_cursors
*
* Unbinded cursor is disassociated with any transactions but still holds
* the original DBI-handle internally. Thus it could be renewed with any running
* transaction or closed.
*
* \see mdbx_cursor_renew()
* \see mdbx_cursor_bind()
* \see mdbx_cursor_close()
*
* \note In contrast to LMDB, the MDBX required that any opened cursors can be
* reused and must be freed explicitly, regardless ones was opened in a
* read-only or write transaction. The REASON for this is eliminates ambiguity
* which helps to avoid errors such as: use-after-free, double-free, i.e.
* memory corruption and segfaults.
*
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open().
*
* \returns A non-zero error value on failure and 0 on success. */
LIBMDBX_API int mdbx_cursor_unbind(MDBX_cursor *cursor);
/** \brief Create a cursor handle for the specified transaction and DBI handle.
* \ingroup c_cursors
*
@ -4783,6 +4952,27 @@ LIBMDBX_API int mdbx_cursor_open(const MDBX_txn *txn, MDBX_dbi dbi,
* or \ref mdbx_cursor_create(). */
LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
/** \brief Unbind or closes all cursors of a given transaction.
* \ingroup c_cursors
*
* Unbinds either closes all cursors associated (opened or renewed) with
* a given transaction in a bulk with minimal overhead.
*
* \see mdbx_cursor_unbind()
* \see mdbx_cursor_close()
*
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
* \param [in] unbind If non-zero, unbinds cursors and leaves ones reusable.
* Otherwise close and dispose cursors.
*
* \returns A negative error value on failure or the number of closed cursors
* on success, some possible errors are:
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_BAD_TXN Given transaction is invalid or has
* a child/nested transaction transaction. */
LIBMDBX_API int mdbx_txn_release_all_cursors(const MDBX_txn *txn, bool unbind);
/** \brief Renew a cursor handle for use within the given transaction.
* \ingroup c_cursors
*
@ -4834,6 +5024,11 @@ LIBMDBX_API MDBX_dbi mdbx_cursor_dbi(const MDBX_cursor *cursor);
* \returns A non-zero error value on failure and 0 on success. */
LIBMDBX_API int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest);
/** FIXME */
LIBMDBX_API int mdbx_cursor_compare(const MDBX_cursor *left,
const MDBX_cursor *right,
bool ignore_nested);
/** \brief Retrieve by cursor.
* \ingroup c_crud
*
@ -4867,6 +5062,21 @@ LIBMDBX_API int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest);
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key,
MDBX_val *data, MDBX_cursor_op op);
/** FIXME */
typedef int(MDBX_predicate_func)(void *context, MDBX_val *key, MDBX_val *value,
void *arg) MDBX_CXX17_NOEXCEPT;
/** FIXME */
LIBMDBX_API int mdbx_cursor_scan(MDBX_cursor *cursor,
MDBX_predicate_func *predicate, void *context,
MDBX_cursor_op start_op,
MDBX_cursor_op turn_op, void *arg);
/** FIXME */
LIBMDBX_API int mdbx_cursor_scan_from(MDBX_cursor *cursor,
MDBX_predicate_func *predicate,
void *context, MDBX_cursor_op from_op,
MDBX_val *from_key, MDBX_val *from_value,
MDBX_cursor_op turn_op, void *arg);
/** \brief Retrieve multiple non-dupsort key/value pairs by cursor.
* \ingroup c_crud
@ -5076,6 +5286,10 @@ mdbx_cursor_eof(const MDBX_cursor *cursor);
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
mdbx_cursor_on_first(const MDBX_cursor *cursor);
/** FIXME */
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
mdbx_cursor_on_first_dup(const MDBX_cursor *cursor);
/** \brief Determines whether the cursor is pointed to the last key-value pair
* or not.
* \ingroup c_cursors
@ -5090,6 +5304,10 @@ mdbx_cursor_on_first(const MDBX_cursor *cursor);
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
mdbx_cursor_on_last(const MDBX_cursor *cursor);
/** FIXME */
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int
mdbx_cursor_on_last_dup(const MDBX_cursor *cursor);
/** \addtogroup c_rqest
* \details \note The estimation result varies greatly depending on the filling
* of specific pages and the overall balance of the b-tree:
@ -5521,48 +5739,21 @@ LIBMDBX_API int mdbx_env_set_hsr(MDBX_env *env, MDBX_hsr_func *hsr_callback);
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API MDBX_hsr_func *
mdbx_env_get_hsr(const MDBX_env *env);
/** \defgroup btree_traversal B-tree Traversal
* This is internal API for mdbx_chk tool. You should avoid to use it, except
* some extremal special cases.
/** \defgroup chk Checking and Recovery
* Basically this is internal API for `mdbx_chk` tool, etc.
* You should avoid to use it, except some extremal special cases.
* \ingroup c_extra
* @{ */
/** \brief Page types for traverse the b-tree.
* \see mdbx_env_pgwalk() \see MDBX_pgvisitor_func */
enum MDBX_page_type_t {
MDBX_page_broken,
MDBX_page_meta,
MDBX_page_large,
MDBX_page_branch,
MDBX_page_leaf,
MDBX_page_dupfixed_leaf,
MDBX_subpage_leaf,
MDBX_subpage_dupfixed_leaf,
MDBX_subpage_broken,
};
#ifndef __cplusplus
typedef enum MDBX_page_type_t MDBX_page_type_t;
#endif
/** \brief Acquires write-transaction lock.
* Provided for custom and/or complex locking scenarios.
* \returns A non-zero error value on failure and 0 on success. */
LIBMDBX_API int mdbx_txn_lock(MDBX_env *env, bool dont_wait);
/** \brief Pseudo-name for MainDB */
#define MDBX_PGWALK_MAIN ((void *)((ptrdiff_t)0))
/** \brief Pseudo-name for GarbageCollectorDB */
#define MDBX_PGWALK_GC ((void *)((ptrdiff_t)-1))
/** \brief Pseudo-name for MetaPages */
#define MDBX_PGWALK_META ((void *)((ptrdiff_t)-2))
/** \brief Callback function for traverse the b-tree. \see mdbx_env_pgwalk() */
typedef int
MDBX_pgvisitor_func(const uint64_t pgno, const unsigned number, void *const ctx,
const int deep, const MDBX_val *dbi_name,
const size_t page_size, const MDBX_page_type_t type,
const MDBX_error_t err, const size_t nentries,
const size_t payload_bytes, const size_t header_bytes,
const size_t unused_bytes) MDBX_CXX17_NOEXCEPT;
/** \brief B-tree traversal function. */
LIBMDBX_API int mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor,
void *ctx, bool dont_check_keys_ordering);
/** \brief Releases write-transaction lock.
* Provided for custom and/or complex locking scenarios.
* \returns A non-zero error value on failure and 0 on success. */
LIBMDBX_API int mdbx_txn_unlock(MDBX_env *env);
/** \brief Open an environment instance using specific meta-page
* for checking and recovery.
@ -5594,7 +5785,236 @@ LIBMDBX_API int mdbx_env_open_for_recoveryW(MDBX_env *env,
* leg(s). */
LIBMDBX_API int mdbx_env_turn_for_recovery(MDBX_env *env, unsigned target_meta);
/** end of btree_traversal @} */
/** \brief FIXME
*/
LIBMDBX_API int mdbx_preopen_snapinfo(const char *pathname, MDBX_envinfo *arg,
size_t bytes);
#if defined(_WIN32) || defined(_WIN64) || defined(DOXYGEN)
/** \copydoc mdbx_preopen_snapinfo()
* \note Available only on Windows.
* \see mdbx_preopen_snapinfo() */
LIBMDBX_API int mdbx_preopen_snapinfoW(const wchar_t *pathname,
MDBX_envinfo *arg, size_t bytes);
#endif /* Windows */
/** \brief Флаги/опции для проверки целостности БД.
* \see mdbx_env_chk() */
enum MDBX_chk_flags_t {
/** Режим проверки по-умолчанию, в том числе в режиме только-чтения. */
MDBX_CHK_DEFAULTS = 0,
/** Проверка в режиме чтения-записи, с захватом блокировки и приостановки
* пишущих транзакций. */
MDBX_CHK_READWRITE = 1,
/** Пропустить обход дерева страниц. */
MDBX_CHK_SKIP_BTREE_TRAVERSAL = 2,
/** Пропустить просмотр записей ключ-значение. */
MDBX_CHK_SKIP_KV_TRAVERSAL = 4,
/** Игнорировать порядок ключей и записей.
* \note Требуется при проверке унаследованных БД созданных с использованием
* нестандартных (пользовательских) функций сравнения ключей или значений. */
MDBX_CHK_IGNORE_ORDER = 8
};
#ifndef __cplusplus
/** \ingroup c_opening */
typedef enum MDBX_chk_flags_t MDBX_chk_flags_t;
#else
DEFINE_ENUM_FLAG_OPERATORS(MDBX_chk_flags_t)
#endif
/** \brief Уровни логирование/детализации информации,
* поставляемой через обратные вызовы при проверке целостности БД.
* \see mdbx_env_chk() */
enum MDBX_chk_severity {
MDBX_chk_severity_prio_shift = 4,
MDBX_chk_severity_kind_mask = 0xF,
MDBX_chk_fatal = 0x00u,
MDBX_chk_error = 0x11u,
MDBX_chk_warning = 0x22u,
MDBX_chk_notice = 0x33u,
MDBX_chk_result = 0x44u,
MDBX_chk_resolution = 0x55u,
MDBX_chk_processing = 0x56u,
MDBX_chk_info = 0x67u,
MDBX_chk_verbose = 0x78u,
MDBX_chk_details = 0x89u,
MDBX_chk_extra = 0x9Au
};
/** \brief Стадии проверки,
* сообщаемые через обратные вызовы при проверке целостности БД.
* \see mdbx_env_chk() */
enum MDBX_chk_stage {
MDBX_chk_none,
MDBX_chk_init,
MDBX_chk_lock,
MDBX_chk_meta,
MDBX_chk_traversal_tree,
MDBX_chk_traversal_freedb,
MDBX_chk_space,
MDBX_chk_traversal_maindb,
MDBX_chk_traversal_subdbs,
MDBX_chk_conclude,
MDBX_chk_unlock,
MDBX_chk_finalize
};
/** \brief Виртуальная строка отчета, формируемого при проверке целостности БД.
* \see mdbx_env_chk() */
typedef struct MDBX_chk_line {
struct MDBX_chk_context *ctx;
uint8_t severity, scope_depth, empty;
char *begin, *end, *out;
} MDBX_chk_line_t;
/** \brief Проблема обнаруженная при проверке целостности БД.
* \see mdbx_env_chk() */
typedef struct MDBX_chk_issue {
struct MDBX_chk_issue *next;
size_t count;
const char *caption;
} MDBX_chk_issue_t;
/** \brief Иерархический контекст при проверке целостности БД.
* \see mdbx_env_chk() */
typedef struct MDBX_chk_scope {
MDBX_chk_issue_t *issues;
struct MDBX_chk_internal *internal;
const void *object;
enum MDBX_chk_stage stage;
enum MDBX_chk_severity verbosity;
size_t subtotal_issues;
union {
void *ptr;
size_t number;
} usr_z, usr_v, usr_o;
} MDBX_chk_scope_t;
/** \brief Пользовательский тип для привязки дополнительных данных,
* связанных с некоторой таблицей ключ-значение, при проверке целостности БД.
* \see mdbx_env_chk() */
typedef struct MDBX_chk_user_subdb_cookie MDBX_chk_user_subdb_cookie_t;
/** \brief Гистограмма с некоторой статистической информацией,
* собираемой при проверке целостности БД.
* \see mdbx_env_chk() */
struct MDBX_chk_histogram {
size_t amount, count, ones, pad;
struct {
size_t begin, end, amount, count;
} ranges[9];
};
/** \brief Информация о некоторой таблицей ключ-значение,
* при проверке целостности БД.
* \see mdbx_env_chk() */
typedef struct MDBX_chk_subdb {
MDBX_chk_user_subdb_cookie_t *cookie;
/** \brief Pseudo-name for MainDB */
#define MDBX_CHK_MAIN ((void *)((ptrdiff_t)0))
/** \brief Pseudo-name for GarbageCollectorDB */
#define MDBX_CHK_GC ((void *)((ptrdiff_t)-1))
/** \brief Pseudo-name for MetaPages */
#define MDBX_CHK_META ((void *)((ptrdiff_t)-2))
MDBX_val name;
MDBX_db_flags_t flags;
int id;
size_t payload_bytes, lost_bytes;
struct {
size_t all, empty, other;
size_t branch, leaf;
size_t nested_branch, nested_leaf, nested_subleaf;
} pages;
struct {
/// Tree deep histogram
struct MDBX_chk_histogram deep;
/// Histogram of large/overflow pages length
struct MDBX_chk_histogram large_pages;
/// Histogram of nested trees height, span length for GC
struct MDBX_chk_histogram nested_tree;
/// Keys length histogram
struct MDBX_chk_histogram key_len;
/// Values length histogram
struct MDBX_chk_histogram val_len;
} histogram;
} MDBX_chk_subdb_t;
/** \brief Контекст проверки целостности БД.
* \see mdbx_env_chk() */
typedef struct MDBX_chk_context {
struct MDBX_chk_internal *internal;
MDBX_env *env;
MDBX_txn *txn;
MDBX_chk_scope_t *scope;
uint8_t scope_nesting;
struct {
size_t total_payload_bytes;
size_t subdb_total, subdb_processed;
size_t total_unused_bytes, unused_pages;
size_t processed_pages, reclaimable_pages, gc_pages, alloc_pages,
backed_pages;
size_t problems_meta, tree_problems, gc_tree_problems, kv_tree_problems,
problems_gc, problems_kv, total_problems;
uint64_t steady_txnid, recent_txnid;
/** Указатель на массив размером subdb_total с указателями на экземпляры
* структур MDBX_chk_subdb_t с информацией о всех таблицах ключ-значение,
* включая MainDB и GC/FreeDB. */
const MDBX_chk_subdb_t *const *subdbs;
} result;
} MDBX_chk_context_t;
/** FIXME */
typedef struct MDBX_chk_callbacks {
bool (*check_break)(MDBX_chk_context_t *ctx);
int (*scope_push)(MDBX_chk_context_t *ctx, MDBX_chk_scope_t *outer,
MDBX_chk_scope_t *inner, const char *fmt, va_list args);
int (*scope_conclude)(MDBX_chk_context_t *ctx, MDBX_chk_scope_t *outer,
MDBX_chk_scope_t *inner, int err);
void (*scope_pop)(MDBX_chk_context_t *ctx, MDBX_chk_scope_t *outer,
MDBX_chk_scope_t *inner);
void (*issue)(MDBX_chk_context_t *ctx, const char *object,
uint64_t entry_number, const char *issue, const char *extra_fmt,
va_list extra_args);
MDBX_chk_user_subdb_cookie_t *(*subdb_filter)(MDBX_chk_context_t *ctx,
const MDBX_val *name,
MDBX_db_flags_t flags);
int (*subdb_conclude)(MDBX_chk_context_t *ctx, const MDBX_chk_subdb_t *subdb,
MDBX_cursor *cursor, int err);
void (*subdb_dispose)(MDBX_chk_context_t *ctx, const MDBX_chk_subdb_t *subdb);
int (*subdb_handle_kv)(MDBX_chk_context_t *ctx, const MDBX_chk_subdb_t *subdb,
size_t entry_number, const MDBX_val *key,
const MDBX_val *value);
int (*stage_begin)(MDBX_chk_context_t *ctx, enum MDBX_chk_stage);
int (*stage_end)(MDBX_chk_context_t *ctx, enum MDBX_chk_stage, int err);
MDBX_chk_line_t *(*print_begin)(MDBX_chk_context_t *ctx,
enum MDBX_chk_severity severity);
void (*print_flush)(MDBX_chk_line_t *);
void (*print_done)(MDBX_chk_line_t *);
void (*print_chars)(MDBX_chk_line_t *, const char *str, size_t len);
void (*print_format)(MDBX_chk_line_t *, const char *fmt, va_list args);
void (*print_size)(MDBX_chk_line_t *, const char *prefix,
const uint64_t value, const char *suffix);
} MDBX_chk_callbacks_t;
/** FIXME */
LIBMDBX_API int mdbx_env_chk(MDBX_env *env, const MDBX_chk_callbacks_t *cb,
MDBX_chk_context_t *ctx,
const enum MDBX_chk_flags_t flags,
enum MDBX_chk_severity verbosity,
unsigned timeout_seconds_16dot16);
/** FIXME */
LIBMDBX_API int mdbx_env_chk_problem(MDBX_chk_context_t *ctx);
/** end of chk @} */
/** end of c_api @} */

940
mdbx.h++

File diff suppressed because it is too large Load Diff

View File

@ -48,6 +48,7 @@
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
@ -685,7 +686,7 @@ __extern_C key_t ftok(const char *, int);
/*----------------------------------------------------------------------------*/
#if defined(MDBX_USE_VALGRIND)
#if defined(ENABLE_MEMCHECK)
#include <valgrind/memcheck.h>
#ifndef VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE
/* LY: available since Valgrind 3.10 */
@ -707,7 +708,7 @@ __extern_C key_t ftok(const char *, int);
#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(a, s) (0)
#define VALGRIND_CHECK_MEM_IS_DEFINED(a, s) (0)
#define RUNNING_ON_VALGRIND (0)
#endif /* MDBX_USE_VALGRIND */
#endif /* ENABLE_MEMCHECK */
#ifdef __SANITIZE_ADDRESS__
#include <sanitizer/asan_interface.h>

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| |
3 |0000 0008|ALLOC_SSCAN|TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | |
4 |0000 0010|ALLOC_FIFO |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | |
5 |0000 0020| |TXN_DRAINED_GC|INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2 | |
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP | |
7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | | | |
5 |0000 0020| |TXN_DRAINED_GC|INTEGERDUP|NODUPDATA | | |P_LEAF2 | |
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_OLDEN | |P_SUBP | |
7 |0000 0080| | | |ALLDUPS |DBI_LINDO | | | |
8 |0000 0100| _MAY_MOVE | | | | | | | <= |
9 |0000 0200| _MAY_UNMAP| | | | | | | <= |
10|0000 0400| | | | | | | | |

View File

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

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

View File

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

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_WHOLE 0, DXB_MAXLEN
int mdbx_txn_lock(MDBX_env *env, bool dontwait) {
int osal_txn_lock(MDBX_env *env, bool dontwait) {
if (dontwait) {
if (!TryEnterCriticalSection(&env->me_windowsbug_lock))
return MDBX_BUSY;
@ -190,16 +190,13 @@ int mdbx_txn_lock(MDBX_env *env, bool dontwait) {
0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH) {
return ERROR_POSSIBLE_DEADLOCK;
return MDBX_EDEADLK;
}
}
if (env->me_flags & MDBX_EXCLUSIVE) {
/* Zap: Failing to release lock 'env->me_windowsbug_lock'
* in function 'mdbx_txn_lock' */
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
return MDBX_SUCCESS;
}
eASSERT(env, !env->me_txn0->mt_owner);
if (env->me_flags & MDBX_EXCLUSIVE)
goto done;
const HANDLE fd4data =
env->me_overlapped_fd ? env->me_overlapped_fd : env->me_lazy_fd;
@ -218,17 +215,20 @@ int mdbx_txn_lock(MDBX_env *env, bool dontwait) {
}
}
if (rc == MDBX_SUCCESS) {
done:
/* Zap: Failing to release lock 'env->me_windowsbug_lock'
* in function 'mdbx_txn_lock' */
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
return rc;
env->me_txn0->mt_owner = osal_thread_self();
return MDBX_SUCCESS;
}
LeaveCriticalSection(&env->me_windowsbug_lock);
return (!dontwait || rc != ERROR_LOCK_VIOLATION) ? rc : MDBX_BUSY;
}
void mdbx_txn_unlock(MDBX_env *env) {
void osal_txn_unlock(MDBX_env *env) {
eASSERT(env, env->me_txn0->mt_owner == osal_thread_self());
if ((env->me_flags & MDBX_EXCLUSIVE) == 0) {
const HANDLE fd4data =
env->me_overlapped_fd ? env->me_overlapped_fd : env->me_lazy_fd;
@ -236,6 +236,7 @@ void mdbx_txn_unlock(MDBX_env *env) {
if (err != MDBX_SUCCESS)
mdbx_panic("%s failed: err %u", __func__, err);
}
env->me_txn0->mt_owner = 0;
LeaveCriticalSection(&env->me_windowsbug_lock);
}
@ -442,7 +443,7 @@ osal_resume_threads_after_remap(mdbx_handle_array_t *array) {
* The osal_lck_downgrade() moves the locking-FSM from "exclusive write"
* state to the "used" (i.e. shared) state.
*
* The mdbx_lck_upgrade() moves the locking-FSM from "used" (i.e. shared)
* The osal_lck_upgrade() moves the locking-FSM from "used" (i.e. shared)
* state to the "exclusive write" state.
*/
@ -615,7 +616,7 @@ MDBX_INTERNAL_FUNC int osal_lck_downgrade(MDBX_env *env) {
return MDBX_SUCCESS /* 5) now at S-? (used), done */;
}
MDBX_INTERNAL_FUNC int mdbx_lck_upgrade(MDBX_env *env) {
MDBX_INTERNAL_FUNC int osal_lck_upgrade(MDBX_env *env, bool dont_wait) {
/* Transite from used state (S-?) to exclusive-write (E-E) */
assert(env->me_lfd != INVALID_HANDLE_VALUE);
@ -625,7 +626,9 @@ MDBX_INTERNAL_FUNC int mdbx_lck_upgrade(MDBX_env *env) {
/* 1) now on S-? (used), try S-E (locked) */
jitter4testing(false);
int rc = flock(env->me_lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_UPPER);
int rc = flock(env->me_lfd,
dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE,
LCK_UPPER);
if (rc != MDBX_SUCCESS) {
/* 2) something went wrong, give up */;
VERBOSE("%s, err %u", "S-?(used) >> S-E(locked)", rc);
@ -640,7 +643,9 @@ MDBX_INTERNAL_FUNC int mdbx_lck_upgrade(MDBX_env *env) {
/* 4) now on ?-E (middle), try E-E (exclusive-write) */
jitter4testing(false);
rc = flock(env->me_lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_LOWER);
rc = flock(env->me_lfd,
dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE,
LCK_LOWER);
if (rc != MDBX_SUCCESS) {
/* 5) something went wrong, give up */;
VERBOSE("%s, err %u", "?-E(middle) >> E-E(exclusive-write)", rc);
@ -677,7 +682,9 @@ MDBX_INTERNAL_FUNC int osal_lck_init(MDBX_env *env,
}
MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env,
MDBX_env *inprocess_neighbor) {
MDBX_env *inprocess_neighbor,
const uint32_t current_pid) {
(void)current_pid;
/* LY: should unmap before releasing the locks to avoid race condition and
* STATUS_USER_MAPPED_FILE/ERROR_USER_MAPPED_FILE */
if (env->me_map)
@ -686,7 +693,7 @@ MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env,
const bool synced = env->me_lck_mmap.lck->mti_unsynced_pages.weak == 0;
osal_munmap(&env->me_lck_mmap);
if (synced && !inprocess_neighbor && env->me_lfd != INVALID_HANDLE_VALUE &&
mdbx_lck_upgrade(env) == MDBX_SUCCESS)
osal_lck_upgrade(env, true) == MDBX_SUCCESS)
/* this will fail if LCK is used/mmapped by other process(es) */
osal_ftruncate(env->me_lfd, 0);
}

View File

@ -1,6 +1,6 @@
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_CHK 1 "2024-03-13" "MDBX 0.12.10"
.TH MDBX_CHK 1 "2024-03-21" "MDBX 0.13"
.SH NAME
mdbx_chk \- MDBX checking tool
.SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_COPY 1 "2024-03-13" "MDBX 0.12.10"
.TH MDBX_COPY 1 "2024-03-21" "MDBX 0.13"
.SH NAME
mdbx_copy \- MDBX environment copy tool
.SH SYNOPSIS

View File

@ -1,7 +1,7 @@
.\" Copyright 2021-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_DROP 1 "2024-03-13" "MDBX 0.12.10"
.TH MDBX_DROP 1 "2024-03-21" "MDBX 0.13"
.SH NAME
mdbx_drop \- MDBX database delete tool
.SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_DUMP 1 "2024-03-13" "MDBX 0.12.10"
.TH MDBX_DUMP 1 "2024-03-21" "MDBX 0.13"
.SH NAME
mdbx_dump \- MDBX environment export tool
.SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_LOAD 1 "2024-03-13" "MDBX 0.12.10"
.TH MDBX_LOAD 1 "2024-03-21" "MDBX 0.13"
.SH NAME
mdbx_load \- MDBX environment import tool
.SH SYNOPSIS

View File

@ -2,7 +2,7 @@
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_STAT 1 "2024-03-13" "MDBX 0.12.10"
.TH MDBX_STAT 1 "2024-03-21" "MDBX 0.13"
.SH NAME
mdbx_stat \- MDBX environment status tool
.SH SYNOPSIS

View File

@ -271,6 +271,11 @@ namespace mdbx {
"into an incompatible memory allocation scheme.");
}
[[noreturn]] __cold void throw_incomparable_cursors() {
throw std::logic_error(
"mdbx:: incomparable and/or invalid cursors to compare positions.");
}
[[noreturn]] __cold void throw_bad_value_size() {
throw bad_value_size(MDBX_BAD_VALSIZE);
}
@ -324,6 +329,8 @@ DEFINE_EXCEPTION(thread_mismatch)
DEFINE_EXCEPTION(transaction_full)
DEFINE_EXCEPTION(transaction_overlapping)
DEFINE_EXCEPTION(duplicated_lck_file)
DEFINE_EXCEPTION(dangling_map_id)
#undef DEFINE_EXCEPTION
__cold const char *error::what() const noexcept {
@ -410,6 +417,7 @@ __cold void error::throw_exception() const {
CASE_EXCEPTION(transaction_full, MDBX_TXN_FULL);
CASE_EXCEPTION(transaction_overlapping, MDBX_TXN_OVERLAPPING);
CASE_EXCEPTION(duplicated_lck_file, MDBX_DUPLICATED_CLK);
CASE_EXCEPTION(dangling_map_id, MDBX_DANGLING_DBI);
#undef CASE_EXCEPTION
default:
if (is_mdbx_error())
@ -527,48 +535,48 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
}
#ifdef MDBX_U128_TYPE
MDBX_U128_TYPE slice::as_uint128() const {
MDBX_U128_TYPE slice::as_uint128_adapt() const {
static_assert(sizeof(MDBX_U128_TYPE) == 16, "WTF?");
if (size() == 16) {
MDBX_U128_TYPE r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint64();
return as_uint64_adapt();
}
#endif /* MDBX_U128_TYPE */
uint64_t slice::as_uint64() const {
uint64_t slice::as_uint64_adapt() const {
static_assert(sizeof(uint64_t) == 8, "WTF?");
if (size() == 8) {
uint64_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint32();
return as_uint32_adapt();
}
uint32_t slice::as_uint32() const {
uint32_t slice::as_uint32_adapt() const {
static_assert(sizeof(uint32_t) == 4, "WTF?");
if (size() == 4) {
uint32_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint16();
return as_uint16_adapt();
}
uint16_t slice::as_uint16() const {
uint16_t slice::as_uint16_adapt() const {
static_assert(sizeof(uint16_t) == 2, "WTF?");
if (size() == 2) {
uint16_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint8();
return as_uint8_adapt();
}
uint8_t slice::as_uint8() const {
uint8_t slice::as_uint8_adapt() const {
static_assert(sizeof(uint8_t) == 1, "WTF?");
if (size() == 1)
return *static_cast<const uint8_t *>(data());
@ -579,48 +587,48 @@ uint8_t slice::as_uint8() const {
}
#ifdef MDBX_I128_TYPE
MDBX_I128_TYPE slice::as_int128() const {
MDBX_I128_TYPE slice::as_int128_adapt() const {
static_assert(sizeof(MDBX_I128_TYPE) == 16, "WTF?");
if (size() == 16) {
MDBX_I128_TYPE r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int64();
return as_int64_adapt();
}
#endif /* MDBX_I128_TYPE */
int64_t slice::as_int64() const {
int64_t slice::as_int64_adapt() const {
static_assert(sizeof(int64_t) == 8, "WTF?");
if (size() == 8) {
uint64_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int32();
return as_int32_adapt();
}
int32_t slice::as_int32() const {
int32_t slice::as_int32_adapt() const {
static_assert(sizeof(int32_t) == 4, "WTF?");
if (size() == 4) {
int32_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int16();
return as_int16_adapt();
}
int16_t slice::as_int16() const {
int16_t slice::as_int16_adapt() const {
static_assert(sizeof(int16_t) == 2, "WTF?");
if (size() == 2) {
int16_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int8();
return as_int8_adapt();
}
int8_t slice::as_int8() const {
int8_t slice::as_int8_adapt() const {
if (size() == 1)
return *static_cast<const int8_t *>(data());
else if (size() == 0)
@ -1535,6 +1543,13 @@ void txn_managed::commit(commit_latency *latency) {
MDBX_CXX20_UNLIKELY err.throw_exception();
}
void txn_managed::commit_embark_read() {
auto env = this->env();
commit();
error::success_or_throw(
::mdbx_txn_begin(env, nullptr, MDBX_TXN_RDONLY, &handle_));
}
//------------------------------------------------------------------------------
bool txn::drop_map(const char *name, bool throw_if_absent) {

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

View File

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

View File

@ -503,8 +503,18 @@ MDBX_INTERNAL_FUNC int osal_fastmutex_init(osal_fastmutex_t *fastmutex) {
#if defined(_WIN32) || defined(_WIN64)
InitializeCriticalSection(fastmutex);
return MDBX_SUCCESS;
#elif MDBX_DEBUG
pthread_mutexattr_t ma;
int rc = pthread_mutexattr_init(&ma);
if (likely(!rc)) {
rc = pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK);
if (likely(!rc) || rc == ENOTSUP)
rc = pthread_mutex_init(fastmutex, &ma);
pthread_mutexattr_destroy(&ma);
}
return rc;
#else
return pthread_mutex_init(fastmutex, NULL);
return pthread_mutex_init(fastmutex, nullptr);
#endif
}
@ -526,7 +536,7 @@ MDBX_INTERNAL_FUNC int osal_fastmutex_acquire(osal_fastmutex_t *fastmutex) {
0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH) {
return ERROR_POSSIBLE_DEADLOCK;
return MDBX_EDEADLK;
}
return MDBX_SUCCESS;
#else

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.
/// \return Error code (MDBX_PANIC) or zero on success.
MDBX_INTERNAL_FUNC int osal_lck_destroy(MDBX_env *env,
MDBX_env *inprocess_neighbor);
MDBX_env *inprocess_neighbor,
const uint32_t current_pid);
/// \brief Connects to shared interprocess locking objects and tries to acquire
/// the maximum lock level (shared if exclusive is not available)
@ -718,6 +719,8 @@ MDBX_INTERNAL_FUNC int osal_lck_seize(MDBX_env *env);
/// operational lock.
/// \return Error code or zero on success
MDBX_INTERNAL_FUNC int osal_lck_downgrade(MDBX_env *env);
MDBX_MAYBE_UNUSED MDBX_INTERNAL_FUNC int osal_lck_upgrade(MDBX_env *env,
bool dont_wait);
/// \brief Locks LCK-file or/and table of readers for (de)registering.
/// \return Error code or zero on success
@ -726,16 +729,12 @@ MDBX_INTERNAL_FUNC int osal_rdt_lock(MDBX_env *env);
/// \brief Unlocks LCK-file or/and table of readers after (de)registering.
MDBX_INTERNAL_FUNC void osal_rdt_unlock(MDBX_env *env);
/// \brief Acquires lock for DB change (on writing transaction start)
/// Reading transactions will not be blocked.
/// Declared as LIBMDBX_API because it is used in mdbx_chk.
/// \brief Acquires write-transaction lock.
/// \return Error code or zero on success
LIBMDBX_API int mdbx_txn_lock(MDBX_env *env, bool dont_wait);
MDBX_INTERNAL_FUNC int osal_txn_lock(MDBX_env *env, bool dont_wait);
/// \brief Releases lock once DB changes is made (after writing transaction
/// has finished).
/// Declared as LIBMDBX_API because it is used in mdbx_chk.
LIBMDBX_API void mdbx_txn_unlock(MDBX_env *env);
/// \brief Releases write-transaction lock..
MDBX_INTERNAL_FUNC void osal_txn_unlock(MDBX_env *env);
/// \brief Sets alive-flag of reader presence (indicative lock) for PID of
/// the current process. The function does no more than needed for

View File

@ -28,6 +28,7 @@ set(LIBMDBX_TEST_SOURCES
append.c++
ttl.c++
nested.c++
fork.c++
)
if(NOT MDBX_BUILD_CXX)
@ -91,6 +92,20 @@ if(UNIX AND NOT SUBPROJECT)
set_target_properties(test_extra_dupfixed_multiple PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
endif()
add_executable(test_extra_hex_base64_base58 extra/hex_base64_base58.c++)
target_include_directories(test_extra_hex_base64_base58 PRIVATE "${PROJECT_SOURCE_DIR}")
target_link_libraries(test_extra_hex_base64_base58 ${TOOL_MDBX_LIB})
if(MDBX_CXX_STANDARD)
set_target_properties(test_extra_hex_base64_base58 PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
endif()
add_executable(test_extra_doubtless_positioning extra/doubtless_positioning.c++)
target_include_directories(test_extra_doubtless_positioning PRIVATE "${PROJECT_SOURCE_DIR}")
target_link_libraries(test_extra_doubtless_positioning ${TOOL_MDBX_LIB})
if(MDBX_CXX_STANDARD)
set_target_properties(test_extra_doubtless_positioning PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
endif()
endif()
endif()
@ -107,7 +122,7 @@ else()
add_test(NAME smoke COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test
--loglevel=verbose
--keygen.seed=${test_seed}
--prng-seed=${test_seed}
--progress --console=no --pathname=smoke.db --dont-cleanup-after basic)
set_tests_properties(smoke PROPERTIES
TIMEOUT 600
@ -129,11 +144,11 @@ else()
add_test(NAME dupsort_writemap COMMAND ${MDBX_OUTPUT_DIR}/mdbx_test
--loglevel=notice
--keygen.seed=${test_seed}
--table=+data.integer --keygen.split=29 --datalen.min=min --datalen.max=max --progress --console=no
--prng-seed=${test_seed}
--table=+data.fixed --keygen.split=29 --datalen=rnd --progress --console=no
--repeat=2 --pathname=dupsort_writemap.db --dont-cleanup-after basic)
set_tests_properties(dupsort_writemap PROPERTIES
TIMEOUT 600
TIMEOUT 3600
RUN_SERIAL OFF)
if(MDBX_BUILD_TOOLS)
add_test(NAME dupsort_writemap_chk COMMAND ${MDBX_OUTPUT_DIR}/mdbx_chk -nvvwc dupsort_writemap.db)
@ -176,6 +191,11 @@ else()
if(MDBX_BUILD_CXX)
add_test(NAME extra_maindb_ordinal COMMAND test_extra_maindb_ordinal)
add_test(NAME extra_dupfixed_multiple COMMAND test_extra_dupfixed_multiple)
add_test(NAME extra_hex_base64_base58 COMMAND test_extra_hex_base64_base58)
add_test(NAME extra_doubtless_positioning COMMAND test_extra_doubtless_positioning)
if (ENABLE_MEMCHECK)
set_tests_properties(extra_doubtless_positioning PROPERTIES TIMEOUT 10800)
endif()
endif()
endif()

View File

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

View File

@ -41,8 +41,9 @@ testcase *registry::create_actor(const actor_config &config,
}
bool registry::review_actor_params(const actor_testcase id,
actor_params &params) {
return instance()->id2record.at(id)->review_params(params);
actor_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);
}
if (!registry::review_actor_params(testcase, params))
failure("Actor config-review failed for space-id %lu\n", space_id);
if (space_id > ACTOR_ID_MAX)
failure("Invalid space-id %lu\n", space_id);
last_space_id = unsigned(space_id);
if (!registry::review_actor_params(testcase, params, unsigned(space_id)))
failure("Actor config-review failed for space-id %lu\n", space_id);
last_space_id = unsigned(space_id);
log_trace("configure_actor: space %lu for %s", space_id,
testcase2str(testcase));
global::actors.emplace_back(
@ -105,6 +106,10 @@ void testcase_setup(const char *casename, const actor_params &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_try, nullptr, params);
#if !defined(_WIN32) && !defined(_WIN64)
configure_actor(last_space_id, ac_forkread, nullptr, params);
configure_actor(last_space_id, ac_forkwrite, nullptr, params);
#endif /* Windows */
log_notice("<<< testcase_setup(%s): done", casename);
} else {
failure("unknown testcase `%s`", casename);

View File

@ -145,6 +145,16 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
return true;
}
if (strcmp(value_cstr, "rnd") == 0 || strcmp(value_cstr, "rand") == 0 ||
strcmp(value_cstr, "random") == 0) {
value = minval;
if (maxval > minval)
value += (prng32() + UINT64_C(44263400549519813)) % (maxval - minval);
if (scale == intkey)
value &= ~3u;
return true;
}
char *suffix = nullptr;
errno = 0;
unsigned long long raw = strtoull(value_cstr, &suffix, 0);
@ -159,7 +169,7 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
uint64_t multiplier = 1;
if (suffix && *suffix) {
if (scale == no_scale)
if (scale == no_scale || scale == intkey)
failure("Option '--%s' doesn't accepts suffixes, so '%s' is unexpected\n",
option, suffix);
if (strcmp(suffix, "K") == 0 || strcasecmp(suffix, "Kilo") == 0)
@ -203,6 +213,8 @@ bool parse_option(int argc, char *const argv[], int &narg, const char *option,
if (value < minval)
failure("The minimal value for option '--%s' is %" PRIu64 "\n", option,
minval);
if (scale == intkey)
value &= ~3u;
return true;
}
@ -422,6 +434,7 @@ void dump(const char *title) {
log_verbose("#%u, testcase %s, space_id/table %u\n", i->actor_id,
testcase2str(i->testcase), i->space_id);
indent.push();
log_verbose("prng-seed: %u\n", i->params.prng_seed);
if (i->params.loglevel) {
log_verbose("log: level %u, %s\n", i->params.loglevel,
@ -461,7 +474,6 @@ void dump(const char *title) {
i->params.keygen.mesh, i->params.keygen.rotate, i->params.keygen.offset,
i->params.keygen.split,
i->params.keygen.width - i->params.keygen.split);
log_verbose("keygen.seed: %u\n", i->params.keygen.seed);
log_verbose("keygen.zerofill: %s\n",
i->params.keygen.zero_fill ? "Yes" : "No");
log_verbose("key: minlen %u, maxlen %u\n", i->params.keylen_min,
@ -681,7 +693,7 @@ bool actor_config::deserialize(const char *str, actor_config &config) {
}
unsigned actor_params::mdbx_keylen_min() const {
return (table_flags & MDBX_INTEGERKEY) ? 4 : 0;
return unsigned(mdbx_limits_keysize_min(table_flags));
}
unsigned actor_params::mdbx_keylen_max() const {
@ -689,7 +701,7 @@ unsigned actor_params::mdbx_keylen_max() const {
}
unsigned actor_params::mdbx_datalen_min() const {
return (table_flags & MDBX_INTEGERDUP) ? 4 : 0;
return unsigned(mdbx_limits_valsize_min(table_flags));
}
unsigned actor_params::mdbx_datalen_max() const {

View File

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

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

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();
/* TODO: работа в несколько потоков */
keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */);
keyvalue_maker.setup(config.params, 0 /* thread_number */);
keygen::buffer a_key = keygen::alloc(config.params.keylen_max);
keygen::buffer a_data_0 = keygen::alloc(config.params.datalen_max);
@ -90,7 +90,7 @@ bool testcase_hill::run() {
assert(b_serial > a_serial);
// создаем первую запись из пары
const keygen::serial_t age_shift = UINT64_C(1) << (a_serial % 31);
const keygen::serial_t age_shift = keyvalue_maker.remix_age(a_serial);
log_trace("uphill: insert-a (age %" PRIu64 ") %" PRIu64, age_shift,
a_serial);
generate_pair(a_serial, a_key, a_data_1, age_shift);
@ -302,7 +302,7 @@ bool testcase_hill::run() {
assert(b_serial > a_serial);
// обновляем первую запись из пары
const keygen::serial_t age_shift = UINT64_C(1) << (a_serial % 31);
const keygen::serial_t age_shift = keyvalue_maker.remix_age(a_serial);
log_trace("downhill: update-a (age 0->%" PRIu64 ") %" PRIu64, age_shift,
a_serial);
generate_pair(a_serial, a_key, a_data_0, 0);

View File

@ -39,6 +39,12 @@ bool testcase_jitter::run() {
if (upper_limit < 1)
upper_limit = config.params.size_now * 2;
tablename_buf buffer;
const char *const tablename = db_tablename(buffer);
tablename_buf buffer_renamed;
const char *const tablename_renamed =
db_tablename(buffer_renamed, ".renamed");
while (should_continue()) {
jitter_delay();
db_open();
@ -48,6 +54,15 @@ bool testcase_jitter::run() {
txn_begin(false);
dbi = db_table_open(true);
check_dbi_error(MDBX_SUCCESS, "created-uncommitted");
bool renamed = false;
if (flipcoin()) {
err = mdbx_dbi_rename(txn_guard.get(), dbi, tablename_renamed);
if (err != MDBX_SUCCESS)
failure_perror("jitter.rename-1", err);
renamed = true;
}
// note: here and below the 4-byte length keys and value are used
// to be compatible with any Db-flags given from command line.
MDBX_val k = {(void *)"k000", 4}, v = {(void *)"v001", 4};
@ -75,7 +90,17 @@ bool testcase_jitter::run() {
failure_perror("jitter.put-2", err);
check_dbi_error(MDBX_BAD_DBI, "dropped-recreated-aborted");
// restore DBI
dbi = db_table_open(false);
dbi = db_table_open(false, renamed);
if (renamed) {
err = mdbx_dbi_open(
txn_guard.get(), tablename_renamed,
flipcoin() ? MDBX_DB_ACCEDE : config.params.table_flags, &dbi);
if (unlikely(err != MDBX_SUCCESS))
failure_perror("open-renamed", err);
err = mdbx_dbi_rename(txn_guard.get(), dbi, tablename);
if (err != MDBX_SUCCESS)
failure_perror("jitter.rename-2", err);
}
check_dbi_error(MDBX_SUCCESS, "dropped-recreated-aborted+reopened");
v = {(void *)"v003", 4};
err = mdbx_put(txn_guard.get(), dbi, &k, &v, MDBX_UPSERT);

View File

@ -14,6 +14,39 @@
#include "test.h++"
static const uint64_t primes[64] = {
/* */
0, 1, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381,
/* */
UINT64_C(32749), UINT64_C(65521), UINT64_C(131071), UINT64_C(262139),
UINT64_C(524287), UINT64_C(1048573), UINT64_C(2097143), UINT64_C(4194301),
UINT64_C(8388593), UINT64_C(16777213), UINT64_C(33554393),
UINT64_C(67108859), UINT64_C(134217689), UINT64_C(268435399),
UINT64_C(536870909), UINT64_C(1073741789), UINT64_C(2147483647),
UINT64_C(4294967291), UINT64_C(8589934583), UINT64_C(17179869143),
UINT64_C(34359738337), UINT64_C(68719476731), UINT64_C(137438953447),
UINT64_C(274877906899), UINT64_C(549755813881), UINT64_C(1099511627689),
UINT64_C(2199023255531), UINT64_C(4398046511093), UINT64_C(8796093022151),
UINT64_C(17592186044399), UINT64_C(35184372088777),
UINT64_C(70368744177643), UINT64_C(140737488355213),
UINT64_C(281474976710597), UINT64_C(562949953421231),
UINT64_C(1125899906842597), UINT64_C(2251799813685119),
UINT64_C(4503599627370449), UINT64_C(9007199254740881),
UINT64_C(18014398509481951), UINT64_C(36028797018963913),
UINT64_C(72057594037927931), UINT64_C(144115188075855859),
UINT64_C(288230376151711717), UINT64_C(576460752303423433),
UINT64_C(1152921504606846883), UINT64_C(2305843009213693951),
UINT64_C(4611686018427387847), UINT64_C(9223372036854775783)};
/* static unsigned supid_log2(uint64_t v) {
unsigned r = 0;
while (v > 1) {
v >>= 1;
r += 1;
}
return r;
} */
namespace keygen {
/* LY: https://en.wikipedia.org/wiki/Injective_function */
@ -48,19 +81,19 @@ serial_t injective(const serial_t serial,
10, 14, 22, 19, 3, 21, 18, 19, 26, 24, 2, 21, 25, 29, 24, 10, 11, 14, 20,
19};
const auto mask = actor_params::serial_mask(bits);
const auto mult = m[bits - 8];
const auto shift = s[bits - 8];
serial_t result = serial * mult;
if (salt) {
const unsigned left = bits / 2;
const unsigned right = bits - left;
result = (result << left) |
((result & actor_params::serial_mask(bits)) >> right);
result = (result << left) | ((result & mask) >> right);
result = (result ^ salt) * mult;
}
result ^= result << shift;
result &= actor_params::serial_mask(bits);
result ^= (result & mask) >> shift;
result &= mask;
log_trace("keygen-injective: serial %" PRIu64 "/%u @%" PRIx64 ",%u,%" PRIu64
" => %" PRIu64 "/%u",
serial, bits, mult, shift, salt, result, bits);
@ -79,7 +112,7 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT))));
assert(!(value_essentials.flags &
~(essentials::prng_fill_flag |
unsigned(MDBX_INTEGERDUP | MDBX_REVERSEDUP))));
unsigned(MDBX_INTEGERDUP | MDBX_REVERSEDUP | MDBX_DUPFIXED))));
log_trace("keygen-pair: serial %" PRIu64 ", data-age %" PRIu64, serial,
value_age);
@ -111,7 +144,7 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
}
serial_t key_serial = serial;
serial_t value_serial = value_age << mapping.split;
serial_t value_serial = (value_age & value_age_mask) << mapping.split;
if (mapping.split) {
if (MDBX_db_flags_t(key_essentials.flags) & MDBX_DUPSORT) {
key_serial >>= mapping.split;
@ -126,15 +159,14 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
actor_params::serial_mask(mapping.split);
}
value_serial |= value_age << mapping.split;
log_trace("keygen-pair: split@%u => k%" PRIu64 ", v%" PRIu64, mapping.split,
key_serial, value_serial);
}
log_trace("keygen-pair: key %" PRIu64 ", value %" PRIu64, key_serial,
value_serial);
mk_begin(key_serial, key_essentials, *key);
mk_begin(value_serial, value_essentials, *value);
key_serial = mk_begin(key_serial, key_essentials, *key);
value_serial = mk_begin(value_serial, value_essentials, *value);
#if 0 /* unused for now */
if (key->value.iov_len + value->value.iov_len > pair_maxlen) {
@ -189,47 +221,82 @@ void __hot maker::pair(serial_t serial, const buffer &key, buffer &value,
log_pair(logging::trace, "kv", key, value);
}
void maker::setup(const config::actor_params_pod &actor, unsigned actor_id,
void maker::setup(const config::actor_params_pod &actor,
unsigned thread_number) {
#if CONSTEXPR_ENUM_FLAGS_OPERATIONS
static_assert(unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT |
MDBX_INTEGERDUP | MDBX_REVERSEDUP) < UINT16_MAX,
MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP) <
UINT16_MAX,
"WTF?");
#else
assert(unsigned(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT |
MDBX_INTEGERDUP | MDBX_REVERSEDUP) < UINT16_MAX);
MDBX_DUPFIXED | MDBX_INTEGERDUP | MDBX_REVERSEDUP) <
UINT16_MAX);
#endif
key_essentials.flags = uint16_t(
actor.table_flags &
MDBX_db_flags_t(MDBX_INTEGERKEY | MDBX_REVERSEKEY | MDBX_DUPSORT));
assert(actor.keylen_min <= UINT16_MAX);
key_essentials.minlen = uint16_t(actor.keylen_min);
assert(actor.keylen_max <= UINT32_MAX);
key_essentials.maxlen =
std::min(uint32_t(actor.keylen_max),
uint32_t(mdbx_limits_keysize_max(
actor.pagesize, MDBX_db_flags_t(key_essentials.flags))));
key_essentials.maxlen = std::min(
uint32_t(actor.keylen_max),
uint32_t(mdbx_limits_keysize_max(actor.pagesize, actor.table_flags)));
key_essentials.bits = (key_essentials.maxlen < sizeof(serial_t))
? key_essentials.maxlen * CHAR_BIT
: sizeof(serial_t) * CHAR_BIT;
key_essentials.mask = actor_params::serial_mask(key_essentials.bits);
assert(key_essentials.bits > 63 ||
key_essentials.mask > primes[key_essentials.bits]);
value_essentials.flags = uint16_t(
actor.table_flags & MDBX_db_flags_t(MDBX_INTEGERDUP | MDBX_REVERSEDUP));
actor.table_flags &
MDBX_db_flags_t(MDBX_INTEGERDUP | MDBX_REVERSEDUP | MDBX_DUPFIXED));
assert(actor.datalen_min <= UINT16_MAX);
value_essentials.minlen = uint16_t(actor.datalen_min);
assert(actor.datalen_max <= UINT32_MAX);
value_essentials.maxlen =
std::min(uint32_t(actor.datalen_max),
uint32_t(mdbx_limits_valsize_max(
actor.pagesize, MDBX_db_flags_t(key_essentials.flags))));
value_essentials.maxlen = std::min(
uint32_t(actor.datalen_max),
uint32_t(mdbx_limits_valsize_max(actor.pagesize, actor.table_flags)));
value_essentials.bits = (value_essentials.maxlen < sizeof(serial_t))
? value_essentials.maxlen * CHAR_BIT
: sizeof(serial_t) * CHAR_BIT;
value_essentials.mask = actor_params::serial_mask(value_essentials.bits);
assert(value_essentials.bits > 63 ||
value_essentials.mask > primes[value_essentials.bits]);
if (!actor.keygen.zero_fill) {
key_essentials.flags |= essentials::prng_fill_flag;
value_essentials.flags |= essentials::prng_fill_flag;
}
(void)thread_number;
mapping = actor.keygen;
salt =
(actor.keygen.seed + uint64_t(actor_id)) * UINT64_C(14653293970879851569);
const auto split = mapping.split;
while (mapping.split >
value_essentials.bits - essentials::value_age_minwidth ||
mapping.split >= mapping.width)
mapping.split -= 1;
if (split != mapping.width)
log_notice("keygen: reduce mapping-split from %u to %u", split,
mapping.split);
const auto width = mapping.width;
while (unsigned((actor.table_flags & MDBX_DUPSORT)
? mapping.width - mapping.split
: mapping.width) > key_essentials.bits)
mapping.width -= 1;
if (width != mapping.width)
log_notice("keygen: reduce mapping-width from %u to %u", width,
mapping.width);
value_age_bits = value_essentials.bits - mapping.split;
value_age_mask = actor_params::serial_mask(value_age_bits);
assert(value_age_bits >= essentials::value_age_minwidth);
salt = (prng_state ^
(thread_number * 1575554837) * UINT64_C(59386707711075671)) *
UINT64_C(14653293970879851569);
base = actor.serial_base();
}
@ -307,11 +374,24 @@ buffer alloc(size_t limit) {
return buffer(ptr);
}
void __hot maker::mk_begin(const serial_t serial, const essentials &params,
serial_t __hot maker::mk_begin(serial_t serial, const essentials &params,
result &out) {
assert(out.limit >= params.maxlen);
assert(params.maxlen >= params.minlen);
assert(serial <= params.mask);
if (unlikely(serial > params.mask)) {
#if 1
serial %= primes[params.bits];
assert(params.mask > primes[params.bits]);
#else
const serial_t maxbits = params.maxlen * CHAR_BIT;
serial ^= (serial >> maxbits / 2) *
serial_t((sizeof(serial_t) > 4) ? UINT64_C(40719303417517073)
: UINT32_C(3708688457));
serial &= params.mask;
#endif
assert(params.maxlen >= length(serial));
}
out.value.iov_len = std::max(unsigned(params.minlen), length(serial));
const auto variation = params.maxlen - params.minlen;
@ -328,6 +408,7 @@ void __hot maker::mk_begin(const serial_t serial, const essentials &params,
assert(length(serial) <= out.value.iov_len);
assert(out.value.iov_len >= params.minlen);
assert(out.value.iov_len <= params.maxlen);
return serial;
}
void __hot maker::mk_continue(const serial_t serial, const essentials &params,

View File

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

View File

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

View File

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

View File

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

View File

@ -37,6 +37,7 @@ MDBX_NORETURN void usage(void) {
" --console[=yes/no] Enable/disable console-like output\n"
" --cleanup-before[=YES/no] Cleanup/remove and re-create database\n"
" --cleanup-after[=YES/no] Cleanup/remove database after completion\n"
" --prng-seed=N Seed PRNG\n"
"Database size control:\n"
" --pagesize=... Database page size: min, max, 256..65536\n"
" --size-lower=N[K|M|G|T] Lower-bound of size in Kb/Mb/Gb/Tb\n"
@ -60,6 +61,10 @@ MDBX_NORETURN void usage(void) {
" --append Append-mode insertions\n"
" --dead.reader Dead-reader simulator\n"
" --dead.writer Dead-writer simulator\n"
#if !defined(_WIN32) && !defined(_WIN64)
" --fork.reader After-fork reader\n"
" --fork.writer After-fork writer\n"
#endif /* Windows */
"Actor options:\n"
" --batch.read=N Read-operations batch size\n"
" --batch.write=N Write-operations batch size\n"
@ -84,7 +89,6 @@ MDBX_NORETURN void usage(void) {
" --datalen=N Set both min/max for data length\n"
" --keygen.width=N TBD (see the source code)\n"
" --keygen.mesh=N TBD (see the source code)\n"
" --keygen.seed=N TBD (see the source code)\n"
" --keygen.zerofill=yes|NO TBD (see the source code)\n"
" --keygen.split=N TBD (see the source code)\n"
" --keygen.rotate=N TBD (see the source code)\n"
@ -140,7 +144,7 @@ void actor_params::set_defaults(const std::string &tmpdir) {
growth_step = -1;
pagesize = -1;
keygen.seed = 1;
prng_seed = 0;
keygen.zero_fill = false;
keygen.keycase = kc_random;
keygen.width = (table_flags & MDBX_DUPSORT) ? 32 : 64;
@ -263,8 +267,19 @@ static void fixup4qemu(actor_params &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
log_trace("#argc = %d", argc);
for (int i = 0; i < argc; ++i)
@ -434,9 +449,11 @@ int main(int argc, char *const argv[]) {
if (config::parse_option(argc, argv, narg, "keygen.mesh",
params.keygen.mesh, 0, 64))
continue;
if (config::parse_option(argc, argv, narg, "keygen.seed",
params.keygen.seed, config::no_scale))
if (config::parse_option(argc, argv, narg, "prng-seed", params.prng_seed,
config::no_scale)) {
prng_seed(params.prng_seed);
continue;
}
if (config::parse_option(argc, argv, narg, "keygen.zerofill",
params.keygen.zero_fill))
continue;
@ -453,49 +470,59 @@ int main(int argc, char *const argv[]) {
keycase_setup(value, params);
continue;
}
if (config::parse_option(argc, argv, narg, "keylen.min", params.keylen_min,
config::no_scale, params.mdbx_keylen_min(),
params.mdbx_keylen_max())) {
if (config::parse_option(
argc, argv, narg, "keylen.min", params.keylen_min,
(params.table_flags & MDBX_INTEGERKEY) ? config::intkey
: config::no_scale,
params.mdbx_keylen_min(), params.mdbx_keylen_max())) {
if ((params.table_flags & MDBX_INTEGERKEY) ||
params.keylen_max < params.keylen_min)
params.keylen_max = params.keylen_min;
continue;
}
if (config::parse_option(argc, argv, narg, "keylen.max", params.keylen_max,
config::no_scale, params.mdbx_keylen_min(),
params.mdbx_keylen_max())) {
if (config::parse_option(
argc, argv, narg, "keylen.max", params.keylen_max,
(params.table_flags & MDBX_INTEGERKEY) ? config::intkey
: config::no_scale,
params.mdbx_keylen_min(), params.mdbx_keylen_max())) {
if ((params.table_flags & MDBX_INTEGERKEY) ||
params.keylen_min > params.keylen_max)
params.keylen_min = params.keylen_max;
continue;
}
if (config::parse_option(argc, argv, narg, "keylen", params.keylen_min,
config::no_scale, params.mdbx_keylen_min(),
params.mdbx_keylen_max())) {
if (config::parse_option(
argc, argv, narg, "keylen", params.keylen_min,
(params.table_flags & MDBX_INTEGERKEY) ? config::intkey
: config::no_scale,
params.mdbx_keylen_min(), params.mdbx_keylen_max())) {
params.keylen_max = params.keylen_min;
continue;
}
if (config::parse_option(argc, argv, narg, "datalen.min",
params.datalen_min, config::no_scale,
params.mdbx_datalen_min(),
params.mdbx_datalen_max())) {
if (config::parse_option(
argc, argv, narg, "datalen.min", params.datalen_min,
(params.table_flags & MDBX_INTEGERDUP) ? config::intkey
: config::no_scale,
params.mdbx_datalen_min(), params.mdbx_datalen_max())) {
if ((params.table_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) ||
params.datalen_max < params.datalen_min)
params.datalen_max = params.datalen_min;
continue;
}
if (config::parse_option(argc, argv, narg, "datalen.max",
params.datalen_max, config::no_scale,
params.mdbx_datalen_min(),
params.mdbx_datalen_max())) {
if (config::parse_option(
argc, argv, narg, "datalen.max", params.datalen_max,
(params.table_flags & MDBX_INTEGERDUP) ? config::intkey
: config::no_scale,
params.mdbx_datalen_min(), params.mdbx_datalen_max())) {
if ((params.table_flags & (MDBX_INTEGERDUP | MDBX_DUPFIXED)) ||
params.datalen_min > params.datalen_max)
params.datalen_min = params.datalen_max;
continue;
}
if (config::parse_option(argc, argv, narg, "datalen", params.datalen_min,
config::no_scale, params.mdbx_datalen_min(),
params.mdbx_datalen_max())) {
if (config::parse_option(
argc, argv, narg, "datalen", params.datalen_min,
(params.table_flags & MDBX_INTEGERDUP) ? config::intkey
: config::no_scale,
params.mdbx_datalen_min(), params.mdbx_datalen_max())) {
params.datalen_max = params.datalen_min;
continue;
}
@ -591,6 +618,18 @@ int main(int argc, char *const argv[]) {
configure_actor(last_space_id, ac_nested, value, params);
continue;
}
#if !defined(_WIN32) && !defined(_WIN64)
if (config::parse_option(argc, argv, narg, "fork.reader", nullptr)) {
fixup4qemu(params);
configure_actor(last_space_id, ac_forkread, value, params);
continue;
}
if (config::parse_option(argc, argv, narg, "fork.writer", nullptr)) {
fixup4qemu(params);
configure_actor(last_space_id, ac_forkwrite, value, params);
continue;
}
#endif /* Windows */
if (*argv[narg] != '-') {
fixup4qemu(params);
@ -704,6 +743,14 @@ int main(int argc, char *const argv[]) {
log_trace("=== done...");
}
if (!failed) {
MDBX_envinfo info;
int err =
mdbx_preopen_snapinfo(params.pathname_db.c_str(), &info, sizeof(info));
if (err != MDBX_SUCCESS)
failure_perror("mdbx_preopen_snapinfo()", err);
}
log_notice("RESULT: %s\n", failed ? "Failed" : "Successful");
if (global::config::cleanup_after) {
if (failed)

View File

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

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

View File

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

View File

@ -60,7 +60,7 @@ do
echo " For instance, when the process 'A' explicitly marks a memory"
echo " region as 'undefined', the process 'B' fill it,"
echo " and after this process 'A' read such region, etc."
MONITOR="valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt"
MONITOR="valgrind --trace-children=yes --log-file=valgrind-%p.log --leak-check=full --track-origins=yes --read-var-info=yes --error-exitcode=42 --suppressions=test/valgrind_suppress.txt"
rm -f valgrind-*.log
;;
--skip-make)

View File

@ -39,6 +39,12 @@ const char *testcase2str(const actor_testcase testcase) {
return "ttl";
case ac_nested:
return "nested";
#if !defined(_WIN32) && !defined(_WIN64)
case ac_forkread:
return "fork.reader";
case ac_forkwrite:
return "fork.writer";
#endif /* Windows */
}
}
@ -537,29 +543,43 @@ int testcase::db_open__begin__table_create_open_clean(MDBX_dbi &handle) {
return err;
}
MDBX_dbi testcase::db_table_open(bool create) {
log_trace(">> testcase::db_table_create");
char tablename_buf[16];
const char *testcase::db_tablename(tablename_buf &buffer,
const char *suffix) const {
const char *tablename = nullptr;
if (config.space_id) {
int rc = snprintf(tablename_buf, sizeof(tablename_buf), "TBL%04u",
config.space_id);
int rc =
snprintf(buffer, sizeof(buffer), "TBL%04u%s", config.space_id, suffix);
if (rc < 4 || rc >= (int)sizeof(tablename_buf) - 1)
failure("snprintf(tablename): %d", rc);
tablename = tablename_buf;
tablename = buffer;
}
log_debug("use %s table", tablename ? tablename : "MAINDB");
return tablename;
}
MDBX_dbi testcase::db_table_open(bool create, bool expect_failure) {
log_trace(">> testcase::db_table_%s%s", create ? "create" : "open",
expect_failure ? "(expect_failure)" : "");
tablename_buf buffer;
const char *tablename = db_tablename(buffer);
MDBX_dbi handle = 0;
int rc = mdbx_dbi_open(txn_guard.get(), tablename,
(create ? MDBX_CREATE : MDBX_DB_DEFAULTS) |
config.params.table_flags,
int rc = mdbx_dbi_open(
txn_guard.get(), tablename,
create ? (MDBX_CREATE | config.params.table_flags)
: (flipcoin() ? MDBX_DB_ACCEDE
: MDBX_DB_DEFAULTS | config.params.table_flags),
&handle);
if (unlikely(rc != MDBX_SUCCESS))
failure_perror("mdbx_dbi_open()", rc);
if (unlikely(expect_failure != (rc != MDBX_SUCCESS))) {
char act[64];
snprintf(act, sizeof(act), "mdbx_dbi_open(create=%s,expect_failure=%s)",
create ? "true" : "false", expect_failure ? "true" : "false");
failure_perror(act, rc);
}
log_trace("<< testcase::db_table_create, handle %u", handle);
log_trace("<< testcase::db_table_%s%s, handle %u", create ? "create" : "open",
expect_failure ? "(expect_failure)" : "", handle);
return handle;
}
@ -579,9 +599,9 @@ void testcase::db_table_drop(MDBX_dbi handle) {
void testcase::db_table_clear(MDBX_dbi handle, MDBX_txn *txn) {
log_trace(">> testcase::db_table_clear, handle %u", handle);
int rc = mdbx_drop(txn ? txn : txn_guard.get(), handle, false);
if (unlikely(rc != MDBX_SUCCESS))
failure_perror("mdbx_drop(delete=false)", rc);
int err = mdbx_drop(txn ? txn : txn_guard.get(), handle, false);
if (unlikely(err != MDBX_SUCCESS))
failure_perror("mdbx_drop(delete=false)", err);
speculum.clear();
log_trace("<< testcase::db_table_clear");
}
@ -589,21 +609,25 @@ void testcase::db_table_clear(MDBX_dbi handle, MDBX_txn *txn) {
void testcase::db_table_close(MDBX_dbi handle) {
log_trace(">> testcase::db_table_close, handle %u", handle);
assert(!txn_guard);
int rc = mdbx_dbi_close(db_guard.get(), handle);
if (unlikely(rc != MDBX_SUCCESS))
failure_perror("mdbx_dbi_close()", rc);
int err = mdbx_dbi_close(db_guard.get(), handle);
if (unlikely(err != MDBX_SUCCESS))
failure_perror("mdbx_dbi_close()", err);
log_trace("<< testcase::db_table_close");
}
void testcase::checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
bool testcase::checkdata(const char *step, MDBX_dbi handle, MDBX_val key2check,
MDBX_val expected_valued) {
MDBX_val actual_value = expected_valued;
int rc = mdbx_get_equal_or_great(txn_guard.get(), handle, &key2check,
int err = mdbx_get_equal_or_great(txn_guard.get(), handle, &key2check,
&actual_value);
if (unlikely(rc != MDBX_SUCCESS))
failure_perror(step, rc);
if (unlikely(err != MDBX_SUCCESS)) {
if (!config.params.speculum || err != MDBX_RESULT_TRUE)
failure_perror(step, (err == MDBX_RESULT_TRUE) ? MDBX_NOTFOUND : err);
return false;
}
if (!is_samedata(&actual_value, &expected_valued))
failure("%s data mismatch", step);
return true;
}
//-----------------------------------------------------------------------------
@ -648,8 +672,8 @@ bool test_execute(const actor_config &config_const) {
size_t(config.params.nrepeat));
else
log_verbose("test successfully (iteration %zi)", iter);
config.params.keygen.seed += INT32_C(0xA4F4D37B);
log_verbose("turn keygen to %u", config.params.keygen.seed);
prng_seed(config.params.prng_seed += INT32_C(0xA4F4D37B));
log_verbose("turn PRNG to %u", config.params.prng_seed);
}
} while (config.params.nrepeat == 0 || iter < config.params.nrepeat);
@ -968,7 +992,9 @@ int testcase::insert(const keygen::buffer &akey, const keygen::buffer &adata,
}
auto it_lowerbound = insertion_result.first;
if (++it_lowerbound != speculum.end()) {
if (insertion_result.second)
++it_lowerbound;
if (it_lowerbound != speculum.end()) {
const auto cursor_lowerbound = speculum_cursors[lowerbound].get();
speculum_check_cursor("after-insert", "lowerbound", it_lowerbound,
cursor_lowerbound, MDBX_GET_CURRENT);
@ -995,30 +1021,37 @@ int testcase::insert(const keygen::buffer &akey, const keygen::buffer &adata,
int testcase::replace(const keygen::buffer &akey,
const keygen::buffer &new_data,
const keygen::buffer &old_data, MDBX_put_flags_t flags) {
const keygen::buffer &old_data, MDBX_put_flags_t flags,
bool hush_keygen_mistakes) {
int expected_err = MDBX_SUCCESS;
if (config.params.speculum) {
const auto S_key = iov2dataview(akey);
const auto S_old = iov2dataview(old_data);
const auto S_new = iov2dataview(new_data);
const auto removed = speculum.erase(SET::key_type(S_key, S_old));
if (unlikely(removed != 1)) {
if (unlikely(!removed)) {
char dump_key[128], dump_value[128];
log_error(
"speculum-%s: %s old value {%s, %s}", "replace",
(removed > 1) ? "multi" : "no",
"speculum-%s: no old pair {%s, %s} (keygen mistake)", "replace",
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
mdbx_dump_val(&old_data->value, dump_value, sizeof(dump_value)));
}
if (unlikely(!speculum.emplace(S_key, S_new).second)) {
expected_err = MDBX_NOTFOUND;
} else if (unlikely(!speculum.emplace(S_key, S_new).second)) {
char dump_key[128], dump_value[128];
log_error(
"speculum-replace: new pair not inserted {%s, %s}",
"speculum-%s: %s {%s, %s}", "replace", "new pair not inserted",
mdbx_dump_val(&akey->value, dump_key, sizeof(dump_key)),
mdbx_dump_val(&new_data->value, dump_value, sizeof(dump_value)));
expected_err = MDBX_KEYEXIST;
}
}
return mdbx_replace(txn_guard.get(), dbi, &akey->value, &new_data->value,
int err = mdbx_replace(txn_guard.get(), dbi, &akey->value, &new_data->value,
&old_data->value, flags);
if (err && err == expected_err && hush_keygen_mistakes) {
log_notice("speculum-%s: %s %d", "replace", "hust keygen mistake", err);
err = MDBX_SUCCESS;
}
return err;
}
int testcase::remove(const keygen::buffer &akey, const keygen::buffer &adata) {

View File

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

View File

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

View File

@ -107,18 +107,22 @@ uint64_t prng64_white(uint64_t &state) {
return bleach64(state);
}
uint32_t prng32(uint64_t &state) {
return (uint32_t)(prng64_careless(state) >> 32);
uint32_t prng32_fast(uint64_t &state) {
return uint32_t(prng64_careless(state) >> 32);
}
uint32_t prng32_white(uint64_t &state) {
return bleach32(uint32_t(prng64_careless(state) >> 32));
}
void prng_fill(uint64_t &state, void *ptr, size_t bytes) {
uint32_t u32 = prng32(state);
uint32_t u32 = prng32_fast(state);
while (bytes >= 4) {
memcpy(ptr, &u32, 4);
ptr = (uint32_t *)ptr + 1;
bytes -= 4;
u32 = prng32(state);
u32 = prng32_fast(state);
}
switch (bytes & 3) {
@ -136,11 +140,11 @@ void prng_fill(uint64_t &state, void *ptr, size_t bytes) {
}
}
static __thread uint64_t prng_state;
/* __thread */ uint64_t prng_state;
void prng_seed(uint64_t seed) { prng_state = bleach64(seed); }
uint32_t prng32(void) { return prng32(prng_state); }
uint32_t prng32(void) { return prng32_white(prng_state); }
uint64_t prng64(void) { return prng64_white(prng_state); }

View File

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

View File

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