diff options
author | George Hazan <ghazan@miranda.im> | 2018-06-05 13:48:05 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2018-06-05 13:48:13 +0300 |
commit | c4d3dc0ddc205412d4d727d505344fe343f01a77 (patch) | |
tree | 2bb2b736550b59dd69bc565feff101b89085cadf /plugins | |
parent | 475038623a1ee85c2e63031fe01dc8c8b620f067 (diff) |
Dbx_mdbx:
- fixes problem with false database corruption reporting when database file shrinks;
- code merge with libmsbx master;
- obsoleted DllMain function expunged;
- manual SRW locks replaced with those provided from Windows when possible
- version bump
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/Dbx_mdbx/src/init.cpp | 6 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/.gitignore | 3 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt | 22 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/Makefile | 20 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/README-RU.md | 62 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/README.md | 54 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/TODO.md | 2 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/appveyor.yml | 2 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/src/bits.h | 13 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c | 14 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/src/lck-windows.c | 177 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/src/mdbx.c | 667 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/src/osal.c | 35 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/src/osal.h | 115 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c | 4 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/src/version.c | 2 | ||||
-rw-r--r-- | plugins/Dbx_mdbx/src/version.h | 4 |
17 files changed, 789 insertions, 413 deletions
diff --git a/plugins/Dbx_mdbx/src/init.cpp b/plugins/Dbx_mdbx/src/init.cpp index aa84d1fdd4..14a4394d2a 100644 --- a/plugins/Dbx_mdbx/src/init.cpp +++ b/plugins/Dbx_mdbx/src/init.cpp @@ -27,12 +27,6 @@ EXTERN_C void NTAPI tls_callback(PVOID module, DWORD reason, PVOID reserved); CMPlugin g_plugin;
-BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD reason, LPVOID reserved)
-{
- tls_callback(hInstDLL, reason, reserved);
- return TRUE;
-}
-
/////////////////////////////////////////////////////////////////////////////////////////
static PLUGININFOEX pluginInfoEx =
diff --git a/plugins/Dbx_mdbx/src/libmdbx/.gitignore b/plugins/Dbx_mdbx/src/libmdbx/.gitignore index 981df32c76..2ea34c8009 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/.gitignore +++ b/plugins/Dbx_mdbx/src/libmdbx/.gitignore @@ -16,6 +16,7 @@ Win32/ build-* cmake-build-* core +example libmdbx.creator.user mdbx-dll.VC.VC.opendb mdbx-dll.VC.db @@ -25,7 +26,7 @@ mdbx_copy mdbx_dump mdbx_load mdbx_stat -ootest/test +mdbx_test test.log test/test.vcxproj.user test/tmp.db diff --git a/plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt b/plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt index 5546488cd1..b664075556 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt +++ b/plugins/Dbx_mdbx/src/libmdbx/CMakeLists.txt @@ -1,17 +1,22 @@ cmake_minimum_required(VERSION 2.8.7) - set(TARGET mdbx) project(${TARGET}) -# FIXME/TODO: Same as https://github.com/leo-yuriev/libfpta +message(WARNING " +*************************************************************** + MDBX is under active development, database format and API + aren't stable at least until 2018Q3. New version won't be + backwards compatible. Main focus of the rework is to provide + clear and robust API and new features. +*************************************************************** +") + set(MDBX_VERSION_MAJOR 0) -set(MDBX_VERSION_MINOR 0) -set(MDBX_VERSION_PATCH 0) -set(MDBX_VERSION_STRING ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}.${MDBX_VERSION_PATCH}) +set(MDBX_VERSION_MINOR 1) +set(MDBX_VERSION_RELEASE 3) +set(MDBX_VERSION_REVISION 1) -add_definitions(-DMDBX_VERSION_MAJOR=${MDBX_VERSION_MAJOR}) -add_definitions(-DMDBX_VERSION_MINOR=${MDBX_VERSION_MINOR}) -add_definitions(-DMDBX_VERSION_PATCH=${MDBX_VERSION_PATCH}) +set(MDBX_VERSION_STRING ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}.${MDBX_VERSION_RELEASE}) enable_language(C) enable_language(CXX) @@ -34,7 +39,6 @@ else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-constant-logical-operand") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections") diff --git a/plugins/Dbx_mdbx/src/libmdbx/Makefile b/plugins/Dbx_mdbx/src/libmdbx/Makefile index 0552412ed0..dabf2f98e7 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/Makefile +++ b/plugins/Dbx_mdbx/src/libmdbx/Makefile @@ -23,11 +23,7 @@ suffix ?= CC ?= gcc CXX ?= g++ -ifeq ($(shell (export LC_ALL=C; ($(CC) --version 2>&1; $(CC) -v 2>&1) | grep -q -i 'e2k' && echo yes)),yes) -CFLAGS ?= -O3 -g3 -Wall -Werror -Wextra -ffunction-sections -fPIC -fvisibility=hidden -else CFLAGS ?= -O2 -g3 -Wall -Werror -Wextra -ffunction-sections -fPIC -fvisibility=hidden -endif XCFLAGS ?= -DNDEBUG=1 -DMDBX_DEBUG=0 -DLIBMDBX_EXPORTS=1 CFLAGS += -D_GNU_SOURCE=1 -std=gnu11 -pthread $(XCFLAGS) @@ -37,7 +33,7 @@ TESTLOG ?= $(shell [ -d /dev/shm ] && echo /dev/shm || echo /tmp)/mdbx-test.log # LY: '--no-as-needed,-lrt' for ability to built with modern glibc, but then run with the old LDFLAGS ?= -Wl,--gc-sections,-z,relro,-O,--no-as-needed,-lrt -EXE_LDFLAGS ?= $(LDFLAGS) -static +EXE_LDFLAGS ?= -pthread -lrt # LY: just for benchmarking IOARENA ?= $(shell \ @@ -63,7 +59,7 @@ TEST_OBJ := $(patsubst %.cc,%.o,$(TEST_SRC)) .PHONY: mdbx all install clean check coverage -all: $(LIBRARIES) $(TOOLS) test/test example +all: $(LIBRARIES) $(TOOLS) mdbx_test example mdbx: libmdbx.a libmdbx.so @@ -83,16 +79,16 @@ install: $(LIBRARIES) $(TOOLS) $(HEADERS) && cp -t $(SANDBOX)$(mandir)/man1 $(MANPAGES) clean: - rm -rf $(TOOLS) test/test @* *.[ao] *.[ls]o *~ tmp.db/* *.gcov *.log *.err src/*.o test/*.o + rm -rf $(TOOLS) mdbx_test @* *.[ao] *.[ls]o *~ tmp.db/* *.gcov *.log *.err src/*.o test/*.o check: all - rm -f $(TESTDB) $(TESTLOG) && (set -o pipefail; test/test --pathname=$(TESTDB) --dont-cleanup-after basic | tee -a $(TESTLOG) | tail -n 42) && ./mdbx_chk -vvn $(TESTDB) + rm -f $(TESTDB) $(TESTLOG) && (set -o pipefail; ./mdbx_test --pathname=$(TESTDB) --dont-cleanup-after basic | tee -a $(TESTLOG) | tail -n 42) && ./mdbx_chk -vvn $(TESTDB) check-singleprocess: all - rm -f $(TESTDB) $(TESTLOG) && (set -o pipefail; test/test --pathname=$(TESTDB) --dont-cleanup-after --hill | tee -a $(TESTLOG) | tail -n 42) && ./mdbx_chk -vvn $(TESTDB) + rm -f $(TESTDB) $(TESTLOG) && (set -o pipefail; ./mdbx_test --pathname=$(TESTDB) --dont-cleanup-after --hill | tee -a $(TESTLOG) | tail -n 42) && ./mdbx_chk -vvn $(TESTDB) check-fault: all - rm -f $(TESTDB) $(TESTLOG) && (set -o pipefail; test/test --pathname=$(TESTDB) --inject-writefault=42 --dump-config --dont-cleanup-after basic | tee -a $(TESTLOG) | tail -n 42) && ./mdbx_chk -vvn $(TESTDB) + rm -f $(TESTDB) $(TESTLOG) && (set -o pipefail; ./mdbx_test --pathname=$(TESTDB) --inject-writefault=42 --dump-config --dont-cleanup-after basic | tee -a $(TESTLOG) | tail -n 42) && ./mdbx_chk -vvn $(TESTDB) define core-rule $(patsubst %.c,%.o,$(1)): $(1) $(CORE_INC) mdbx.h Makefile @@ -117,8 +113,8 @@ libmdbx.so: $(CORE_OBJ) mdbx_%: src/tools/mdbx_%.c libmdbx.a $(CC) $(CFLAGS) $^ $(EXE_LDFLAGS) -o $@ -test/test: $(TEST_OBJ) libmdbx.a - $(CXX) $(CXXFLAGS) $^ $(EXE_LDFLAGS) -o $@ +mdbx_test: $(TEST_OBJ) libmdbx.so + $(CXX) $(CXXFLAGS) $(TEST_OBJ) -Wl,-rpath . -L . -l mdbx $(EXE_LDFLAGS) -o $@ ############################################################################### diff --git a/plugins/Dbx_mdbx/src/libmdbx/README-RU.md b/plugins/Dbx_mdbx/src/libmdbx/README-RU.md index 095a25984f..233545552d 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/README-RU.md +++ b/plugins/Dbx_mdbx/src/libmdbx/README-RU.md @@ -14,28 +14,28 @@ and [by Yandex](https://translate.yandex.ru/translate?url=https%3A%2F%2Fgithub.c **Сейчас MDBX _активно перерабатывается_** и к середине 2018 -ожидается большое измените как API, так и формата базы данных. +ожидается большое изменение как API, так и формата базы данных. К сожалению, обновление приведет к потере совместимости с предыдущими версиями. -Цель этой революции в обеспечении более четкого надежного -API и добавлении новых функции, а также в наделении базы данных +Цель этой революции - обеспечение более четкого надежного +API и добавление новых функции, а также наделение базы данных новыми свойствами. В настоящее время MDBX предназначена для Linux, а также поддерживает Windows (начиная с Windows Server 2008) в качестве -дополнительно платформы. Поддержка других ОС может быть +дополнительной платформы. Поддержка других ОС может быть обеспечена на коммерческой основе. Однако такие усовершенствования (т. е. pull-requests) могут быть приняты в мейнстрим только в том случае, если будет доступен соответствующий публичный и бесплатный сервис непрерывной -интеграции (aka Countinious Integration). +интеграции (aka Continuous Integration). ## Содержание - [Обзор](#Обзор) - [Сравнение с другими СУБД](#Сравнение-с-другими-СУБД) - - [История & Acknowledgements](#История) + - [История & Acknowledgments](#История) - [Основные свойства](#Основные-свойства) - [Сравнение производительности](#Сравнение-производительности) - [Интегральная производительность](#Интегральная-производительность) @@ -62,8 +62,8 @@ _libmdbx_ позволяет множеству процессов совмес _libmdbx_ обеспечивает [serializability](https://en.wikipedia.org/wiki/Serializability) -изменений и согласованность данных после аварий. При этом транзакции -изменяющие данные никак не мешают операциям чтения и выполняются строго +изменений и согласованность данных после аварий. При этом транзакции, +изменяющие данные, никак не мешают операциям чтения и выполняются строго последовательно с использованием единственного [мьютекса](https://en.wikipedia.org/wiki/Mutual_exclusion). @@ -101,7 +101,7 @@ Tables](https://github.com/leo-yuriev/libfpta), aka ["Позитивные Technologies](https://www.ptsecurity.ru). -#### Acknowledgements +#### Acknowledgments Howard Chu (Symas Corporation) - the author of LMDB, from which originated the MDBX in 2015. @@ -113,7 +113,7 @@ which was used for begin development of LMDB. Основные свойства ================= -_libmdbx_ наследует все ключевые возможности и особенности от +_libmdbx_ наследует все ключевые возможности и особенности своего прародителя [LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database), но с устранением ряда описываемых далее проблем и архитектурных недочетов. @@ -129,7 +129,7 @@ _libmdbx_ наследует все ключевые возможности и [MVCC](https://ru.wikipedia.org/wiki/MVCC) и [COW](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8). Изменения строго последовательны и не блокируются чтением, - конфликты между транзакциями не возможны. + конфликты между транзакциями невозможны. При этом гарантируется чтение только зафиксированных данных, см [relaxing serializability](https://en.wikipedia.org/wiki/Serializability). 4. Чтение и поиск [без блокировок](https://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D1%83%D1%8E%D1%89%D0%B0%D1%8F_%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F), @@ -193,7 +193,7 @@ github](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015). 1. Такое сравнение не совсем правомочно, его следует делать с движками ориентированными на хранение данных в памяти ([Tarantool](https://tarantool.io/), [Redis](https://redis.io/)). - 2. Превосходство libmdbx становится еще более подавляющем, что мешает + 2. Превосходство libmdbx становится еще более подавляющим, что мешает восприятию информации. ![Comparison #1: Integral Performance](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-1.png) @@ -217,7 +217,7 @@ github](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015). - Логарифмическая шкала справа и желтые интервальные отрезки соответствуют времени выполнения транзакций. При этом каждый отрезок - показывает минимальное и максимальное время затраченное на выполнение + показывает минимальное и максимальное время, затраченное на выполнение транзакций, а крестиком отмечено среднеквадратичное значение. Выполняется **10.000 транзакций в режиме синхронной фиксации данных** на @@ -243,7 +243,7 @@ github](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015). - Логарифмическая шкала справа и желтые интервальные отрезки соответствуют времени выполнения транзакций. При этом каждый отрезок - показывает минимальное и максимальное время затраченное на выполнение + показывает минимальное и максимальное время, затраченное на выполнение транзакций, а крестиком отмечено среднеквадратичное значение. Выполняется **100.000 транзакций в режиме отложенной фиксации данных** @@ -274,7 +274,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко - Логарифмическая шкала справа и желтые интервальные отрезки соответствуют времени выполнения транзакций. При этом каждый отрезок - показывает минимальное и максимальное время затраченное на выполнение + показывает минимальное и максимальное время, затраченное на выполнение транзакций, а крестиком отмечено среднеквадратичное значение. Выполняется **1.000.000 транзакций в режиме асинхронной фиксации @@ -283,7 +283,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко консистентны на момент завершения одной из транзакций, но допускается потеря изменений из значительного количества последних транзакций. Во всех движках при этом включался режим предполагающий минимальную -нагрузку на диск по-записи, и соответственно минимальную гарантию +нагрузку на диск по записи, и соответственно минимальную гарантию сохранности данных. В _libmdbx_ при этом используется режим асинхронной записи измененных страниц на диск посредством ядра ОС и системного вызова [msync(MS_ASYNC)](https://linux.die.net/man/2/msync). @@ -348,7 +348,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко > _libmdbx_ фиксация транзакции также требует от 2 IOPS. Однако, в БД с > журналом кол-во IOPS будет меняться в зависимости от файловой системы, > но не от кол-ва записей или их объема. Тогда как в _libmdbx_ кол-во - > будет расти логарифмически от кол-во записей/строк в БД (по высоте + > будет расти логарифмически от кол-ва записей/строк в БД (по высоте > b+tree). 3. [COW](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8) @@ -364,7 +364,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко > значительного копирования данных в памяти и массы других затратных операций. > Поэтому обусловленное этим недостатком падение производительности становится > заметным только при отказе от фиксации изменений на диске. - > Соответственно, корректнее сказать что _libmdbx_ позволяет + > Соответственно, корректнее сказать, что _libmdbx_ позволяет > получить персистентность ценой минимального падения производительности. > Если же нет необходимости оперативно сохранять данные, то логичнее > использовать `std::map`. @@ -457,7 +457,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко Однако, при аварийном отключении питания или сбое в ядре ОС, на диске может быть сохранена только часть измененных страниц БД. При этом с большой -вероятностью может оказаться так, что будут сохранены мета-страницы со +вероятностью может оказаться, что будут сохранены мета-страницы со ссылками на страницы с новыми версиями данных, но не сами новые данные. В этом случае БД будет безвозвратна разрушена, даже если до аварии производилась полная синхронизация данных (посредством @@ -471,11 +471,11 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко с переносом изменений после фиксации данных. * При завершении транзакций, в зависимости от состояния - синхронности данных между диском и оперативной память, + синхронности данных между диском и оперативной памятью, _libmdbx_ помечает точки фиксации либо как сильные (strong), либо как слабые (weak). Так например, в режиме `WRITEMAP+MAPSYNC` завершаемые транзакции помечаются как - слабые, а при явной синхронизации данных как сильные. + слабые, а при явной синхронизации данных - как сильные. * В _libmdbx_ поддерживается не две, а три отдельные мета-страницы. Это позволяет выполнять фиксацию транзакций с формированием как @@ -489,20 +489,20 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко сильной фиксации. Этим обеспечивается гарантия сохранности БД. Такая гарантия надежности не дается бесплатно. Для -сохранности данных, страницы формирующие крайний снимок с +сохранности данных, страницы, формирующие крайний снимок с сильной фиксацией, не должны повторно использоваться (перезаписываться) до формирования следующей сильной точки фиксации. Таким образом, крайняя точка фиксации создает описанный выше эффект "долгого чтения". Разница же здесь в том, что при исчерпании свободных страниц ситуация будет автоматически исправлена, посредством записи изменений на диск -и формированием новой сильной точки фиксации. +и формирования новой сильной точки фиксации. Таким образом, в режиме безопасной асинхронной фиксации _libmdbx_ будет всегда использовать новые страницы до исчерпания места в БД или до явного формирования сильной точки фиксации посредством `mdbx_env_sync()`. -При этом суммарный трафик записи на диск будет примерно такой-же, -как если бы отдельно фиксировалась каждая транзакций. +При этом суммарный трафик записи на диск будет примерно такой же, +как если бы отдельно фиксировалась каждая транзакция. В текущей версии _libmdbx_ вам предоставляется выбор между безопасным режимом (по умолчанию) асинхронной фиксации, и режимом `UTTERLY_NOSYNC` когда @@ -535,9 +535,9 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко Посредством `mdbx_env_set_oomfunc()` может быть установлен внешний обработчик (callback), который будет вызван при - исчерпания свободных страниц из-за долгой операцией чтения. + исчерпании свободных страниц из-за долгой операцией чтения. Обработчику будет передан PID и pthread_id виновника. - В свою очередь обработчик может предпринять одно из действий: + В свою очередь обработчик может предпринять одно из действий: * нейтрализовать виновника (отправить сигнал kill #9), если долгое чтение выполняется сторонним процессом; @@ -545,7 +545,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко * отменить или перезапустить проблемную операцию чтения, если операция выполняется одним из потоков текущего процесса; - * подождать некоторое время, в расчете что проблемная операция + * подождать некоторое время, в расчете на то, что проблемная операция чтения будет штатно завершена; * прервать текущую операцию изменения данных с возвратом кода @@ -612,7 +612,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко 19. Возможность посредством `mdbx_is_dirty()` определить находятся ли некоторый ключ или данные в "грязной" странице БД. Таким образом, избегая лишнего копирования данных перед выполнением модифицирующих -операций (значения в размещенные "грязных" страницах могут быть +операций (значения, размещенные в "грязных" страницах, могут быть перезаписаны при изменениях, иначе они будут неизменны). 20. Корректное обновление текущей записи, в том числе сортированного @@ -632,7 +632,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко > > - обращение к уже освобожденной памяти; > - попытки повторного освобождения памяти; - > - memory corruption and segfaults. + > - повреждение памяти и ошибки сегментации. 22. Дополнительный код ошибки `MDBX_EMULTIVAL`, который возвращается из `mdbx_put()` и `mdbx_replace()` при попытке выполнить неоднозначное @@ -666,7 +666,7 @@ _libmdbx_ при этом не ведет WAL, а передает весь ко цикле обновления данных и записи на диск. Фактически _libmdbx_ выполняет постоянную компактификацию данных, но не затрачивая на это дополнительных ресурсов, а только освобождая их. При освобождении места -в БД и установки соответствующих параметров геометрии базы данных, также будет +в БД и установке соответствующих параметров геометрии базы данных, также будет уменьшаться размер файла на диске, в том числе в **Windows**. -------------------------------------------------------------------------------- diff --git a/plugins/Dbx_mdbx/src/libmdbx/README.md b/plugins/Dbx_mdbx/src/libmdbx/README.md index 216c997f08..ba6f6b8045 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/README.md +++ b/plugins/Dbx_mdbx/src/libmdbx/README.md @@ -7,25 +7,33 @@ libmdbx [![Build status](https://ci.appveyor.com/api/projects/status/ue94mlopn50dqiqg/branch/master?svg=true)](https://ci.appveyor.com/project/leo-yuriev/libmdbx/branch/master) [![Coverity Scan Status](https://scan.coverity.com/projects/12915/badge.svg)](https://scan.coverity.com/projects/reopen-libmdbx) -### Project Status - -**MDBX is under _active development_**, database format and -API aren't stable at least until 2018Q3. New version won't be -backwards compatible. Main focus of the rework is to provide -clear and robust API and new features. - -Nowadays MDBX intended for Linux and support Windows (since -Windows Server 2008) as complementary platform. Support for +## Project Status for now + + - The stable versions ([_stable/0.0_](https://github.com/leo-yuriev/libmdbx/tree/stable/0.0) and [_stable/0.1_](https://github.com/leo-yuriev/libmdbx/tree/stable/0.1) branches) of _MDBX_ are frozen, i.e. no new features or API changes, but only bug fixes. + - The next version ([_devel_](https://github.com/leo-yuriev/libmdbx/tree/devel) branch) **is under active non-public development**, i.e. current API and set of features are extreme volatile. + - The immediate goal of development is formation of the stable API and the stable internal database format, which allows realise all PLANNED FEATURES: + 1. Integrity check by [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree); + 2. Support for [raw block devices](https://en.wikipedia.org/wiki/Raw_device); + 3. Separate place (HDD) for large data items; + 4. Using "[Roaring bitmaps](http://roaringbitmap.org/about/)" inside garbage collector; + 5. Non-sequential reclaiming, like PostgreSQL's [Vacuum](https://www.postgresql.org/docs/9.1/static/sql-vacuum.html); + 6. [Asynchronous lazy data flushing](https://sites.fas.harvard.edu/~cs265/papers/kathuria-2008.pdf) to disk(s); + 7. etc... + +----- + +Nowadays MDBX intended for Linux, and support Windows (since +Windows Server 2008) as a complementary platform. Support for other OS could be implemented on commercial basis. However such enhancements (i.e. pull requests) could be accepted in -mainstream only when corresponding public and free Countinious +mainstream only when corresponding public and free Continuous Integration service will be available. ## Contents - [Overview](#overview) - [Comparison with other DBs](#comparison-with-other-dbs) - - [History & Acknowledgements](#history) + - [History & Acknowledgments](#history) - [Main features](#main-features) - [Performance comparison](#performance-comparison) - [Integral performance](#integral-performance) @@ -65,7 +73,7 @@ Because _libmdbx_ is currently overhauled, I think it's better to just link ### History -_libmdbx_ design is based on [Lightning Memory-Mapped Database](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database). +The _libmdbx_ design is based on [Lightning Memory-Mapped Database](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database). Initial development was going in [ReOpenLDAP](https://github.com/leo-yuriev/ReOpenLDAP) project, about a year later it received separate development effort and in autumn 2015 was isolated to separate project, which was [presented at Highload++ 2015 conference](http://www.highload.ru/2015/abstracts/1831.html). @@ -73,7 +81,7 @@ received separate development effort and in autumn 2015 was isolated to separate Since early 2017 _libmdbx_ is used in [Fast Positive Tables](https://github.com/leo-yuriev/libfpta), by [Positive Technologies](https://www.ptsecurity.com). -#### Acknowledgements +#### Acknowledgments Howard Chu (Symas Corporation) - the author of LMDB, from which originated the MDBX in 2015. @@ -269,7 +277,7 @@ scanning data directory. #### Long-time read transactions problem Garbage collection problem exists in all databases one way or another (e.g. VACUUM in PostgreSQL). -But in _libmbdx_ and LMDB it's even more important because of high performance and deliberate +But in _libmdbx_ and LMDB it's even more important because of high performance and deliberate simplification of internals with emphasis on performance. * Altering data during long read operation may exhaust available space on persistent storage. @@ -305,7 +313,7 @@ _libmdbx_ addresses the problem, details below. Illustrations to this problem ca In `WRITEMAP+MAPSYNC` mode dirty pages are written to persistent storage by kernel. This means that in case of application crash OS kernel will write all dirty data to disk and nothing will be lost. But in case of hardware malfunction or OS kernel fatal error only some dirty data might be synced to disk, and there is high probability that pages with metadata saved, -will point to non-saved, hence non-existent, data pages. In such situation DB is completely corrupted and can't be +will point to non-saved, hence non-existent, data pages. In such situation, DB is completely corrupted and can't be repaired even if there was full sync before the crash via `mdbx_env_sync(). _libmdbx_ addresses this by fully reimplementing write path of data: @@ -328,7 +336,7 @@ synchronization point. So last steady synchronization point creates "long-time r of memory exhaustion the problem will be immediately addressed by flushing changes to persistent storage and forming new steady synchronization point. -So in async-write mode _libmdbx_ will always use new pages until memory is exhausted or `mdbx_env_sync()`is invoked. Total +So in async-write mode _libmdbx_ will always use new pages until memory is exhausted or `mdbx_env_sync()` is invoked. Total disk usage will be almost the same as in sync-write mode. Current _libmdbx_ gives a choice of safe async-write mode (default) and `UTTERLY_NOSYNC` mode which may result in full DB @@ -344,7 +352,7 @@ Improvements over LMDB 1. `LIFO RECLAIM` mode: The newest pages are picked for reuse instead of the oldest. - This allows to minimize reclaim loop and make it execution time independent from total page count. + This allows to minimize reclaim loop and make it execution time independent of total page count. This results in OS kernel cache mechanisms working with maximum efficiency. In case of using disk controllers or storages with @@ -356,7 +364,7 @@ Improvements over LMDB `mdbx_env_set_oomfunc()` allows to set a callback, which will be called in the event of memory exhausting during long-time read transaction. Callback will be invoked with PID and pthread_id of offending thread as parameters. - Callback can do any of this things to remedy the problem: + Callback can do any of these things to remedy the problem: * wait for read transaction to finish normally; @@ -400,7 +408,7 @@ Improvements over LMDB 15. Ability to close DB in "dirty" state (without data flush and creation of steady synchronization point) via `mdbx_env_close_ex()`. -16. Ability to get addition info, including number of the oldest snapshot of DB, which is used by one of the readers. +16. Ability to get additional info, including number of the oldest snapshot of DB, which is used by one of the readers. Implemented via `mdbx_env_info()`. 17. `mdbx_del()` doesn't ignore additional argument (specifier) `data` @@ -409,7 +417,7 @@ Improvements over LMDB 18. Ability to open dbi-table with simultaneous setup of comparators for keys and values, via `mdbx_dbi_open_ex()`. -19. Ability to find out if key or value are in dirty page. This may be useful to make a decision to avoid +19. Ability to find out if key or value is in dirty page. This may be useful to make a decision to avoid excessive CoW before updates. Implemented via `mdbx_is_dirty()`. 20. Correct update of current record in `MDBX_CURRENT` mode of `mdbx_cursor_put()`, including sorted duplicated. @@ -440,12 +448,12 @@ Improvements over LMDB 27. Advanced dynamic control over DB size, including ability to choose page size via `mdbx_env_set_geometry()`, including on Windows. -28. Three meta-pages instead two, this allows to guarantee consistently update weak sync-points without risking to +28. Three meta-pages instead of two, this allows to guarantee consistently update weak sync-points without risking to corrupt last steady sync-point. -29. Automatic reclaim of freed pages to specific reserved space in the end of database file. This lowers amount of pages, +29. Automatic reclaim of freed pages to specific reserved space at the end of database file. This lowers amount of pages, loaded to memory, used in update/flush loop. In fact _libmdbx_ constantly performs compactification of data, - but doesn't use addition resources for that. Space reclaim of DB and setup of database geometry parameters also decreases + but doesn't use additional resources for that. Space reclaim of DB and setup of database geometry parameters also decreases size of the database on disk, including on Windows. -------------------------------------------------------------------------------- diff --git a/plugins/Dbx_mdbx/src/libmdbx/TODO.md b/plugins/Dbx_mdbx/src/libmdbx/TODO.md index 32c4f0ac25..810b18dc4a 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/TODO.md +++ b/plugins/Dbx_mdbx/src/libmdbx/TODO.md @@ -70,7 +70,7 @@ - [x] Привести в порядок volatile. - [x] контроль meta.mapsize. - [x] переработка формата: заголовки страниц, meta, clk... -- [x] зачистка Doxygen и бесполезных коментариев. +- [x] зачистка Doxygen и бесполезных комментариев. - [x] Добавить поле типа контрольной суммы. - [x] Добавить поле/флаг размера pgno_t. - [x] Поменять сигнатуры. diff --git a/plugins/Dbx_mdbx/src/libmdbx/appveyor.yml b/plugins/Dbx_mdbx/src/libmdbx/appveyor.yml index 0b8bb4216f..032a38c58c 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/appveyor.yml +++ b/plugins/Dbx_mdbx/src/libmdbx/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.1.2.{build} +version: 0.1.4.{build} environment: matrix: diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/bits.h b/plugins/Dbx_mdbx/src/libmdbx/src/bits.h index 7f69cdc066..e3174fc172 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/bits.h +++ b/plugins/Dbx_mdbx/src/libmdbx/src/bits.h @@ -45,6 +45,9 @@ #if _MSC_VER > 1800 # pragma warning(disable : 4464) /* relative include path contains '..' */ #endif +#if _MSC_VER > 1913 +# pragma warning(disable : 5045) /* Compiler will insert Spectre mitigation... */ +#endif #pragma warning(disable : 4710) /* 'xyz': function not inlined */ #pragma warning(disable : 4711) /* function 'xyz' selected for automatic inline expansion */ #pragma warning(disable : 4201) /* nonstandard extension used : nameless struct / union */ @@ -948,13 +951,13 @@ static __inline void mdbx_jitter4testing(bool tiny) { /* Internal prototypes and inlines */ int mdbx_reader_check0(MDBX_env *env, int rlocked, int *dead); -void mdbx_rthc_dtor(void *rthc); -void mdbx_rthc_lock(void); -void mdbx_rthc_unlock(void); int mdbx_rthc_alloc(mdbx_thread_key_t *key, MDBX_reader *begin, MDBX_reader *end); -void mdbx_rthc_remove(mdbx_thread_key_t key); -void mdbx_rthc_cleanup(void); +void mdbx_rthc_remove(const mdbx_thread_key_t key); + +void mdbx_rthc_global_init(void); +void mdbx_rthc_global_dtor(void); +void mdbx_rthc_thread_dtor(void *ptr); static __inline bool mdbx_is_power2(size_t x) { return (x & (x - 1)) == 0; } diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c b/plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c index 64f493cb15..532505e807 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/lck-posix.c @@ -32,18 +32,12 @@ /*----------------------------------------------------------------------------*/ /* rthc */ -static pthread_mutex_t mdbx_rthc_mutex = PTHREAD_MUTEX_INITIALIZER; - -void mdbx_rthc_lock(void) { - mdbx_ensure(NULL, pthread_mutex_lock(&mdbx_rthc_mutex) == 0); -} - -void mdbx_rthc_unlock(void) { - mdbx_ensure(NULL, pthread_mutex_unlock(&mdbx_rthc_mutex) == 0); +static __cold __attribute__((constructor)) void mdbx_global_constructor(void) { + mdbx_rthc_global_init(); } -void __attribute__((destructor)) mdbx_global_destructor(void) { - mdbx_rthc_cleanup(); +static __cold __attribute__((destructor)) void mdbx_global_destructor(void) { + mdbx_rthc_global_dtor(); } /*----------------------------------------------------------------------------*/ diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/lck-windows.c b/plugins/Dbx_mdbx/src/libmdbx/src/lck-windows.c index 9093ab3859..aae7c320c2 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/lck-windows.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/lck-windows.c @@ -27,30 +27,62 @@ /*----------------------------------------------------------------------------*/ /* rthc */ -static CRITICAL_SECTION rthc_critical_section; - -void NTAPI tls_callback(PVOID module, DWORD reason, PVOID reserved) { - (void)module; +static void NTAPI tls_callback(PVOID module, DWORD reason, PVOID reserved) { (void)reserved; switch (reason) { case DLL_PROCESS_ATTACH: - InitializeCriticalSection(&rthc_critical_section); + mdbx_rthc_global_init(); break; case DLL_PROCESS_DETACH: - DeleteCriticalSection(&rthc_critical_section); + mdbx_rthc_global_dtor(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: - mdbx_rthc_cleanup(); + mdbx_rthc_thread_dtor(module); break; } } -void mdbx_rthc_lock(void) { EnterCriticalSection(&rthc_critical_section); } - -void mdbx_rthc_unlock(void) { LeaveCriticalSection(&rthc_critical_section); } +/* *INDENT-OFF* */ +/* clang-format off */ +#if defined(_MSC_VER) +# pragma const_seg(push) +# pragma data_seg(push) + +# ifdef _WIN64 + /* kick a linker to create the TLS directory if not already done */ +# pragma comment(linker, "/INCLUDE:_tls_used") + /* Force some symbol references. */ +# pragma comment(linker, "/INCLUDE:mdbx_tls_callback") + /* specific const-segment for WIN64 */ +# pragma const_seg(".CRT$XLB") + const +# else + /* kick a linker to create the TLS directory if not already done */ +# pragma comment(linker, "/INCLUDE:__tls_used") + /* Force some symbol references. */ +# pragma comment(linker, "/INCLUDE:_mdbx_tls_callback") + /* specific data-segment for WIN32 */ +# pragma data_seg(".CRT$XLB") +# endif + + PIMAGE_TLS_CALLBACK mdbx_tls_callback = tls_callback; +# pragma data_seg(pop) +# pragma const_seg(pop) + +#elif defined(__GNUC__) +# ifdef _WIN64 + const +# endif + PIMAGE_TLS_CALLBACK mdbx_tls_callback __attribute__((section(".CRT$XLB"), used)) + = tls_callback; +#else +# error FIXME +#endif +/* *INDENT-ON* */ +/* clang-format on */ /*----------------------------------------------------------------------------*/ @@ -123,7 +155,10 @@ int mdbx_rdt_lock(MDBX_env *env) { /* transite from S-? (used) to S-E (locked), e.g. exclusive lock upper-part */ if (flock(env->me_lfd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER)) return MDBX_SUCCESS; - return GetLastError(); + + int rc = GetLastError(); + mdbx_shlock_releaseShared(&env->me_remap_guard); + return rc; } void mdbx_rdt_unlock(MDBX_env *env) { @@ -462,10 +497,12 @@ int mdbx_rpid_clear(MDBX_env *env) { * or otherwise the errcode. */ int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid) { (void)env; - HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid); int rc; - if (hProcess) { + if (likely(hProcess)) { rc = WaitForSingleObject(hProcess, 0); + if (unlikely(rc == WAIT_FAILED)) + rc = GetLastError(); CloseHandle(hProcess); } else { rc = GetLastError(); @@ -487,68 +524,88 @@ int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid) { } } -/*----------------------------------------------------------------------------*/ -/* shared lock - Copyright (C) 1995-2002 Brad Wilson -*/ +//---------------------------------------------------------------------------- +// shared lock +// Copyright (C) 1995-2002 Brad Wilson -void mdbx_shlock_init(MDBX_shlock *lck) -{ - lck->readerCount = lck->writerCount = 0; -} +typedef void (WINAPI *pfnSrwFunc)(PSRWLOCK); -void mdbx_shlock_acquireShared(MDBX_shlock *lck) -{ - while (1) { - // If there's a writer already, spin without unnecessarily - // interlocking the CPUs +static pfnSrwFunc fnLockInit = 0, fnLockShared = 0, fnUnlockShared = 0, fnLockExcl = 0, fnUnlockExcl = 0; - if (lck->writerCount != 0) { - YieldProcessor(); - continue; - } +void mdbx_shlock_init(MDBX_shlock *lck) { + HINSTANCE hInst = GetModuleHandleA("kernel32.dll"); + fnLockInit = (pfnSrwFunc)GetProcAddress(hInst, "InitializeSRWLock"); + if (fnLockInit != NULL) { + fnLockShared = (pfnSrwFunc)GetProcAddress(hInst, "AcquireSRWLockShared"); + fnUnlockShared = (pfnSrwFunc)GetProcAddress(hInst, "ReleaseSRWLockShared"); + fnLockExcl = (pfnSrwFunc)GetProcAddress(hInst, "AcquireSRWLockExclusive"); + fnUnlockExcl = (pfnSrwFunc)GetProcAddress(hInst, "ReleaseSRWLockExclusive"); - // Add to the readers list + fnLockInit(&lck->srwLock); + } else + lck->readerCount = lck->writerCount = 0; +} - _InterlockedIncrement((long*)&lck->readerCount); +void mdbx_shlock_acquireShared(MDBX_shlock *lck) { + if (fnLockShared) { + fnLockShared(&lck->srwLock); + return; + } - // Check for writers again (we may have been pre-empted). If - // there are no writers writing or waiting, then we're done. + while (1) { + // If there's a writer already, spin without unnecessarily + // interlocking the CPUs - if (lck->writerCount == 0) - break; + if (lck->writerCount != 0) { + YieldProcessor(); + continue; + } - // Remove from the readers list, spin, try again + // Add to the readers list - _InterlockedDecrement((long*)&lck->readerCount); - YieldProcessor(); - } -} + _InterlockedIncrement((long *)&lck->readerCount); + + // Check for writers again (we may have been pre-empted). If + // there are no writers writing or waiting, then we're done. + if (lck->writerCount == 0) + break; -void mdbx_shlock_releaseShared(MDBX_shlock *lck) -{ - _InterlockedDecrement((long*)&lck->readerCount); + // Remove from the readers list, spin, try again + _InterlockedDecrement((long *)&lck->readerCount); + YieldProcessor(); + } } -void mdbx_shlock_acquireExclusive(MDBX_shlock *lck) -{ - // See if we can become the writer (expensive, because it inter- - // locks the CPUs, so writing should be an infrequent process) +void mdbx_shlock_releaseShared(MDBX_shlock *lck) { + if (fnUnlockShared) + fnUnlockShared(&lck->srwLock); + else + _InterlockedDecrement((long *)&lck->readerCount); +} - while (_InterlockedExchange((long*)&lck->writerCount, 1) == 1) { - YieldProcessor(); - } +void mdbx_shlock_acquireExclusive(MDBX_shlock *lck) { + if (fnLockExcl) { + fnLockShared(&lck->srwLock); + return; + } - // Now we're the writer, but there may be outstanding readers. - // Spin until there aren't any more; new readers will wait now - // that we're the writer. + // See if we can become the writer (expensive, because it inter- + // locks the CPUs, so writing should be an infrequent process) + while (_InterlockedExchange((long *)&lck->writerCount, 1) == 1) { + YieldProcessor(); + } - while (lck->readerCount != 0) { - YieldProcessor(); - } + // Now we're the writer, but there may be outstanding readers. + // Spin until there aren't any more; new readers will wait now + // that we're the writer. + while (lck->readerCount != 0) { + YieldProcessor(); + } } -void mdbx_shlock_releaseExclusive(MDBX_shlock *lck) -{ - lck->writerCount = 0; +void mdbx_shlock_releaseExclusive(MDBX_shlock *lck) { + if (fnUnlockExcl) + fnUnlockExcl(&lck->srwLock); + else + lck->writerCount = 0; } diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/mdbx.c b/plugins/Dbx_mdbx/src/libmdbx/src/mdbx.c index 7845e6d733..9264c31105 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/mdbx.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/mdbx.c @@ -163,43 +163,260 @@ typedef struct rthc_entry_t { #define RTHC_INITIAL_LIMIT 16 #endif -static unsigned rthc_count; -static unsigned rthc_limit = RTHC_INITIAL_LIMIT; +#if defined(_WIN32) || defined(_WIN64) +static CRITICAL_SECTION rthc_critical_section; +#else +int __cxa_thread_atexit_impl(void (*dtor)(void *), void *obj, void *dso_symbol) + __attribute__((weak)); +static pthread_mutex_t mdbx_rthc_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t mdbx_rthc_cond = PTHREAD_COND_INITIALIZER; +static mdbx_thread_key_t mdbx_rthc_key; +static volatile uint32_t mdbx_rthc_pending; + +static void __cold mdbx_workaround_glibc_bug21031(void) { + /* Workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=21031 + * + * Due race between pthread_key_delete() and __nptl_deallocate_tsd() + * The destructor(s) of thread-local-storate object(s) may be running + * in another thread(s) and be blocked or not finished yet. + * In such case we get a SEGFAULT after unload this library DSO. + * + * So just by yielding a few timeslices we give a chance + * to such destructor(s) for completion and avoids segfault. */ + sched_yield(); + sched_yield(); + sched_yield(); +} +#endif + +static unsigned rthc_count, rthc_limit; +static rthc_entry_t *rthc_table; static rthc_entry_t rthc_table_static[RTHC_INITIAL_LIMIT]; -static rthc_entry_t *rthc_table = rthc_table_static; -__cold void mdbx_rthc_dtor(void *ptr) { - MDBX_reader *rthc = (MDBX_reader *)ptr; +static __cold void mdbx_rthc_lock(void) { +#if defined(_WIN32) || defined(_WIN64) + EnterCriticalSection(&rthc_critical_section); +#else + mdbx_ensure(nullptr, pthread_mutex_lock(&mdbx_rthc_mutex) == 0); +#endif +} + +static __cold void mdbx_rthc_unlock(void) { +#if defined(_WIN32) || defined(_WIN64) + LeaveCriticalSection(&rthc_critical_section); +#else + mdbx_ensure(nullptr, pthread_mutex_unlock(&mdbx_rthc_mutex) == 0); +#endif +} + +static __inline int mdbx_thread_key_create(mdbx_thread_key_t *key) { + int rc; +#if defined(_WIN32) || defined(_WIN64) + *key = TlsAlloc(); + rc = (*key != TLS_OUT_OF_INDEXES) ? MDBX_SUCCESS : GetLastError(); +#else + rc = pthread_key_create(key, nullptr); +#endif + mdbx_trace("&key = %p, value 0x%x, rc %d", key, (unsigned)*key, rc); + return rc; +} + +static __inline void mdbx_thread_key_delete(mdbx_thread_key_t key) { + mdbx_trace("key = 0x%x", (unsigned)key); +#if defined(_WIN32) || defined(_WIN64) + mdbx_ensure(nullptr, TlsFree(key)); +#else + mdbx_ensure(nullptr, pthread_key_delete(key) == 0); + mdbx_workaround_glibc_bug21031(); +#endif +} + +static __inline void *mdbx_thread_rthc_get(mdbx_thread_key_t key) { +#if defined(_WIN32) || defined(_WIN64) + return TlsGetValue(key); +#else + return pthread_getspecific(key); +#endif +} +static void mdbx_thread_rthc_set(mdbx_thread_key_t key, const void *value) { +#if defined(_WIN32) || defined(_WIN64) + mdbx_ensure(nullptr, TlsSetValue(key, (void *)value)); +#else +#define MDBX_THREAD_RTHC_ZERO 0 +#define MDBX_THREAD_RTHC_REGISTERD 1 +#define MDBX_THREAD_RTHC_COUNTED 2 + static __thread uint32_t thread_registration_state; + if (value && unlikely(thread_registration_state == MDBX_THREAD_RTHC_ZERO)) { + thread_registration_state = MDBX_THREAD_RTHC_REGISTERD; + mdbx_trace("thread registered 0x%" PRIxPTR, (uintptr_t)mdbx_thread_self()); + if (&__cxa_thread_atexit_impl == nullptr || + __cxa_thread_atexit_impl(mdbx_rthc_thread_dtor, + &thread_registration_state, + (void *)&mdbx_version /* dso_anchor */)) { + mdbx_ensure(nullptr, pthread_setspecific( + mdbx_rthc_key, &thread_registration_state) == 0); + thread_registration_state = MDBX_THREAD_RTHC_COUNTED; + const unsigned count_before = mdbx_atomic_add32(&mdbx_rthc_pending, 1); + mdbx_ensure(nullptr, count_before < INT_MAX); + mdbx_trace("fallback to pthreads' tsd, key 0x%x, count %u", + (unsigned)mdbx_rthc_key, count_before); + (void)count_before; + } + } + mdbx_ensure(nullptr, pthread_setspecific(key, value) == 0); +#endif +} + +__cold void mdbx_rthc_global_init(void) { + rthc_limit = RTHC_INITIAL_LIMIT; + rthc_table = rthc_table_static; +#if defined(_WIN32) || defined(_WIN64) + InitializeCriticalSection(&rthc_critical_section); +#else + mdbx_ensure(nullptr, + pthread_key_create(&mdbx_rthc_key, mdbx_rthc_thread_dtor) == 0); + mdbx_trace("pid %d, &mdbx_rthc_key = %p, value 0x%x", mdbx_getpid(), + &mdbx_rthc_key, (unsigned)mdbx_rthc_key); +#endif +} + +/* dtor called for thread, i.e. for all mdbx's environment objects */ +void mdbx_rthc_thread_dtor(void *ptr) { mdbx_rthc_lock(); + mdbx_trace(">> pid %d, thread 0x%" PRIxPTR ", rthc %p", mdbx_getpid(), + (uintptr_t)mdbx_thread_self(), ptr); + const mdbx_pid_t self_pid = mdbx_getpid(); for (unsigned i = 0; i < rthc_count; ++i) { - if (rthc >= rthc_table[i].begin && rthc < rthc_table[i].end) { - if (rthc->mr_pid == self_pid) { - rthc->mr_pid = 0; - mdbx_coherent_barrier(); - } - break; + const mdbx_thread_key_t key = rthc_table[i].key; + MDBX_reader *const rthc = mdbx_thread_rthc_get(key); + if (rthc < rthc_table[i].begin || rthc >= rthc_table[i].end) + continue; +#if !defined(_WIN32) && !defined(_WIN64) + if (pthread_setspecific(key, nullptr) != 0) { + mdbx_trace("== thread 0x%" PRIxPTR + ", rthc %p: ignore race with tsd-key deletion", + (uintptr_t)mdbx_thread_self(), ptr); + continue /* ignore race with tsd-key deletion by mdbx_env_close() */; + } +#endif + + mdbx_trace("== thread 0x%" PRIxPTR + ", rthc %p, [%i], %p ... %p (%+i), rtch-pid %i, " + "current-pid %i", + (uintptr_t)mdbx_thread_self(), rthc, i, rthc_table[i].begin, + rthc_table[i].end, (int)(rthc - rthc_table[i].begin), + rthc->mr_pid, self_pid); + if (rthc->mr_pid == self_pid) { + mdbx_trace("==== thread 0x%" PRIxPTR ", rthc %p, cleanup", + (uintptr_t)mdbx_thread_self(), rthc); + rthc->mr_pid = 0; } } + +#if defined(_WIN32) || defined(_WIN64) + mdbx_trace("<< thread 0x%" PRIxPTR ", rthc %p", (uintptr_t)mdbx_thread_self(), + ptr); mdbx_rthc_unlock(); +#else + const char self_registration = *(char *)ptr; + *(char *)ptr = MDBX_THREAD_RTHC_ZERO; + mdbx_trace("== thread 0x%" PRIxPTR ", rthc %p, pid %d, self-status %d", + (uintptr_t)mdbx_thread_self(), ptr, mdbx_getpid(), + self_registration); + if (self_registration == MDBX_THREAD_RTHC_COUNTED) + mdbx_ensure(nullptr, mdbx_atomic_sub32(&mdbx_rthc_pending, 1) > 0); + + if (mdbx_rthc_pending == 0) { + mdbx_trace("== thread 0x%" PRIxPTR ", rthc %p, pid %d, wake", + (uintptr_t)mdbx_thread_self(), ptr, mdbx_getpid()); + mdbx_ensure(nullptr, pthread_cond_broadcast(&mdbx_rthc_cond) == 0); + } + + mdbx_trace("<< thread 0x%" PRIxPTR ", rthc %p", (uintptr_t)mdbx_thread_self(), + ptr); + /* Allow tail call optimization, i.e. gcc should generate the jmp instruction + * instead of a call for pthread_mutex_unlock() and therefore CPU could not + * return to current DSO's code section, which may be unloaded immediately + * after the mutex got released. */ + pthread_mutex_unlock(&mdbx_rthc_mutex); +#endif } -__cold void mdbx_rthc_cleanup(void) { +__cold void mdbx_rthc_global_dtor(void) { + mdbx_trace( + ">> pid %d, &mdbx_rthc_global_dtor %p, &mdbx_rthc_thread_dtor = %p, " + "&mdbx_rthc_remove = %p", + mdbx_getpid(), &mdbx_rthc_global_dtor, &mdbx_rthc_thread_dtor, + &mdbx_rthc_remove); + mdbx_rthc_lock(); +#if !defined(_WIN32) && !defined(_WIN64) + char *rthc = (char *)pthread_getspecific(mdbx_rthc_key); + mdbx_trace("== thread 0x%" PRIxPTR ", rthc %p, pid %d, self-status %d", + (uintptr_t)mdbx_thread_self(), rthc, mdbx_getpid(), + rthc ? *rthc : -1); + if (rthc) { + const char self_registration = *(char *)rthc; + *rthc = MDBX_THREAD_RTHC_ZERO; + if (self_registration == MDBX_THREAD_RTHC_COUNTED) + mdbx_ensure(nullptr, mdbx_atomic_sub32(&mdbx_rthc_pending, 1) > 0); + } + + struct timespec abstime; + mdbx_ensure(nullptr, clock_gettime(CLOCK_REALTIME, &abstime) == 0); + abstime.tv_nsec += 1000000000l / 10; + if (abstime.tv_nsec >= 1000000000l) { + abstime.tv_nsec -= 1000000000l; + abstime.tv_sec += 1; + } +#if MDBX_DEBUG > 0 + abstime.tv_sec += 600; +#endif + + for (unsigned left; (left = mdbx_rthc_pending) > 0;) { + mdbx_trace("pid %d, pending %u, wait for...", mdbx_getpid(), left); + const int rc = + pthread_cond_timedwait(&mdbx_rthc_cond, &mdbx_rthc_mutex, &abstime); + if (rc && rc != EINTR) + break; + } + mdbx_thread_key_delete(mdbx_rthc_key); +#endif + const mdbx_pid_t self_pid = mdbx_getpid(); for (unsigned i = 0; i < rthc_count; ++i) { - mdbx_thread_key_t key = rthc_table[i].key; - MDBX_reader *rthc = mdbx_thread_rthc_get(key); - if (rthc) { - mdbx_thread_rthc_set(key, NULL); + const mdbx_thread_key_t key = rthc_table[i].key; + mdbx_thread_key_delete(key); + for (MDBX_reader *rthc = rthc_table[i].begin; rthc < rthc_table[i].end; + ++rthc) { + mdbx_trace("== [%i] = key %u, %p ... %p, rthc %p (%+i), " + "rthc-pid %i, current-pid %i", + i, key, rthc_table[i].begin, rthc_table[i].end, rthc, + (int)(rthc - rthc_table[i].begin), rthc->mr_pid, self_pid); if (rthc->mr_pid == self_pid) { rthc->mr_pid = 0; - mdbx_coherent_barrier(); + mdbx_trace("== cleanup %p", rthc); } } } + + rthc_limit = rthc_count = 0; + if (rthc_table != rthc_table_static) + free(rthc_table); + rthc_table = nullptr; mdbx_rthc_unlock(); + +#if defined(_WIN32) || defined(_WIN64) + DeleteCriticalSection(&rthc_critical_section); +#else + /* LY: yielding a few timeslices to give a more chance + * to racing destructor(s) for completion. */ + mdbx_workaround_glibc_bug21031(); +#endif + + mdbx_trace("<< pid %d\n", mdbx_getpid()); } __cold int mdbx_rthc_alloc(mdbx_thread_key_t *key, MDBX_reader *begin, @@ -212,11 +429,13 @@ __cold int mdbx_rthc_alloc(mdbx_thread_key_t *key, MDBX_reader *begin, return rc; mdbx_rthc_lock(); + mdbx_trace(">> key 0x%x, rthc_count %u, rthc_limit %u", *key, rthc_count, + rthc_limit); if (rthc_count == rthc_limit) { rthc_entry_t *new_table = - realloc((rthc_table == rthc_table_static) ? NULL : rthc_table, + realloc((rthc_table == rthc_table_static) ? nullptr : rthc_table, sizeof(rthc_entry_t) * rthc_limit * 2); - if (new_table == NULL) { + if (new_table == nullptr) { rc = MDBX_ENOMEM; goto bailout; } @@ -225,11 +444,13 @@ __cold int mdbx_rthc_alloc(mdbx_thread_key_t *key, MDBX_reader *begin, rthc_table = new_table; rthc_limit *= 2; } - + mdbx_trace("== [%i] = key %u, %p ... %p", rthc_count, *key, begin, end); rthc_table[rthc_count].key = *key; rthc_table[rthc_count].begin = begin; rthc_table[rthc_count].end = end; ++rthc_count; + mdbx_trace("<< key 0x%x, rthc_count %u, rthc_limit %u", *key, rthc_count, + rthc_limit); mdbx_rthc_unlock(); return MDBX_SUCCESS; @@ -239,18 +460,25 @@ bailout: return rc; } -__cold void mdbx_rthc_remove(mdbx_thread_key_t key) { - mdbx_rthc_lock(); +__cold void mdbx_rthc_remove(const mdbx_thread_key_t key) { mdbx_thread_key_delete(key); + mdbx_rthc_lock(); + mdbx_trace(">> key 0x%x, rthc_count %u, rthc_limit %u", key, rthc_count, + rthc_limit); for (unsigned i = 0; i < rthc_count; ++i) { if (key == rthc_table[i].key) { const mdbx_pid_t self_pid = mdbx_getpid(); + mdbx_trace("== [%i], %p ...%p, current-pid %d", i, rthc_table[i].begin, + rthc_table[i].end, self_pid); + for (MDBX_reader *rthc = rthc_table[i].begin; rthc < rthc_table[i].end; - ++rthc) - if (rthc->mr_pid == self_pid) + ++rthc) { + if (rthc->mr_pid == self_pid) { rthc->mr_pid = 0; - mdbx_coherent_barrier(); + mdbx_trace("== cleanup %p", rthc); + } + } if (--rthc_count > 0) rthc_table[i] = rthc_table[rthc_count]; else if (rthc_table != rthc_table_static) { @@ -262,6 +490,8 @@ __cold void mdbx_rthc_remove(mdbx_thread_key_t key) { } } + mdbx_trace("<< key 0x%x, rthc_count %u, rthc_limit %u", key, rthc_count, + rthc_limit); mdbx_rthc_unlock(); } @@ -732,63 +962,81 @@ enum { #define MDBX_END_SLOT 0x80 /* release any reader slot if MDBX_NOTLS */ static int mdbx_txn_end(MDBX_txn *txn, unsigned mode); -static int mdbx_page_get(MDBX_cursor *mc, pgno_t pgno, MDBX_page **mp, - int *lvl); -static int mdbx_page_search_root(MDBX_cursor *mc, MDBX_val *key, int modify); +static int __must_check_result mdbx_page_get(MDBX_cursor *mc, pgno_t pgno, + MDBX_page **mp, int *lvl); +static int __must_check_result mdbx_page_search_root(MDBX_cursor *mc, + MDBX_val *key, int modify); #define MDBX_PS_MODIFY 1 #define MDBX_PS_ROOTONLY 2 #define MDBX_PS_FIRST 4 #define MDBX_PS_LAST 8 -static int mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, int flags); -static int mdbx_page_merge(MDBX_cursor *csrc, MDBX_cursor *cdst); +static int __must_check_result mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, + int flags); +static int __must_check_result mdbx_page_merge(MDBX_cursor *csrc, + MDBX_cursor *cdst); #define MDBX_SPLIT_REPLACE MDBX_APPENDDUP /* newkey is not new */ -static int mdbx_page_split(MDBX_cursor *mc, MDBX_val *newkey, MDBX_val *newdata, - pgno_t newpgno, unsigned nflags); - -static int mdbx_read_header(MDBX_env *env, MDBX_meta *meta); -static int mdbx_sync_locked(MDBX_env *env, unsigned flags, - MDBX_meta *const pending); +static int __must_check_result mdbx_page_split(MDBX_cursor *mc, + MDBX_val *newkey, + MDBX_val *newdata, + pgno_t newpgno, unsigned nflags); + +static int __must_check_result mdbx_read_header(MDBX_env *env, MDBX_meta *meta, + uint64_t *filesize); +static int __must_check_result mdbx_sync_locked(MDBX_env *env, unsigned flags, + MDBX_meta *const pending); static void mdbx_env_close0(MDBX_env *env); static MDBX_node *mdbx_node_search(MDBX_cursor *mc, MDBX_val *key, int *exactp); -static int mdbx_node_add(MDBX_cursor *mc, unsigned indx, MDBX_val *key, - MDBX_val *data, pgno_t pgno, unsigned flags); +static int __must_check_result mdbx_node_add(MDBX_cursor *mc, unsigned indx, + MDBX_val *key, MDBX_val *data, + pgno_t pgno, unsigned flags); static void mdbx_node_del(MDBX_cursor *mc, size_t ksize); static void mdbx_node_shrink(MDBX_page *mp, unsigned indx); -static int mdbx_node_move(MDBX_cursor *csrc, MDBX_cursor *cdst, int fromleft); -static int mdbx_node_read(MDBX_cursor *mc, MDBX_node *leaf, MDBX_val *data); +static int __must_check_result mdbx_node_move(MDBX_cursor *csrc, + MDBX_cursor *cdst, int fromleft); +static int __must_check_result mdbx_node_read(MDBX_cursor *mc, MDBX_node *leaf, + MDBX_val *data); static size_t mdbx_leaf_size(MDBX_env *env, MDBX_val *key, MDBX_val *data); static size_t mdbx_branch_size(MDBX_env *env, MDBX_val *key); -static int mdbx_rebalance(MDBX_cursor *mc); -static int mdbx_update_key(MDBX_cursor *mc, MDBX_val *key); +static int __must_check_result mdbx_rebalance(MDBX_cursor *mc); +static int __must_check_result mdbx_update_key(MDBX_cursor *mc, MDBX_val *key); static void mdbx_cursor_pop(MDBX_cursor *mc); -static int mdbx_cursor_push(MDBX_cursor *mc, MDBX_page *mp); - -static int mdbx_cursor_del0(MDBX_cursor *mc); -static int mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, - unsigned flags); -static int mdbx_cursor_sibling(MDBX_cursor *mc, int move_right); -static int mdbx_cursor_next(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, - MDBX_cursor_op op); -static int mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, - MDBX_cursor_op op); -static int mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, - MDBX_cursor_op op, int *exactp); -static int mdbx_cursor_first(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data); -static int mdbx_cursor_last(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data); - -static void mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi, - MDBX_xcursor *mx); -static void mdbx_xcursor_init0(MDBX_cursor *mc); -static void mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node); -static void mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx, - int force); - -static int mdbx_drop0(MDBX_cursor *mc, int subs); +static int __must_check_result mdbx_cursor_push(MDBX_cursor *mc, MDBX_page *mp); + +static int __must_check_result mdbx_cursor_del0(MDBX_cursor *mc); +static int __must_check_result mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, + MDBX_val *key, MDBX_val *data, + unsigned flags); +static int __must_check_result mdbx_cursor_sibling(MDBX_cursor *mc, + int move_right); +static int __must_check_result mdbx_cursor_next(MDBX_cursor *mc, MDBX_val *key, + MDBX_val *data, + MDBX_cursor_op op); +static int __must_check_result mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, + MDBX_val *data, + MDBX_cursor_op op); +static int __must_check_result mdbx_cursor_set(MDBX_cursor *mc, MDBX_val *key, + MDBX_val *data, + MDBX_cursor_op op, int *exactp); +static int __must_check_result mdbx_cursor_first(MDBX_cursor *mc, MDBX_val *key, + MDBX_val *data); +static int __must_check_result mdbx_cursor_last(MDBX_cursor *mc, MDBX_val *key, + MDBX_val *data); + +static int __must_check_result mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, + MDBX_dbi dbi, MDBX_xcursor *mx); +static int __must_check_result mdbx_xcursor_init0(MDBX_cursor *mc); +static int __must_check_result mdbx_xcursor_init1(MDBX_cursor *mc, + MDBX_node *node); +static int __must_check_result mdbx_xcursor_init2(MDBX_cursor *mc, + MDBX_xcursor *src_mx, + int force); + +static int __must_check_result mdbx_drop0(MDBX_cursor *mc, int subs); static MDBX_cmp_func mdbx_cmp_memn, mdbx_cmp_memnr, mdbx_cmp_int_ai, mdbx_cmp_int_a2, mdbx_cmp_int_ua; @@ -917,6 +1165,7 @@ void __cold mdbx_debug_log(int type, const char *function, int line, else if (line > 0) fprintf(stderr, "%d: ", line); vfprintf(stderr, fmt, args); + fflush(stderr); } va_end(args); } @@ -1074,13 +1323,15 @@ static void mdbx_cursor_chk(MDBX_cursor *mc) { /* Count all the pages in each DB and in the freelist and make sure * it matches the actual number of pages being used. * All named DBs must be open for a correct count. */ -static void mdbx_audit(MDBX_txn *txn) { +static int mdbx_audit(MDBX_txn *txn) { MDBX_cursor mc; MDBX_val key, data; int rc; pgno_t freecount = 0; - mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; while ((rc = mdbx_cursor_get(&mc, &key, &data, MDBX_NEXT)) == 0) freecount += *(pgno_t *)data.iov_base; mdbx_tassert(txn, rc == MDBX_NOTFOUND); @@ -1090,7 +1341,9 @@ static void mdbx_audit(MDBX_txn *txn) { MDBX_xcursor mx; if (!(txn->mt_dbflags[i] & DB_VALID)) continue; - mdbx_cursor_init(&mc, txn, i, &mx); + rc = mdbx_cursor_init(&mc, txn, i, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; if (txn->mt_dbs[i].md_root == P_INVALID) continue; count += txn->mt_dbs[i].md_branch_pages + txn->mt_dbs[i].md_leaf_pages + @@ -1117,7 +1370,9 @@ static void mdbx_audit(MDBX_txn *txn) { " total: %" PRIaPGNO " next_pgno: %" PRIaPGNO "\n", txn->mt_txnid, freecount, count + NUM_METAS, freecount + count + NUM_METAS, txn->mt_next_pgno); + return MDBX_CORRUPTED; } + return MDBX_SUCCESS; } int mdbx_cmp(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *a, @@ -1878,6 +2133,10 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp, * catch-up with itself by growing while trying to save it. */ flags &= ~(MDBX_ALLOC_GC | MDBX_ALLOC_KICK | MDBX_COALESCE | MDBX_LIFORECLAIM); + } else if (unlikely(txn->mt_dbs[FREE_DBI].md_entries == 0)) { + /* avoid (recursive) search inside empty tree and while tree is updating, + * https://github.com/leo-yuriev/libmdbx/issues/31 */ + flags &= ~MDBX_ALLOC_GC; } } @@ -1944,7 +2203,9 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp, /* Prepare to fetch more and coalesce */ oldest = (flags & MDBX_LIFORECLAIM) ? mdbx_find_oldest(txn) : env->me_oldest[0]; - mdbx_cursor_init(&recur, txn, FREE_DBI, NULL); + rc = mdbx_cursor_init(&recur, txn, FREE_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + goto fail; if (flags & MDBX_LIFORECLAIM) { /* Begin from oldest reader if any */ if (oldest > 2) { @@ -3223,7 +3484,9 @@ static int mdbx_freelist_save(MDBX_txn *txn) { unsigned cleanup_reclaimed_pos = 0, refill_reclaimed_pos = 0; const bool lifo = (env->me_flags & MDBX_LIFORECLAIM) != 0; - mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; /* MDBX_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */ const intptr_t clean_limit = @@ -3945,7 +4208,9 @@ int mdbx_txn_commit(MDBX_txn *txn) { MDBX_val data; data.iov_len = sizeof(MDBX_db); - mdbx_cursor_init(&mc, txn, MAIN_DBI, NULL); + rc = mdbx_cursor_init(&mc, txn, MAIN_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + goto fail; for (i = CORE_DBS; i < txn->mt_numdbs; i++) { if (txn->mt_dbflags[i] & DB_DIRTY) { if (unlikely(TXN_DBI_CHANGED(txn, i))) { @@ -4006,11 +4271,17 @@ fail: /* Read the environment parameters of a DB environment * before mapping it into memory. */ -static int __cold mdbx_read_header(MDBX_env *env, MDBX_meta *meta) { +static int __cold mdbx_read_header(MDBX_env *env, MDBX_meta *meta, + uint64_t *filesize) { assert(offsetof(MDBX_page, mp_meta) == PAGEHDRSZ); + + int rc = mdbx_filesize(env->me_fd, filesize); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + memset(meta, 0, sizeof(MDBX_meta)); meta->mm_datasync_sign = MDBX_DATASIGN_WEAK; - int rc = MDBX_CORRUPTED; + rc = MDBX_CORRUPTED; /* Read twice all meta pages so we can find the latest one. */ unsigned loop_limit = NUM_METAS * 2; @@ -4146,6 +4417,17 @@ static int __cold mdbx_read_header(MDBX_env *env, MDBX_meta *meta) { continue; } + /* LY: check filesize & used_bytes */ + const uint64_t used_bytes = + page.mp_meta.mm_geo.next * (uint64_t)page.mp_meta.mm_psize; + if (used_bytes > *filesize) { + mdbx_notice("meta[%u] used-bytes (%" PRIu64 ") beyond filesize (%" PRIu64 + "), skip it", + meta_number, used_bytes, *filesize); + rc = MDBX_CORRUPTED; + continue; + } + /* LY: check mapsize limits */ const uint64_t mapsize_min = page.mp_meta.mm_geo.lower * (uint64_t)page.mp_meta.mm_psize; @@ -4164,8 +4446,6 @@ static int __cold mdbx_read_header(MDBX_env *env, MDBX_meta *meta) { if (mapsize_max > MAX_MAPSIZE || MAX_PAGENO < mdbx_roundup2((size_t)mapsize_max, env->me_os_psize) / (size_t)page.mp_meta.mm_psize) { - const uint64_t used_bytes = - page.mp_meta.mm_geo.next * (uint64_t)page.mp_meta.mm_psize; if (page.mp_meta.mm_geo.next - 1 > MAX_PAGENO || used_bytes > MAX_MAPSIZE) { mdbx_notice("meta[%u] has too large max-mapsize (%" PRIu64 "), skip it", @@ -4328,7 +4608,30 @@ static int mdbx_sync_locked(MDBX_env *env, unsigned flags, const size_t usedbytes = pgno_align2os_bytes(env, pending->mm_geo.next); if (env->me_sync_threshold && env->me_sync_pending >= env->me_sync_threshold) - flags &= MDBX_WRITEMAP | MDBX_SHRINK_ALLOWED; + flags &= MDBX_WRITEMAP | MDBX_SHRINK_ALLOWED; /* force steady */ + + /* LY: check conditions to shrink datafile */ + const pgno_t backlog_gap = + pending->mm_dbs[FREE_DBI].md_depth + mdbx_backlog_extragap(env); + pgno_t shrink = 0; + if ((flags & MDBX_SHRINK_ALLOWED) && pending->mm_geo.shrink && + pending->mm_geo.now - pending->mm_geo.next > + pending->mm_geo.shrink + backlog_gap) { + const pgno_t aligner = + pending->mm_geo.grow ? pending->mm_geo.grow : pending->mm_geo.shrink; + const pgno_t with_backlog_gap = pending->mm_geo.next + backlog_gap; + const pgno_t aligned = pgno_align2os_pgno( + env, with_backlog_gap + aligner - with_backlog_gap % aligner); + const pgno_t bottom = + (aligned > pending->mm_geo.lower) ? aligned : pending->mm_geo.lower; + if (pending->mm_geo.now > bottom) { + flags &= MDBX_WRITEMAP | MDBX_SHRINK_ALLOWED; /* force steady */ + shrink = pending->mm_geo.now - bottom; + pending->mm_geo.now = bottom; + if (mdbx_meta_txnid_stable(env, head) == pending->mm_txnid_a) + mdbx_meta_set_txnid(env, pending, pending->mm_txnid_a + 1); + } + } /* LY: step#1 - sync previously written/updated data-pages */ int rc = MDBX_RESULT_TRUE; @@ -4355,28 +4658,6 @@ static int mdbx_sync_locked(MDBX_env *env, unsigned flags, } } - /* LY: check conditions to shrink datafile */ - const pgno_t backlog_gap = - pending->mm_dbs[FREE_DBI].md_depth + mdbx_backlog_extragap(env); - pgno_t shrink = 0; - if ((flags & MDBX_SHRINK_ALLOWED) && pending->mm_geo.shrink && - pending->mm_geo.now - pending->mm_geo.next > - pending->mm_geo.shrink + backlog_gap) { - const pgno_t aligner = - pending->mm_geo.grow ? pending->mm_geo.grow : pending->mm_geo.shrink; - const pgno_t with_backlog_gap = pending->mm_geo.next + backlog_gap; - const pgno_t aligned = pgno_align2os_pgno( - env, with_backlog_gap + aligner - with_backlog_gap % aligner); - const pgno_t bottom = - (aligned > pending->mm_geo.lower) ? aligned : pending->mm_geo.lower; - if (pending->mm_geo.now > bottom) { - shrink = pending->mm_geo.now - bottom; - pending->mm_geo.now = bottom; - if (mdbx_meta_txnid_stable(env, head) == pending->mm_txnid_a) - mdbx_meta_set_txnid(env, pending, pending->mm_txnid_a + 1); - } - } - /* Steady or Weak */ if (env->me_sync_pending == 0) { pending->mm_datasync_sign = mdbx_meta_sign(pending); @@ -4974,9 +5255,10 @@ int __cold mdbx_env_get_maxreaders(MDBX_env *env, unsigned *readers) { /* Further setup required for opening an MDBX environment */ static int __cold mdbx_setup_dxb(MDBX_env *env, int lck_rc) { + uint64_t filesize_before_mmap; MDBX_meta meta; int rc = MDBX_RESULT_FALSE; - int err = mdbx_read_header(env, &meta); + int err = mdbx_read_header(env, &meta, &filesize_before_mmap); if (unlikely(err != MDBX_SUCCESS)) { if (lck_rc != /* lck exclusive */ MDBX_RESULT_TRUE || err != MDBX_ENODATA || (env->me_flags & MDBX_RDONLY) != 0) @@ -5002,12 +5284,12 @@ static int __cold mdbx_setup_dxb(MDBX_env *env, int lck_rc) { if (unlikely(err != MDBX_SUCCESS)) return err; - err = mdbx_ftruncate(env->me_fd, env->me_dbgeo.now); + err = mdbx_ftruncate(env->me_fd, filesize_before_mmap = env->me_dbgeo.now); if (unlikely(err != MDBX_SUCCESS)) return err; #ifndef NDEBUG /* just for checking */ - err = mdbx_read_header(env, &meta); + err = mdbx_read_header(env, &meta, &filesize_before_mmap); if (unlikely(err != MDBX_SUCCESS)) return err; #endif @@ -5095,11 +5377,6 @@ static int __cold mdbx_setup_dxb(MDBX_env *env, int lck_rc) { env->me_dbgeo.shrink = pgno2bytes(env, meta.mm_geo.shrink); } - uint64_t filesize_before_mmap; - err = mdbx_filesize(env->me_fd, &filesize_before_mmap); - if (unlikely(err != MDBX_SUCCESS)) - return err; - const size_t expected_bytes = mdbx_roundup2(pgno2bytes(env, meta.mm_geo.now), env->me_os_psize); mdbx_ensure(env, expected_bytes >= used_bytes); @@ -6155,9 +6432,11 @@ static int mdbx_page_search(MDBX_cursor *mc, MDBX_val *key, int flags) { MDBX_cursor mc2; if (unlikely(TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))) return MDBX_BAD_DBI; - mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL); + rc = mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; rc = mdbx_page_search(&mc2, &mc->mc_dbx->md_name, 0); - if (rc) + if (unlikely(rc != MDBX_SUCCESS)) return rc; { MDBX_val data; @@ -6341,7 +6620,9 @@ int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data) { if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) return MDBX_BAD_TXN; - mdbx_cursor_init(&mc, txn, dbi, &mx); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; return mdbx_cursor_set(&mc, key, data, MDBX_SET, &exact); } @@ -6396,7 +6677,9 @@ static int mdbx_cursor_sibling(MDBX_cursor *mc, int move_right) { return rc; } - mdbx_cursor_push(mc, mp); + rc = mdbx_cursor_push(mc, mp); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; if (!move_right) mc->mc_ki[mc->mc_top] = NUMKEYS(mp) - 1; @@ -6476,7 +6759,9 @@ skip: leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; } if (data) { if (unlikely((rc = mdbx_node_read(mc, leaf, data)) != MDBX_SUCCESS)) @@ -6564,7 +6849,9 @@ static int mdbx_cursor_prev(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; } if (data) { if (unlikely((rc = mdbx_node_read(mc, leaf, data)) != MDBX_SUCCESS)) @@ -6731,7 +7018,9 @@ set1: } if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; } if (likely(data)) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { @@ -6806,8 +7095,9 @@ static int mdbx_cursor_first(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data) { if (likely(data)) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - mdbx_cassert(mc, mc->mc_xcursor != nullptr); - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; rc = mdbx_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); if (unlikely(rc)) return rc; @@ -6850,8 +7140,9 @@ static int mdbx_cursor_last(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data) { if (likely(data)) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { - mdbx_cassert(mc, mc->mc_xcursor != nullptr); - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; rc = mdbx_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL); if (unlikely(rc)) return rc; @@ -6904,7 +7195,9 @@ int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, if (data) { if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { if (unlikely(!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))) { - mdbx_xcursor_init1(mc, leaf); + rc = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; rc = mdbx_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); if (unlikely(rc)) return rc; @@ -7040,13 +7333,16 @@ int mdbx_cursor_get(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, static int mdbx_cursor_touch(MDBX_cursor *mc) { int rc = MDBX_SUCCESS; - if (mc->mc_dbi >= CORE_DBS && !(*mc->mc_dbflag & (DB_DIRTY | DB_DUPDATA))) { + if (mc->mc_dbi >= CORE_DBS && + (*mc->mc_dbflag & (DB_DIRTY | DB_DUPDATA)) == 0) { /* Touch DB record of named DB */ MDBX_cursor mc2; MDBX_xcursor mcx; if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)) return MDBX_BAD_DBI; - mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx); + rc = mdbx_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; rc = mdbx_page_search(&mc2, &mc->mc_dbx->md_name, MDBX_PS_MODIFY); if (unlikely(rc)) return rc; @@ -7227,7 +7523,9 @@ int mdbx_cursor_put(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, return rc2; } assert(np->mp_flags & P_LEAF); - mdbx_cursor_push(mc, np); + rc2 = mdbx_cursor_push(mc, np); + if (unlikely(rc2 != MDBX_SUCCESS)) + return rc2; mc->mc_db->md_root = np->mp_pgno; mc->mc_db->md_depth++; *mc->mc_dbflag |= DB_DIRTY; @@ -7575,7 +7873,9 @@ new_sub: ? MDBX_CURRENT | MDBX_NOOVERWRITE | MDBX_NOSPILL : MDBX_CURRENT | MDBX_NOSPILL; } else { - mdbx_xcursor_init1(mc, leaf); + rc2 = mdbx_xcursor_init1(mc, leaf); + if (unlikely(rc2 != MDBX_SUCCESS)) + return rc2; xflags = (flags & MDBX_NODUPDATA) ? MDBX_NOOVERWRITE | MDBX_NOSPILL : MDBX_NOSPILL; } @@ -7604,7 +7904,9 @@ new_sub: continue; if (m2->mc_pg[i] == mp) { if (m2->mc_ki[i] == mc->mc_ki[i]) { - mdbx_xcursor_init2(m2, mx, dupdata_flag); + rc2 = mdbx_xcursor_init2(m2, mx, dupdata_flag); + if (unlikely(rc2 != MDBX_SUCCESS)) + return rc2; } else if (!insert_key && m2->mc_ki[i] < nkeys) { XCURSOR_REFRESH(m2, mp, m2->mc_ki[i]); } @@ -7734,6 +8036,8 @@ int mdbx_cursor_del(MDBX_cursor *mc, unsigned flags) { } } mc->mc_db->md_entries--; + mdbx_cassert(mc, mc->mc_db->md_entries > 0 && mc->mc_db->md_depth > 0 && + mc->mc_db->md_root != P_INVALID); return rc; } else { mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED; @@ -8122,8 +8426,10 @@ static void mdbx_node_shrink(MDBX_page *mp, unsigned indx) { * depend only on the parent DB. * * [in] mc The main cursor whose sorted-dups cursor is to be initialized. */ -static void mdbx_xcursor_init0(MDBX_cursor *mc) { +static int mdbx_xcursor_init0(MDBX_cursor *mc) { MDBX_xcursor *mx = mc->mc_xcursor; + if (unlikely(mx == nullptr)) + return MDBX_CORRUPTED; mx->mx_cursor.mc_xcursor = NULL; mx->mx_cursor.mc_txn = mc->mc_txn; @@ -8138,6 +8444,7 @@ static void mdbx_xcursor_init0(MDBX_cursor *mc) { mx->mx_dbx.md_name.iov_base = NULL; mx->mx_dbx.md_cmp = mc->mc_dbx->md_dcmp; mx->mx_dbx.md_dcmp = NULL; + return MDBX_SUCCESS; } /* Final setup of a sorted-dups cursor. @@ -8145,8 +8452,10 @@ static void mdbx_xcursor_init0(MDBX_cursor *mc) { * [in] mc The main cursor whose sorted-dups cursor is to be initialized. * [in] node The data containing the MDBX_db record for the sorted-dup database. */ -static void mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) { +static int mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) { MDBX_xcursor *mx = mc->mc_xcursor; + if (unlikely(mx == nullptr)) + return MDBX_CORRUPTED; mdbx_cassert(mc, mc->mc_txn->mt_txnid >= mc->mc_txn->mt_env->me_oldest[0]); if (node->mn_flags & F_SUBDATA) { @@ -8185,6 +8494,8 @@ static void mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) { sizeof(size_t)) mx->mx_dbx.md_cmp = mdbx_cmp_clong; #endif */ + + return MDBX_SUCCESS; } /* Fixup a sorted-dups cursor due to underlying update. @@ -8194,9 +8505,11 @@ static void mdbx_xcursor_init1(MDBX_cursor *mc, MDBX_node *node) { * [in] mc The main cursor whose sorted-dups cursor is to be fixed up. * [in] src_mx The xcursor of an up-to-date cursor. * [in] new_dupdata True if converting from a non-F_DUPDATA item. */ -static void mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx, - int new_dupdata) { +static int mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx, + int new_dupdata) { MDBX_xcursor *mx = mc->mc_xcursor; + if (unlikely(mx == nullptr)) + return MDBX_CORRUPTED; mdbx_cassert(mc, mc->mc_txn->mt_txnid >= mc->mc_txn->mt_env->me_oldest[0]); if (new_dupdata) { @@ -8207,17 +8520,18 @@ static void mdbx_xcursor_init2(MDBX_cursor *mc, MDBX_xcursor *src_mx, mx->mx_dbflag = DB_VALID | DB_USRVALID | DB_DUPDATA; mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp; } else if (!(mx->mx_cursor.mc_flags & C_INITIALIZED)) { - return; + return MDBX_SUCCESS; } mx->mx_db = src_mx->mx_db; mx->mx_cursor.mc_pg[0] = src_mx->mx_cursor.mc_pg[0]; mdbx_debug("Sub-db -%u root page %" PRIaPGNO "", mx->mx_cursor.mc_dbi, mx->mx_db.md_root); + return MDBX_SUCCESS; } /* Initialize a cursor for a given transaction and database. */ -static void mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi, - MDBX_xcursor *mx) { +static int mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi, + MDBX_xcursor *mx) { mc->mc_signature = MDBX_MC_SIGNATURE; mc->mc_next = NULL; mc->mc_backup = NULL; @@ -8232,16 +8546,23 @@ static void mdbx_cursor_init(MDBX_cursor *mc, MDBX_txn *txn, MDBX_dbi dbi, mc->mc_flags = 0; mc->mc_ki[0] = 0; mc->mc_xcursor = NULL; + if (txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) { mdbx_tassert(txn, mx != NULL); mx->mx_cursor.mc_signature = MDBX_MC_SIGNATURE; mc->mc_xcursor = mx; - mdbx_xcursor_init0(mc); + int rc = mdbx_xcursor_init0(mc); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; } + mdbx_cassert(mc, mc->mc_txn->mt_txnid >= mc->mc_txn->mt_env->me_oldest[0]); + int rc = MDBX_SUCCESS; if (unlikely(*mc->mc_dbflag & DB_STALE)) { - mdbx_page_search(mc, NULL, MDBX_PS_ROOTONLY); + rc = mdbx_page_search(mc, NULL, MDBX_PS_ROOTONLY); + rc = (rc != MDBX_NOTFOUND) ? rc : MDBX_SUCCESS; } + return rc; } int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { @@ -8270,7 +8591,11 @@ int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { size += sizeof(MDBX_xcursor); if (likely((mc = malloc(size)) != NULL)) { - mdbx_cursor_init(mc, txn, dbi, (MDBX_xcursor *)(mc + 1)); + int rc = mdbx_cursor_init(mc, txn, dbi, (MDBX_xcursor *)(mc + 1)); + if (unlikely(rc != MDBX_SUCCESS)) { + free(mc); + return rc; + } if (txn->mt_cursors) { mc->mc_next = txn->mt_cursors[dbi]; txn->mt_cursors[dbi] = mc; @@ -8281,7 +8606,6 @@ int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **ret) { } *ret = mc; - return MDBX_SUCCESS; } @@ -8320,8 +8644,7 @@ int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *mc) { if (unlikely(txn->mt_flags & MDBX_TXN_BLOCKED)) return MDBX_BAD_TXN; - mdbx_cursor_init(mc, txn, mc->mc_dbi, mc->mc_xcursor); - return MDBX_SUCCESS; + return mdbx_cursor_init(mc, txn, mc->mc_dbi, mc->mc_xcursor); } /* Return the count of duplicate data items for the current key */ @@ -8821,6 +9144,7 @@ static int mdbx_page_merge(MDBX_cursor *csrc, MDBX_cursor *cdst) { rc = mdbx_page_loose(csrc, psrc); if (unlikely(rc)) return rc; + if (IS_LEAF(psrc)) csrc->mc_db->md_leaf_pages--; else @@ -8858,6 +9182,10 @@ static int mdbx_page_merge(MDBX_cursor *csrc, MDBX_cursor *cdst) { uint16_t depth = cdst->mc_db->md_depth; mdbx_cursor_pop(cdst); rc = mdbx_rebalance(cdst); + if (unlikely(rc)) + return rc; + mdbx_cassert(cdst, cdst->mc_db->md_entries > 0); + /* Did the tree height change? */ if (depth != cdst->mc_db->md_depth) snum += cdst->mc_db->md_depth - depth; @@ -8865,7 +9193,7 @@ static int mdbx_page_merge(MDBX_cursor *csrc, MDBX_cursor *cdst) { cdst->mc_snum = (uint16_t)snum; cdst->mc_top = (uint16_t)(snum - 1); } - return rc; + return MDBX_SUCCESS; } /* Copy the contents of a cursor. @@ -8916,12 +9244,14 @@ static int mdbx_rebalance(MDBX_cursor *mc) { NUMKEYS(mc->mc_pg[mc->mc_top]) >= minkeys) { mdbx_debug("no need to rebalance page %" PRIaPGNO ", above fill threshold", mc->mc_pg[mc->mc_top]->mp_pgno); + mdbx_cassert(mc, mc->mc_db->md_entries > 0); return MDBX_SUCCESS; } if (mc->mc_snum < 2) { - MDBX_page *mp = mc->mc_pg[0]; - unsigned nkeys = NUMKEYS(mp); + MDBX_page *const mp = mc->mc_pg[0]; + const unsigned nkeys = NUMKEYS(mp); + mdbx_cassert(mc, (mc->mc_db->md_entries == 0) == (nkeys == 0)); if (IS_SUBP(mp)) { mdbx_debug("Can't rebalance a subpage, ignoring"); return MDBX_SUCCESS; @@ -8951,7 +9281,7 @@ static int mdbx_rebalance(MDBX_cursor *mc) { mc->mc_snum = 0; mc->mc_top = 0; mc->mc_flags &= ~C_INITIALIZED; - } else if (IS_BRANCH(mp) && NUMKEYS(mp) == 1) { + } else if (IS_BRANCH(mp) && nkeys == 1) { int i; mdbx_debug("collapsing root page!"); rc = mdbx_pnl_append(&mc->mc_txn->mt_befree_pages, mp->mp_pgno); @@ -9112,12 +9442,17 @@ static int mdbx_cursor_del0(MDBX_cursor *mc) { * Other cursors adjustments were already done * by mdbx_rebalance and aren't needed here. */ if (!mc->mc_snum) { + mdbx_cassert(mc, mc->mc_db->md_entries == 0 && mc->mc_db->md_depth == 0 && + mc->mc_db->md_root == P_INVALID); mc->mc_flags |= C_DEL | C_EOF; return rc; } mp = mc->mc_pg[mc->mc_top]; nkeys = NUMKEYS(mp); + mdbx_cassert(mc, (mc->mc_db->md_entries > 0 && nkeys > 0) || + ((mc->mc_flags & C_SUB) && + mc->mc_db->md_entries == 0 && nkeys == 0)); /* Adjust other cursors pointing to mp */ for (m2 = mc->mc_txn->mt_cursors[dbi]; !rc && m2; m2 = m2->mc_next) { @@ -9135,7 +9470,8 @@ static int mdbx_cursor_del0(MDBX_cursor *mc) { m3->mc_flags |= C_EOF; rc = MDBX_SUCCESS; continue; - } + } else if (unlikely(rc != MDBX_SUCCESS)) + break; } if (mc->mc_db->md_flags & MDBX_DUPSORT) { MDBX_node *node = @@ -9149,8 +9485,9 @@ static int mdbx_cursor_del0(MDBX_cursor *mc) { if (!(node->mn_flags & F_SUBDATA)) m3->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(node); } else { - mdbx_xcursor_init1(m3, node); - m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL; + rc = mdbx_xcursor_init1(m3, node); + if (likely(rc == MDBX_SUCCESS)) + m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL; } } } @@ -9196,7 +9533,9 @@ static int mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, mdbx_debug("====> delete db %u key [%s], data [%s]", dbi, DKEY(key), DVAL(data)); - mdbx_cursor_init(&mc, txn, dbi, &mx); + rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; if (data) { op = MDBX_GET_BOTH; @@ -9685,11 +10024,12 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED))) return (txn->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EACCESS : MDBX_BAD_TXN; - mdbx_cursor_init(&mc, txn, dbi, &mx); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; mc.mc_next = txn->mt_cursors[dbi]; txn->mt_cursors[dbi] = &mc; - int rc = MDBX_SUCCESS; /* LY: support for update (explicit overwrite) */ if (flags & MDBX_CURRENT) { rc = mdbx_cursor_get(&mc, key, NULL, MDBX_SET); @@ -10014,7 +10354,9 @@ static int __cold mdbx_env_compact(MDBX_env *env, mdbx_filehandle_t fd) { MDBX_cursor mc; MDBX_val key, data; - mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + rc = mdbx_cursor_init(&mc, txn, FREE_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; while ((rc = mdbx_cursor_get(&mc, &key, &data, MDBX_NEXT)) == 0) freecount += *(pgno_t *)data.iov_base; if (unlikely(rc != MDBX_NOTFOUND)) @@ -10435,8 +10777,10 @@ int mdbx_dbi_open_ex(MDBX_txn *txn, const char *table_name, unsigned user_flags, key.iov_len = len; key.iov_base = (void *)table_name; MDBX_cursor mc; - mdbx_cursor_init(&mc, txn, MAIN_DBI, NULL); - int rc = mdbx_cursor_set(&mc, &key, &data, MDBX_SET, &exact); + int rc = mdbx_cursor_init(&mc, txn, MAIN_DBI, NULL); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + rc = mdbx_cursor_set(&mc, &key, &data, MDBX_SET, &exact); if (unlikely(rc != MDBX_SUCCESS)) { if (rc != MDBX_NOTFOUND || !(user_flags & MDBX_CREATE)) return rc; @@ -10572,7 +10916,9 @@ int __cold mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *arg, MDBX_cursor mc; MDBX_xcursor mx; /* Stale, must read the DB's root. cursor_init does it for us. */ - mdbx_cursor_init(&mc, txn, dbi, &mx); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; } return mdbx_stat0(txn->mt_env, &txn->mt_dbs[dbi], arg); } @@ -10676,7 +11022,9 @@ static int mdbx_drop0(MDBX_cursor *mc, int subs) { if (!mc->mc_db->md_overflow_pages && !subs) break; } else if (subs && (ni->mn_flags & F_SUBDATA)) { - mdbx_xcursor_init1(mc, ni); + rc = mdbx_xcursor_init1(mc, ni); + if (unlikely(rc != MDBX_SUCCESS)) + goto done; rc = mdbx_drop0(&mc->mc_xcursor->mx_cursor, 0); if (unlikely(rc)) goto done; @@ -11512,11 +11860,12 @@ int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *new_data, MDBX_cursor mc; MDBX_xcursor mx; - mdbx_cursor_init(&mc, txn, dbi, &mx); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; mc.mc_next = txn->mt_cursors[dbi]; txn->mt_cursors[dbi] = &mc; - int rc; MDBX_val present_key = *key; if (F_ISSET(flags, MDBX_CURRENT | MDBX_NOOVERWRITE)) { /* в old_data значение для выбора конкретного дубликата */ @@ -11639,10 +11988,12 @@ int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, MDBX_cursor mc; MDBX_xcursor mx; - mdbx_cursor_init(&mc, txn, dbi, &mx); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; int exact = 0; - int rc = mdbx_cursor_set(&mc, key, data, MDBX_SET_KEY, &exact); + rc = mdbx_cursor_set(&mc, key, data, MDBX_SET_KEY, &exact); if (unlikely(rc != MDBX_SUCCESS)) { if (rc == MDBX_NOTFOUND && values_count) *values_count = 0; @@ -11886,8 +12237,10 @@ int mdbx_set_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, MDBX_cursor mc; MDBX_xcursor mx; MDBX_val old_data; - mdbx_cursor_init(&mc, txn, dbi, &mx); - int rc = mdbx_cursor_set(&mc, key, &old_data, MDBX_SET, NULL); + int rc = mdbx_cursor_init(&mc, txn, dbi, &mx); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + rc = mdbx_cursor_set(&mc, key, &old_data, MDBX_SET, NULL); if (unlikely(rc != MDBX_SUCCESS)) { if (rc == MDBX_NOTFOUND && data) { mc.mc_next = txn->mt_cursors[dbi]; diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/osal.c b/plugins/Dbx_mdbx/src/libmdbx/src/osal.c index bcb9a767f1..d7ec30e78a 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/osal.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/osal.c @@ -19,8 +19,6 @@ #if defined(_WIN32) || defined(_WIN64) #include <winternl.h> -typedef BOOL (WINAPI *pfnGetFileInformationByHandleEx)(HANDLE, FILE_INFO_BY_HANDLE_CLASS, LPVOID, DWORD); - static int waitstatus2errcode(DWORD result) { switch (result) { case WAIT_OBJECT_0: @@ -702,39 +700,6 @@ int mdbx_ftruncate(mdbx_filehandle_t fd, uint64_t length) { /*----------------------------------------------------------------------------*/ -int mdbx_thread_key_create(mdbx_thread_key_t *key) { -#if defined(_WIN32) || defined(_WIN64) - *key = TlsAlloc(); - return (*key != TLS_OUT_OF_INDEXES) ? MDBX_SUCCESS : GetLastError(); -#else - return pthread_key_create(key, mdbx_rthc_dtor); -#endif -} - -void mdbx_thread_key_delete(mdbx_thread_key_t key) { -#if defined(_WIN32) || defined(_WIN64) - mdbx_ensure(NULL, TlsFree(key)); -#else - mdbx_ensure(NULL, pthread_key_delete(key) == 0); -#endif -} - -void *mdbx_thread_rthc_get(mdbx_thread_key_t key) { -#if defined(_WIN32) || defined(_WIN64) - return TlsGetValue(key); -#else - return pthread_getspecific(key); -#endif -} - -void mdbx_thread_rthc_set(mdbx_thread_key_t key, const void *value) { -#if defined(_WIN32) || defined(_WIN64) - mdbx_ensure(NULL, TlsSetValue(key, (void *)value)); -#else - mdbx_ensure(NULL, pthread_setspecific(key, value) == 0); -#endif -} - int mdbx_thread_create(mdbx_thread_t *thread, THREAD_RESULT(THREAD_CALL *start_routine)(void *), void *arg) { diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/osal.h b/plugins/Dbx_mdbx/src/libmdbx/src/osal.h index e01311cb01..5b6a1e9eff 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/osal.h +++ b/plugins/Dbx_mdbx/src/libmdbx/src/osal.h @@ -21,13 +21,14 @@ #ifdef _MSC_VER #pragma warning(push, 1) -#pragma warning(disable : 4548) /* expression before comma has no effect; \ +#pragma warning(disable : 4548) /* expression before comma has no effect; \ \ \ expected expression with side - effect */ -#pragma warning(disable : 4530) /* C++ exception handler used, but unwind \ +#pragma warning(disable : 4530) /* C++ exception handler used, but unwind \ \ \ * semantics are not enabled. Specify /EHsc */ -#pragma warning(disable : 4577) /* 'noexcept' used with no exception handling \ - * mode specified; termination on exception is \ - * not guaranteed. Specify /EHsc */ +#pragma warning( \ + disable : 4577) /* 'noexcept' used with no exception handling \ \ \ + * mode specified; termination on exception is \ \ not \ + * guaranteed. Specify /EHsc */ #if !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif @@ -322,8 +323,8 @@ static __inline void mdbx_memory_barrier(void) { #endif } -/*----------------------------------------------------------------------------*/ -/* Cache coherence and invalidation */ + /*----------------------------------------------------------------------------*/ + /* Cache coherence and invalidation */ #ifndef MDBX_CACHE_IS_COHERENT #if defined(__ia32__) || defined(__e2k__) || defined(__hppa) || \ @@ -374,8 +375,8 @@ static __inline void mdbx_invalidate_cache(void *addr, size_t nbytes) { #endif } -/*----------------------------------------------------------------------------*/ -/* libc compatibility stuff */ + /*----------------------------------------------------------------------------*/ + /* libc compatibility stuff */ #ifndef mdbx_assert_fail void mdbx_assert_fail(const MDBX_env *env, const char *msg, const char *func, @@ -469,10 +470,6 @@ int mdbx_thread_create(mdbx_thread_t *thread, THREAD_RESULT(THREAD_CALL *start_routine)(void *), void *arg); int mdbx_thread_join(mdbx_thread_t thread); -int mdbx_thread_key_create(mdbx_thread_key_t *key); -void mdbx_thread_key_delete(mdbx_thread_key_t key); -void *mdbx_thread_rthc_get(mdbx_thread_key_t key); -void mdbx_thread_rthc_set(mdbx_thread_key_t key, const void *value); int mdbx_filesync(mdbx_filehandle_t fd, bool fullsync); int mdbx_filesize_sync(mdbx_filehandle_t fd); @@ -564,10 +561,14 @@ LIBMDBX_API void mdbx_txn_unlock(MDBX_env *env); int mdbx_rpid_set(MDBX_env *env); int mdbx_rpid_clear(MDBX_env *env); -typedef struct MDBX_shlock -{ - __declspec(align(64)) long volatile readerCount; - __declspec(align(64)) long volatile writerCount; +typedef struct MDBX_shlock { + union { + struct { + __declspec(align(64)) long volatile readerCount; + __declspec(align(64)) long volatile writerCount; + }; + RTL_SRWLOCK srwLock; + }; } MDBX_shlock; void mdbx_shlock_init(MDBX_shlock *lck); @@ -577,11 +578,11 @@ void mdbx_shlock_acquireExclusive(MDBX_shlock *lck); void mdbx_shlock_releaseExclusive(MDBX_shlock *lck); /* Checks reader by pid. - * - * Returns: - * MDBX_RESULT_TRUE, if pid is live (unable to acquire lock) - * MDBX_RESULT_FALSE, if pid is dead (lock acquired) - * or otherwise the errcode. */ +* +* Returns: +* MDBX_RESULT_TRUE, if pid is live (unable to acquire lock) +* MDBX_RESULT_FALSE, if pid is dead (lock acquired) +* or otherwise the errcode. */ int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid); /*----------------------------------------------------------------------------*/ @@ -596,11 +597,11 @@ int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid); /* LY: nothing required */ #elif defined(_MSC_VER) #pragma warning(disable : 4163) /* 'xyz': not available as an intrinsic */ -#pragma warning(disable : 4133) /* 'function': incompatible types - from \ +#pragma warning(disable : 4133) /* 'function': incompatible types - from \ \ \ 'size_t' to 'LONGLONG' */ -#pragma warning(disable : 4244) /* 'return': conversion from 'LONGLONG' to \ +#pragma warning(disable : 4244) /* 'return': conversion from 'LONGLONG' to \ \ \ 'std::size_t', possible loss of data */ -#pragma warning(disable : 4267) /* 'function': conversion from 'size_t' to \ +#pragma warning(disable : 4267) /* 'function': conversion from 'size_t' to \ \ \ 'long', possible loss of data */ #pragma intrinsic(_InterlockedExchangeAdd, _InterlockedCompareExchange) #pragma intrinsic(_InterlockedExchangeAdd64, _InterlockedCompareExchange64) @@ -610,80 +611,80 @@ int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid); #error FIXME atomic-ops #endif -static __inline uint32_t mdbx_atomic_add32(volatile uint32_t *p, uint32_t v) { + static __inline uint32_t mdbx_atomic_add32(volatile uint32_t *p, uint32_t v) { #if !defined(__cplusplus) && defined(ATOMIC_VAR_INIT) - assert(atomic_is_lock_free(p)); - return atomic_fetch_add((_Atomic uint32_t *)p, v); + assert(atomic_is_lock_free(p)); + return atomic_fetch_add((_Atomic uint32_t *)p, v); #elif defined(__GNUC__) || defined(__clang__) - return __sync_fetch_and_add(p, v); + return __sync_fetch_and_add(p, v); #else #ifdef _MSC_VER - return _InterlockedExchangeAdd(p, v); + return _InterlockedExchangeAdd(p, v); #endif #ifdef __APPLE__ - return OSAtomicAdd32(v, (volatile int32_t *)p); + return OSAtomicAdd32(v, (volatile int32_t *)p); #endif #endif -} + } -static __inline uint64_t mdbx_atomic_add64(volatile uint64_t *p, uint64_t v) { + static __inline uint64_t mdbx_atomic_add64(volatile uint64_t *p, uint64_t v) { #if !defined(__cplusplus) && defined(ATOMIC_VAR_INIT) - assert(atomic_is_lock_free(p)); - return atomic_fetch_add((_Atomic uint64_t *)p, v); + assert(atomic_is_lock_free(p)); + return atomic_fetch_add((_Atomic uint64_t *)p, v); #elif defined(__GNUC__) || defined(__clang__) - return __sync_fetch_and_add(p, v); + return __sync_fetch_and_add(p, v); #else #ifdef _MSC_VER #ifdef _WIN64 - return _InterlockedExchangeAdd64((volatile int64_t *)p, v); + return _InterlockedExchangeAdd64((volatile int64_t *)p, v); #else - return InterlockedExchangeAdd64((volatile int64_t *)p, v); + return InterlockedExchangeAdd64((volatile int64_t *)p, v); #endif #endif /* _MSC_VER */ #ifdef __APPLE__ - return OSAtomicAdd64(v, (volatile int64_t *)p); + return OSAtomicAdd64(v, (volatile int64_t *)p); #endif #endif -} + } #define mdbx_atomic_sub32(p, v) mdbx_atomic_add32(p, 0 - (v)) #define mdbx_atomic_sub64(p, v) mdbx_atomic_add64(p, 0 - (v)) -static __inline bool mdbx_atomic_compare_and_swap32(volatile uint32_t *p, - uint32_t c, uint32_t v) { + static __inline bool mdbx_atomic_compare_and_swap32(volatile uint32_t *p, + uint32_t c, uint32_t v) { #if !defined(__cplusplus) && defined(ATOMIC_VAR_INIT) - assert(atomic_is_lock_free(p)); - return atomic_compare_exchange_strong((_Atomic uint32_t *)p, &c, v); + assert(atomic_is_lock_free(p)); + return atomic_compare_exchange_strong((_Atomic uint32_t *)p, &c, v); #elif defined(__GNUC__) || defined(__clang__) - return __sync_bool_compare_and_swap(p, c, v); + return __sync_bool_compare_and_swap(p, c, v); #else #ifdef _MSC_VER - return c == _InterlockedCompareExchange(p, v, c); + return c == _InterlockedCompareExchange(p, v, c); #endif #ifdef __APPLE__ - return c == OSAtomicCompareAndSwap32Barrier(c, v, (volatile int32_t *)p); + return c == OSAtomicCompareAndSwap32Barrier(c, v, (volatile int32_t *)p); #endif #endif -} + } -static __inline bool mdbx_atomic_compare_and_swap64(volatile uint64_t *p, - uint64_t c, uint64_t v) { + static __inline bool mdbx_atomic_compare_and_swap64(volatile uint64_t *p, + uint64_t c, uint64_t v) { #if !defined(__cplusplus) && defined(ATOMIC_VAR_INIT) - assert(atomic_is_lock_free(p)); - return atomic_compare_exchange_strong((_Atomic uint64_t *)p, &c, v); + assert(atomic_is_lock_free(p)); + return atomic_compare_exchange_strong((_Atomic uint64_t *)p, &c, v); #elif defined(__GNUC__) || defined(__clang__) - return __sync_bool_compare_and_swap(p, c, v); + return __sync_bool_compare_and_swap(p, c, v); #else #ifdef _MSC_VER - return c == _InterlockedCompareExchange64((volatile int64_t *)p, v, c); + return c == _InterlockedCompareExchange64((volatile int64_t *)p, v, c); #endif #ifdef __APPLE__ - return c == OSAtomicCompareAndSwap64Barrier(c, v, (volatile uint64_t *)p); + return c == OSAtomicCompareAndSwap64Barrier(c, v, (volatile uint64_t *)p); #endif #endif -} + } -/*----------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------*/ #if defined(_MSC_VER) && _MSC_VER >= 1900 && _MSC_VER < 1920 /* LY: MSVC 2015/2017 has buggy/inconsistent PRIuPTR/PRIxPTR macros diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c index 681ee63d46..0fd23ae69f 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/tools/mdbx_chk.c @@ -837,8 +837,8 @@ int main(int argc, char *argv[]) { #endif /* !WINDOWS */ envname = argv[optind]; - print("Running mdbx_chk for 'read-%s' in %s mode...\n", envname, - (envflags & MDBX_RDONLY) ? "read" : "write"); + print("Running mdbx_chk for %s in 'read-%s' mode...\n", envname, + (envflags & MDBX_RDONLY) ? "only" : "write"); fflush(NULL); rc = mdbx_env_create(&env); diff --git a/plugins/Dbx_mdbx/src/libmdbx/src/version.c b/plugins/Dbx_mdbx/src/libmdbx/src/version.c index c325be5878..3272ff61c8 100644 --- a/plugins/Dbx_mdbx/src/libmdbx/src/version.c +++ b/plugins/Dbx_mdbx/src/libmdbx/src/version.c @@ -18,7 +18,7 @@ #error "API version mismatch!" #endif -#define MDBX_VERSION_RELEASE 2 +#define MDBX_VERSION_RELEASE 4 #define MDBX_VERSION_REVISION 1 /*LIBMDBX_EXPORTS*/ const mdbx_version_info mdbx_version = { diff --git a/plugins/Dbx_mdbx/src/version.h b/plugins/Dbx_mdbx/src/version.h index ba74fc9109..81bf0f7155 100644 --- a/plugins/Dbx_mdbx/src/version.h +++ b/plugins/Dbx_mdbx/src/version.h @@ -1,7 +1,7 @@ #define __MAJOR_VERSION 0
#define __MINOR_VERSION 95
-#define __RELEASE_NUM 8
-#define __BUILD_NUM 7
+#define __RELEASE_NUM 9
+#define __BUILD_NUM 1
#include <stdver.h>
|