Compare commits

...

10 Commits

Author SHA1 Message Date
Leo Yuriev
f4a9018690 mdbx: bump internal version info to v0.1.3
Change-Id: I7db311c812d531f6628101715f6658005db3ea24
2018-04-03 20:37:07 +03:00
Leo Yuriev
f8eb423d36 mdbx-make: remote '-O3' for Elbrus (done by __hot attributes).
Change-Id: I6a3afeaf1ddf6d231941aae023dff89046bc4349
2018-04-03 20:06:45 +03:00
Leo Yuriev
797ae9d1db mdbx: minor fix debug-output for mdbx_thread_key_create().
Change-Id: I718130cf2385b6221187cbcdeedd9d48d05289bb
2018-04-03 20:04:10 +03:00
Leo Yuriev
f08c7ccac0 mdbx-make: rename mdbx_test and link with dso-library.
Change-Id: I39a04f82bc31c0865a8d02379a596048518205cd
2018-04-03 19:28:12 +03:00
Leo Yuriev
864be54f01 mdbx: fix/rework rthc counting.
Change-Id: I97fd5bd6dc1a46658138d82db10e937c9595d81f
2018-04-03 19:28:12 +03:00
Leo Yuriev
571b50622e mdbx: restore workaround for glibc's bug #21031.
Workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=21031

Change-Id: I6cf9e037cc2fc298096b78ec96773f19478ed5c0
2018-04-03 17:44:18 +03:00
Leo Yuriev
36a86bbc6e mdbx: fix cursor tracking inside mdbx_rebalance(). 2018-03-26 20:20:14 +03:00
Leo Yuriev
ae708aab13 mdbx: update README.md 2018-03-26 14:17:35 +03:00
Leo Yuriev
02fc5fe158 mdbx: update internal version info. 2018-03-23 17:23:28 +03:00
Leo Yuriev
45f159ddd3 mdbx: mdbx_cursor_put(MDBX_APPEND+MDBX_NOOVERWRITE) should return MDBX_KEYEXIST instead of MDBX_EKEYMISMATCH.
When MDBX_APPEND is used the ordering of keys is important.
Therefore MDBX_EKEYMISMATCH should me returned when given key <= last.

On the other hand, if MDBX_NOOVERWRITE is also used
then the MDBX_KEYEXIST should be returned, but not MDBX_EKEYMISMATCH.
2018-03-23 17:21:27 +03:00
11 changed files with 330 additions and 138 deletions

View File

@@ -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 $@
###############################################################################

View File

@@ -12,13 +12,24 @@ and [by Yandex](https://translate.yandex.ru/translate?url=https%3A%2F%2Fgithub.c
### Project Status
**Now MDBX is under _active development_** and until 2018Q2 is expected a big
change both of API and database format. Unfortunately those update will lead to
loss of compatibility with previous versions.
The aim of this revolution in providing a clearer robust API and adding new
features, including the database properties.
**Сейчас MDBX _активно перерабатывается_** и к середине 2018
ожидается большое измените как API, так и формата базы данных.
К сожалению, обновление приведет к потере совместимости с
предыдущими версиями.
Цель этой революции в обеспечении более четкого надежного
API и добавлении новых функции, а также в наделении базы данных
новыми свойствами.
В настоящее время MDBX предназначена для Linux, а также
поддерживает Windows (начиная с Windows Server 2008) в качестве
дополнительно платформы. Поддержка других ОС может быть
обеспечена на коммерческой основе. Однако такие
усовершенствования (т. е. pull-requests) могут быть приняты в
мейнстрим только в том случае, если будет доступен
соответствующий публичный и бесплатный сервис непрерывной
интеграции (aka Countinious Integration).
## Содержание
@@ -43,7 +54,7 @@ features, including the database properties.
_libmdbx_ - это встраиваемый key-value движок хранения со специфическим
набором свойств и возможностей, ориентированный на создание уникальных
легковесных решений с предельной производительностью.
легковесных решений с предельной производительностью под Linux и Windows.
_libmdbx_ позволяет множеству процессов совместно читать и обновлять
несколько key-value таблиц с соблюдением [ACID](https://ru.wikipedia.org/wiki/ACID),

View File

@@ -9,9 +9,17 @@ libmdbx
### Project Status
**MDBX is under _active development_**, database format and API aren't stable
at least until 2018Q2. New version won't be backwards compatible.
Main focus of the rework is to provide clear and robust API and new features.
**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
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
Integration service will be available.
## Contents
@@ -34,7 +42,7 @@ Main focus of the rework is to provide clear and robust API and new features.
## Overview
_libmdbx_ is an embedded lightweight key-value database engine oriented for performance.
_libmdbx_ is an embedded lightweight key-value database engine oriented for performance under Linux and Windows.
_libmdbx_ allows multiple processes to read and update several key-value tables concurrently,
while being [ACID](https://en.wikipedia.org/wiki/ACID)-compliant, with minimal overhead and operation cost of Olog(N).

View File

@@ -1,4 +1,4 @@
version: 0.1.2.{build}
version: 0.1.3.{build}
environment:
matrix:

View File

@@ -948,13 +948,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; }

View File

@@ -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);
static __cold __attribute__((constructor)) void mdbx_global_constructor(void) {
mdbx_rthc_global_init();
}
void mdbx_rthc_unlock(void) {
mdbx_ensure(NULL, pthread_mutex_unlock(&mdbx_rthc_mutex) == 0);
}
void __attribute__((destructor)) mdbx_global_destructor(void) {
mdbx_rthc_cleanup();
static __cold __attribute__((destructor)) void mdbx_global_destructor(void) {
mdbx_rthc_global_dtor();
}
/*----------------------------------------------------------------------------*/

View File

@@ -27,31 +27,24 @@
/*----------------------------------------------------------------------------*/
/* rthc */
static CRITICAL_SECTION rthc_critical_section;
static void NTAPI tls_callback(PVOID module, DWORD reason, PVOID reserved) {
(void)module;
(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)

View File

@@ -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;
mdbx_rthc_lock();
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;
}
}
mdbx_rthc_unlock();
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
}
__cold void mdbx_rthc_cleanup(void) {
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) {
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_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_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) {
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();
}
@@ -917,6 +1147,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);
}
@@ -7195,7 +7426,8 @@ int mdbx_cursor_put(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data,
} else {
rc = mdbx_cursor_set(mc, key, &d2, MDBX_SET, &exact);
}
if ((flags & MDBX_NOOVERWRITE) && rc == 0) {
if ((flags & MDBX_NOOVERWRITE) &&
(rc == MDBX_SUCCESS || rc == MDBX_EKEYMISMATCH)) {
mdbx_debug("duplicate key [%s]", DKEY(key));
*data = d2;
return MDBX_KEYEXIST;
@@ -8934,27 +9166,22 @@ static int mdbx_rebalance(MDBX_cursor *mc) {
if (unlikely(rc))
return rc;
/* Adjust cursors pointing to mp */
const MDBX_dbi dbi = mc->mc_dbi;
for (MDBX_cursor *m2 = mc->mc_txn->mt_cursors[dbi]; m2;
m2 = m2->mc_next) {
MDBX_cursor *m3 =
(mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2;
if (!(m3->mc_flags & C_INITIALIZED) || (m3->mc_snum < mc->mc_snum))
continue;
if (m3->mc_pg[0] == mp) {
m3->mc_snum = 0;
m3->mc_top = 0;
m3->mc_flags &= ~C_INITIALIZED;
}
}
mc->mc_snum = 0;
mc->mc_top = 0;
mc->mc_flags &= ~C_INITIALIZED;
{
MDBX_cursor *m2, *m3;
MDBX_dbi dbi = mc->mc_dbi;
for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2 = m2->mc_next) {
if (mc->mc_flags & C_SUB)
m3 = &m2->mc_xcursor->mx_cursor;
else
m3 = m2;
if (!(m3->mc_flags & C_INITIALIZED) || (m3->mc_snum < mc->mc_snum))
continue;
if (m3->mc_pg[0] == mp) {
m3->mc_snum = 0;
m3->mc_top = 0;
m3->mc_flags &= ~C_INITIALIZED;
}
}
}
} else if (IS_BRANCH(mp) && NUMKEYS(mp) == 1) {
int i;
mdbx_debug("collapsing root page!");

View File

@@ -700,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) {

View File

@@ -469,10 +469,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);

View File

@@ -18,8 +18,8 @@
#error "API version mismatch!"
#endif
#define MDBX_VERSION_RELEASE 0
#define MDBX_VERSION_REVISION 0
#define MDBX_VERSION_RELEASE 3
#define MDBX_VERSION_REVISION 1
/*LIBMDBX_EXPORTS*/ const mdbx_version_info mdbx_version = {
MDBX_VERSION_MAJOR,