mirror of
https://github.com/isar/libmdbx.git
synced 2025-12-15 16:42:22 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4a9018690 | ||
|
|
f8eb423d36 | ||
|
|
797ae9d1db | ||
|
|
f08c7ccac0 | ||
|
|
864be54f01 | ||
|
|
571b50622e | ||
|
|
36a86bbc6e | ||
|
|
ae708aab13 | ||
|
|
02fc5fe158 | ||
|
|
45f159ddd3 |
20
Makefile
20
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 $@
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
||||
23
README-RU.md
23
README-RU.md
@@ -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),
|
||||
|
||||
16
README.md
16
README.md
@@ -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).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 0.1.2.{build}
|
||||
version: 0.1.3.{build}
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
|
||||
10
src/bits.h
10
src/bits.h
@@ -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; }
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
@@ -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)
|
||||
|
||||
329
src/mdbx.c
329
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;
|
||||
|
||||
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!");
|
||||
|
||||
33
src/osal.c
33
src/osal.c
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user