mdbxmou 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/.github/workflows/ci.yml +32 -0
  2. package/.github/workflows/publish.yml +27 -0
  3. package/.gitmodules +3 -0
  4. package/CMakeLists.txt +53 -0
  5. package/LICENSE +201 -0
  6. package/README.md +639 -0
  7. package/build.js +11 -0
  8. package/deps/libmdbx/.clang-format +3 -0
  9. package/deps/libmdbx/.cmake-format.yaml +3 -0
  10. package/deps/libmdbx/.le.ini +40 -0
  11. package/deps/libmdbx/CMakeLists.txt +1269 -0
  12. package/deps/libmdbx/COPYRIGHT +159 -0
  13. package/deps/libmdbx/ChangeLog.md +2786 -0
  14. package/deps/libmdbx/GNUmakefile +950 -0
  15. package/deps/libmdbx/LICENSE +177 -0
  16. package/deps/libmdbx/Makefile +16 -0
  17. package/deps/libmdbx/NOTICE +39 -0
  18. package/deps/libmdbx/README.md +863 -0
  19. package/deps/libmdbx/TODO.md +43 -0
  20. package/deps/libmdbx/cmake/compiler.cmake +1221 -0
  21. package/deps/libmdbx/cmake/profile.cmake +58 -0
  22. package/deps/libmdbx/cmake/utils.cmake +524 -0
  23. package/deps/libmdbx/conanfile.py +323 -0
  24. package/deps/libmdbx/docs/Doxyfile.in +2734 -0
  25. package/deps/libmdbx/docs/_preface.md +47 -0
  26. package/deps/libmdbx/docs/_restrictions.md +248 -0
  27. package/deps/libmdbx/docs/_starting.md +245 -0
  28. package/deps/libmdbx/docs/_toc.md +34 -0
  29. package/deps/libmdbx/docs/header.html +96 -0
  30. package/deps/libmdbx/example/CMakeLists.txt +6 -0
  31. package/deps/libmdbx/example/README.md +1 -0
  32. package/deps/libmdbx/example/example-mdbx.c +154 -0
  33. package/deps/libmdbx/example/sample-bdb.txt +77 -0
  34. package/deps/libmdbx/mdbx.h +6655 -0
  35. package/deps/libmdbx/mdbx.h++ +6428 -0
  36. package/deps/libmdbx/packages/buildroot/0001-package-libmdbx-new-package-library-database.patch +173 -0
  37. package/deps/libmdbx/src/alloy.c +54 -0
  38. package/deps/libmdbx/src/api-cold.c +543 -0
  39. package/deps/libmdbx/src/api-copy.c +912 -0
  40. package/deps/libmdbx/src/api-cursor.c +754 -0
  41. package/deps/libmdbx/src/api-dbi.c +315 -0
  42. package/deps/libmdbx/src/api-env.c +1434 -0
  43. package/deps/libmdbx/src/api-extra.c +165 -0
  44. package/deps/libmdbx/src/api-key-transform.c +197 -0
  45. package/deps/libmdbx/src/api-misc.c +286 -0
  46. package/deps/libmdbx/src/api-opts.c +575 -0
  47. package/deps/libmdbx/src/api-range-estimate.c +365 -0
  48. package/deps/libmdbx/src/api-txn-data.c +454 -0
  49. package/deps/libmdbx/src/api-txn.c +921 -0
  50. package/deps/libmdbx/src/atomics-ops.h +364 -0
  51. package/deps/libmdbx/src/atomics-types.h +97 -0
  52. package/deps/libmdbx/src/audit.c +109 -0
  53. package/deps/libmdbx/src/bits.md +34 -0
  54. package/deps/libmdbx/src/chk.c +1796 -0
  55. package/deps/libmdbx/src/cogs.c +309 -0
  56. package/deps/libmdbx/src/cogs.h +506 -0
  57. package/deps/libmdbx/src/coherency.c +170 -0
  58. package/deps/libmdbx/src/config.h.in +88 -0
  59. package/deps/libmdbx/src/cursor.c +2396 -0
  60. package/deps/libmdbx/src/cursor.h +391 -0
  61. package/deps/libmdbx/src/dbi.c +717 -0
  62. package/deps/libmdbx/src/dbi.h +142 -0
  63. package/deps/libmdbx/src/debug_begin.h +36 -0
  64. package/deps/libmdbx/src/debug_end.h +15 -0
  65. package/deps/libmdbx/src/dpl.c +486 -0
  66. package/deps/libmdbx/src/dpl.h +134 -0
  67. package/deps/libmdbx/src/dxb.c +1335 -0
  68. package/deps/libmdbx/src/env.c +607 -0
  69. package/deps/libmdbx/src/essentials.h +125 -0
  70. package/deps/libmdbx/src/gc-get.c +1345 -0
  71. package/deps/libmdbx/src/gc-put.c +970 -0
  72. package/deps/libmdbx/src/gc.h +40 -0
  73. package/deps/libmdbx/src/global.c +474 -0
  74. package/deps/libmdbx/src/internals.h +585 -0
  75. package/deps/libmdbx/src/layout-dxb.h +288 -0
  76. package/deps/libmdbx/src/layout-lck.h +289 -0
  77. package/deps/libmdbx/src/lck-posix.c +859 -0
  78. package/deps/libmdbx/src/lck-windows.c +607 -0
  79. package/deps/libmdbx/src/lck.c +174 -0
  80. package/deps/libmdbx/src/lck.h +110 -0
  81. package/deps/libmdbx/src/logging_and_debug.c +250 -0
  82. package/deps/libmdbx/src/logging_and_debug.h +159 -0
  83. package/deps/libmdbx/src/man1/mdbx_chk.1 +106 -0
  84. package/deps/libmdbx/src/man1/mdbx_copy.1 +95 -0
  85. package/deps/libmdbx/src/man1/mdbx_drop.1 +48 -0
  86. package/deps/libmdbx/src/man1/mdbx_dump.1 +101 -0
  87. package/deps/libmdbx/src/man1/mdbx_load.1 +105 -0
  88. package/deps/libmdbx/src/man1/mdbx_stat.1 +86 -0
  89. package/deps/libmdbx/src/mdbx.c++ +1837 -0
  90. package/deps/libmdbx/src/meta.c +656 -0
  91. package/deps/libmdbx/src/meta.h +168 -0
  92. package/deps/libmdbx/src/mvcc-readers.c +414 -0
  93. package/deps/libmdbx/src/node.c +365 -0
  94. package/deps/libmdbx/src/node.h +102 -0
  95. package/deps/libmdbx/src/ntdll.def +1246 -0
  96. package/deps/libmdbx/src/options.h +534 -0
  97. package/deps/libmdbx/src/osal.c +3485 -0
  98. package/deps/libmdbx/src/osal.h +587 -0
  99. package/deps/libmdbx/src/page-get.c +483 -0
  100. package/deps/libmdbx/src/page-iov.c +185 -0
  101. package/deps/libmdbx/src/page-iov.h +34 -0
  102. package/deps/libmdbx/src/page-ops.c +744 -0
  103. package/deps/libmdbx/src/page-ops.h +142 -0
  104. package/deps/libmdbx/src/pnl.c +236 -0
  105. package/deps/libmdbx/src/pnl.h +146 -0
  106. package/deps/libmdbx/src/preface.h +990 -0
  107. package/deps/libmdbx/src/proto.h +105 -0
  108. package/deps/libmdbx/src/refund.c +212 -0
  109. package/deps/libmdbx/src/sort.h +484 -0
  110. package/deps/libmdbx/src/spill.c +431 -0
  111. package/deps/libmdbx/src/spill.h +74 -0
  112. package/deps/libmdbx/src/table.c +107 -0
  113. package/deps/libmdbx/src/tls.c +551 -0
  114. package/deps/libmdbx/src/tls.h +43 -0
  115. package/deps/libmdbx/src/tools/chk.c +673 -0
  116. package/deps/libmdbx/src/tools/copy.c +166 -0
  117. package/deps/libmdbx/src/tools/drop.c +199 -0
  118. package/deps/libmdbx/src/tools/dump.c +515 -0
  119. package/deps/libmdbx/src/tools/load.c +831 -0
  120. package/deps/libmdbx/src/tools/stat.c +516 -0
  121. package/deps/libmdbx/src/tools/wingetopt.c +87 -0
  122. package/deps/libmdbx/src/tools/wingetopt.h +30 -0
  123. package/deps/libmdbx/src/tree-ops.c +1554 -0
  124. package/deps/libmdbx/src/tree-search.c +140 -0
  125. package/deps/libmdbx/src/txl.c +99 -0
  126. package/deps/libmdbx/src/txl.h +26 -0
  127. package/deps/libmdbx/src/txn.c +1083 -0
  128. package/deps/libmdbx/src/unaligned.h +205 -0
  129. package/deps/libmdbx/src/utils.c +32 -0
  130. package/deps/libmdbx/src/utils.h +76 -0
  131. package/deps/libmdbx/src/version.c.in +44 -0
  132. package/deps/libmdbx/src/walk.c +290 -0
  133. package/deps/libmdbx/src/walk.h +20 -0
  134. package/deps/libmdbx/src/windows-import.c +152 -0
  135. package/deps/libmdbx/src/windows-import.h +128 -0
  136. package/deps/libmdbx/test/CMakeLists.txt +317 -0
  137. package/deps/libmdbx/test/append.c++ +237 -0
  138. package/deps/libmdbx/test/base.h++ +92 -0
  139. package/deps/libmdbx/test/battery-tmux.sh +64 -0
  140. package/deps/libmdbx/test/cases.c++ +118 -0
  141. package/deps/libmdbx/test/chrono.c++ +134 -0
  142. package/deps/libmdbx/test/chrono.h++ +85 -0
  143. package/deps/libmdbx/test/config.c++ +643 -0
  144. package/deps/libmdbx/test/config.h++ +334 -0
  145. package/deps/libmdbx/test/copy.c++ +62 -0
  146. package/deps/libmdbx/test/dead.c++ +39 -0
  147. package/deps/libmdbx/test/dump-load.sh +40 -0
  148. package/deps/libmdbx/test/extra/crunched_delete.c++ +409 -0
  149. package/deps/libmdbx/test/extra/cursor_closing.c++ +410 -0
  150. package/deps/libmdbx/test/extra/dbi.c++ +229 -0
  151. package/deps/libmdbx/test/extra/doubtless_positioning.c++ +253 -0
  152. package/deps/libmdbx/test/extra/dupfix_addodd.c +94 -0
  153. package/deps/libmdbx/test/extra/dupfix_multiple.c++ +311 -0
  154. package/deps/libmdbx/test/extra/early_close_dbi.c++ +137 -0
  155. package/deps/libmdbx/test/extra/hex_base64_base58.c++ +118 -0
  156. package/deps/libmdbx/test/extra/maindb_ordinal.c++ +61 -0
  157. package/deps/libmdbx/test/extra/open.c++ +96 -0
  158. package/deps/libmdbx/test/extra/pcrf/README.md +2 -0
  159. package/deps/libmdbx/test/extra/pcrf/pcrf_test.c +380 -0
  160. package/deps/libmdbx/test/extra/probe.c++ +10 -0
  161. package/deps/libmdbx/test/extra/txn.c++ +407 -0
  162. package/deps/libmdbx/test/extra/upsert_alldups.c +193 -0
  163. package/deps/libmdbx/test/fork.c++ +263 -0
  164. package/deps/libmdbx/test/hill.c++ +447 -0
  165. package/deps/libmdbx/test/jitter.c++ +197 -0
  166. package/deps/libmdbx/test/keygen.c++ +393 -0
  167. package/deps/libmdbx/test/keygen.h++ +130 -0
  168. package/deps/libmdbx/test/log.c++ +358 -0
  169. package/deps/libmdbx/test/log.h++ +91 -0
  170. package/deps/libmdbx/test/main.c++ +706 -0
  171. package/deps/libmdbx/test/nested.c++ +318 -0
  172. package/deps/libmdbx/test/osal-unix.c++ +647 -0
  173. package/deps/libmdbx/test/osal-windows.c++ +440 -0
  174. package/deps/libmdbx/test/osal.h++ +41 -0
  175. package/deps/libmdbx/test/stochastic.sh +690 -0
  176. package/deps/libmdbx/test/stub/LICENSE +24 -0
  177. package/deps/libmdbx/test/stub/README.md +8 -0
  178. package/deps/libmdbx/test/stub/pthread_barrier.c +104 -0
  179. package/deps/libmdbx/test/stub/pthread_barrier.h +77 -0
  180. package/deps/libmdbx/test/test.c++ +1551 -0
  181. package/deps/libmdbx/test/test.h++ +298 -0
  182. package/deps/libmdbx/test/tmux.conf +3 -0
  183. package/deps/libmdbx/test/try.c++ +30 -0
  184. package/deps/libmdbx/test/ttl.c++ +240 -0
  185. package/deps/libmdbx/test/utils.c++ +203 -0
  186. package/deps/libmdbx/test/utils.h++ +326 -0
  187. package/deps/libmdbx/test/valgrind_suppress.txt +536 -0
  188. package/lib/mdbx_evn_async.js +211 -0
  189. package/lib/mdbx_worker.js +195 -0
  190. package/lib/nativemou.js +6 -0
  191. package/package.json +38 -0
  192. package/src/async/envmou_close.cpp +34 -0
  193. package/src/async/envmou_close.hpp +32 -0
  194. package/src/async/envmou_copy_to.cpp +29 -0
  195. package/src/async/envmou_copy_to.hpp +38 -0
  196. package/src/async/envmou_keys.cpp +201 -0
  197. package/src/async/envmou_keys.hpp +50 -0
  198. package/src/async/envmou_open.cpp +38 -0
  199. package/src/async/envmou_open.hpp +33 -0
  200. package/src/async/envmou_query.cpp +167 -0
  201. package/src/async/envmou_query.hpp +53 -0
  202. package/src/dbimou.cpp +522 -0
  203. package/src/dbimou.hpp +82 -0
  204. package/src/env_arg0.hpp +24 -0
  205. package/src/envmou.cpp +445 -0
  206. package/src/envmou.hpp +116 -0
  207. package/src/modulemou.cpp +113 -0
  208. package/src/querymou.cpp +177 -0
  209. package/src/querymou.hpp +93 -0
  210. package/src/txnmou.cpp +254 -0
  211. package/src/txnmou.hpp +122 -0
  212. package/src/typemou.hpp +239 -0
  213. package/src/valuemou.hpp +194 -0
  214. package/test/async.js +67 -0
  215. package/test/e3.js +38 -0
  216. package/test/e4.js +89 -0
  217. package/test/e5.js +162 -0
  218. package/test/test-batch-ops.js +243 -0
  219. package/test/test-cursor-mode.js +84 -0
  220. package/test/test-multi-mode.js +87 -0
@@ -0,0 +1,551 @@
1
+ /// \copyright SPDX-License-Identifier: Apache-2.0
2
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
3
+
4
+ #include "internals.h"
5
+
6
+ typedef struct rthc_entry {
7
+ MDBX_env *env;
8
+ } rthc_entry_t;
9
+
10
+ #if MDBX_DEBUG
11
+ #define RTHC_INITIAL_LIMIT 1
12
+ #else
13
+ #define RTHC_INITIAL_LIMIT 16
14
+ #endif
15
+
16
+ static unsigned rthc_count, rthc_limit = RTHC_INITIAL_LIMIT;
17
+ static rthc_entry_t rthc_table_static[RTHC_INITIAL_LIMIT];
18
+ static rthc_entry_t *rthc_table = rthc_table_static;
19
+
20
+ static int uniq_peek(const osal_mmap_t *pending, osal_mmap_t *scan) {
21
+ int rc;
22
+ uint64_t bait;
23
+ lck_t *const pending_lck = pending->lck;
24
+ lck_t *const scan_lck = scan->lck;
25
+ if (pending_lck) {
26
+ bait = atomic_load64(&pending_lck->bait_uniqueness, mo_AcquireRelease);
27
+ rc = MDBX_SUCCESS;
28
+ } else {
29
+ bait = 0 /* hush MSVC warning */;
30
+ rc = osal_msync(scan, 0, sizeof(lck_t), MDBX_SYNC_DATA);
31
+ if (rc == MDBX_SUCCESS)
32
+ rc = osal_pread(pending->fd, &bait, sizeof(scan_lck->bait_uniqueness), offsetof(lck_t, bait_uniqueness));
33
+ }
34
+ if (likely(rc == MDBX_SUCCESS) && bait == atomic_load64(&scan_lck->bait_uniqueness, mo_AcquireRelease))
35
+ rc = MDBX_RESULT_TRUE;
36
+
37
+ TRACE("uniq-peek: %s, bait 0x%016" PRIx64 ",%s rc %d", pending_lck ? "mem" : "file", bait,
38
+ (rc == MDBX_RESULT_TRUE) ? " found," : (rc ? " FAILED," : ""), rc);
39
+ return rc;
40
+ }
41
+
42
+ static int uniq_poke(const osal_mmap_t *pending, osal_mmap_t *scan, uint64_t *abra) {
43
+ if (*abra == 0) {
44
+ const uintptr_t tid = osal_thread_self();
45
+ uintptr_t uit = 0;
46
+ memcpy(&uit, &tid, (sizeof(tid) < sizeof(uit)) ? sizeof(tid) : sizeof(uit));
47
+ *abra = rrxmrrxmsx_0(osal_monotime() + UINT64_C(5873865991930747) * uit);
48
+ }
49
+ const uint64_t cadabra =
50
+ rrxmrrxmsx_0(*abra + UINT64_C(7680760450171793) * (unsigned)osal_getpid()) << 24 | *abra >> 40;
51
+ lck_t *const scan_lck = scan->lck;
52
+ atomic_store64(&scan_lck->bait_uniqueness, cadabra, mo_AcquireRelease);
53
+ *abra = *abra * UINT64_C(6364136223846793005) + 1;
54
+ return uniq_peek(pending, scan);
55
+ }
56
+
57
+ __cold int rthc_uniq_check(const osal_mmap_t *pending, MDBX_env **found) {
58
+ *found = nullptr;
59
+ uint64_t salt = 0;
60
+ for (size_t i = 0; i < rthc_count; ++i) {
61
+ MDBX_env *const scan = rthc_table[i].env;
62
+ if (!scan->lck_mmap.lck || &scan->lck_mmap == pending)
63
+ continue;
64
+ int err = atomic_load64(&scan->lck_mmap.lck->bait_uniqueness, mo_AcquireRelease)
65
+ ? uniq_peek(pending, &scan->lck_mmap)
66
+ : uniq_poke(pending, &scan->lck_mmap, &salt);
67
+ if (err == MDBX_ENODATA) {
68
+ uint64_t length = 0;
69
+ if (likely(osal_filesize(pending->fd, &length) == MDBX_SUCCESS && length == 0)) {
70
+ /* LY: skip checking since LCK-file is empty, i.e. just created. */
71
+ DEBUG("%s", "unique (new/empty lck)");
72
+ return MDBX_SUCCESS;
73
+ }
74
+ }
75
+ if (err == MDBX_RESULT_TRUE)
76
+ err = uniq_poke(pending, &scan->lck_mmap, &salt);
77
+ if (err == MDBX_RESULT_TRUE) {
78
+ (void)osal_msync(&scan->lck_mmap, 0, sizeof(lck_t), MDBX_SYNC_KICK);
79
+ err = uniq_poke(pending, &scan->lck_mmap, &salt);
80
+ }
81
+ if (err == MDBX_RESULT_TRUE) {
82
+ err = uniq_poke(pending, &scan->lck_mmap, &salt);
83
+ *found = scan;
84
+ DEBUG("found %p", __Wpedantic_format_voidptr(*found));
85
+ return MDBX_SUCCESS;
86
+ }
87
+ if (unlikely(err != MDBX_SUCCESS)) {
88
+ DEBUG("failed rc %d", err);
89
+ return err;
90
+ }
91
+ }
92
+
93
+ DEBUG("%s", "unique");
94
+ return MDBX_SUCCESS;
95
+ }
96
+
97
+ //------------------------------------------------------------------------------
98
+
99
+ #if defined(_WIN32) || defined(_WIN64)
100
+ static CRITICAL_SECTION rthc_critical_section;
101
+ #else
102
+
103
+ static pthread_mutex_t rthc_mutex = PTHREAD_MUTEX_INITIALIZER;
104
+ static pthread_cond_t rthc_cond = PTHREAD_COND_INITIALIZER;
105
+ static osal_thread_key_t rthc_key;
106
+ static mdbx_atomic_uint32_t rthc_pending;
107
+
108
+ static inline uint64_t rthc_signature(const void *addr, uint8_t kind) {
109
+ uint64_t salt = osal_thread_self() * UINT64_C(0xA2F0EEC059629A17) ^ UINT64_C(0x01E07C6FDB596497) * (uintptr_t)(addr);
110
+ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
111
+ return salt << 8 | kind;
112
+ #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
113
+ return (uint64_t)kind << 56 | salt >> 8;
114
+ #else
115
+ #error "FIXME: Unsupported byte order"
116
+ #endif /* __BYTE_ORDER__ */
117
+ }
118
+
119
+ #define MDBX_THREAD_RTHC_REGISTERED(addr) rthc_signature(addr, 0x0D)
120
+ #define MDBX_THREAD_RTHC_COUNTED(addr) rthc_signature(addr, 0xC0)
121
+ static __thread uint64_t rthc_thread_state
122
+ #if __has_attribute(tls_model) && (defined(__PIC__) || defined(__pic__) || MDBX_BUILD_SHARED_LIBRARY)
123
+ __attribute__((tls_model("local-dynamic")))
124
+ #endif
125
+ ;
126
+
127
+ #if defined(__APPLE__) && defined(__SANITIZE_ADDRESS__) && !defined(MDBX_ATTRIBUTE_NO_SANITIZE_ADDRESS)
128
+ /* Avoid ASAN-trap due the target TLS-variable feed by Darwin's tlv_free() */
129
+ #define MDBX_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((__no_sanitize_address__, __noinline__))
130
+ #else
131
+ #define MDBX_ATTRIBUTE_NO_SANITIZE_ADDRESS inline
132
+ #endif
133
+
134
+ MDBX_ATTRIBUTE_NO_SANITIZE_ADDRESS static uint64_t rthc_read(const void *rthc) { return *(volatile uint64_t *)rthc; }
135
+
136
+ MDBX_ATTRIBUTE_NO_SANITIZE_ADDRESS static uint64_t rthc_compare_and_clean(const void *rthc, const uint64_t signature) {
137
+ #if MDBX_64BIT_CAS
138
+ return atomic_cas64((mdbx_atomic_uint64_t *)rthc, signature, 0);
139
+ #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
140
+ return atomic_cas32((mdbx_atomic_uint32_t *)rthc, (uint32_t)signature, 0);
141
+ #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
142
+ return atomic_cas32((mdbx_atomic_uint32_t *)rthc, (uint32_t)(signature >> 32), 0);
143
+ #else
144
+ #error "FIXME: Unsupported byte order"
145
+ #endif
146
+ }
147
+
148
+ static inline int rthc_atexit(void (*dtor)(void *), void *obj, void *dso_symbol) {
149
+ #ifndef MDBX_HAVE_CXA_THREAD_ATEXIT_IMPL
150
+ #if defined(LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL) || defined(HAVE___CXA_THREAD_ATEXIT_IMPL) || \
151
+ __GLIBC_PREREQ(2, 18) || defined(BIONIC)
152
+ #define MDBX_HAVE_CXA_THREAD_ATEXIT_IMPL 1
153
+ #else
154
+ #define MDBX_HAVE_CXA_THREAD_ATEXIT_IMPL 0
155
+ #endif
156
+ #endif /* MDBX_HAVE_CXA_THREAD_ATEXIT_IMPL */
157
+
158
+ #ifndef MDBX_HAVE_CXA_THREAD_ATEXIT
159
+ #if defined(LIBCXXABI_HAS_CXA_THREAD_ATEXIT) || defined(HAVE___CXA_THREAD_ATEXIT)
160
+ #define MDBX_HAVE_CXA_THREAD_ATEXIT 1
161
+ #elif !MDBX_HAVE_CXA_THREAD_ATEXIT_IMPL && (defined(__linux__) || defined(__gnu_linux__))
162
+ #define MDBX_HAVE_CXA_THREAD_ATEXIT 1
163
+ #else
164
+ #define MDBX_HAVE_CXA_THREAD_ATEXIT 0
165
+ #endif
166
+ #endif /* MDBX_HAVE_CXA_THREAD_ATEXIT */
167
+
168
+ int rc = MDBX_ENOSYS;
169
+ #if MDBX_HAVE_CXA_THREAD_ATEXIT_IMPL && !MDBX_HAVE_CXA_THREAD_ATEXIT
170
+ #define __cxa_thread_atexit __cxa_thread_atexit_impl
171
+ #endif
172
+ #if MDBX_HAVE_CXA_THREAD_ATEXIT || defined(__cxa_thread_atexit)
173
+ extern int __cxa_thread_atexit(void (*dtor)(void *), void *obj, void *dso_symbol) MDBX_WEAK_IMPORT_ATTRIBUTE;
174
+ if (&__cxa_thread_atexit)
175
+ rc = __cxa_thread_atexit(dtor, obj, dso_symbol);
176
+ #elif defined(__APPLE__) || defined(_DARWIN_C_SOURCE)
177
+ extern void _tlv_atexit(void (*termfunc)(void *objAddr), void *objAddr) MDBX_WEAK_IMPORT_ATTRIBUTE;
178
+ if (&_tlv_atexit) {
179
+ (void)dso_symbol;
180
+ _tlv_atexit(dtor, obj);
181
+ rc = 0;
182
+ }
183
+ #else
184
+ (void)dtor;
185
+ (void)obj;
186
+ (void)dso_symbol;
187
+ #endif
188
+ return rc;
189
+ }
190
+
191
+ __cold void workaround_glibc_bug21031(void) {
192
+ /* Workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=21031
193
+ *
194
+ * Due race between pthread_key_delete() and __nptl_deallocate_tsd()
195
+ * The destructor(s) of thread-local-storage object(s) may be running
196
+ * in another thread(s) and be blocked or not finished yet.
197
+ * In such case we get a SEGFAULT after unload this library DSO.
198
+ *
199
+ * So just by yielding a few timeslices we give a chance
200
+ * to such destructor(s) for completion and avoids segfault. */
201
+ sched_yield();
202
+ sched_yield();
203
+ sched_yield();
204
+ }
205
+ #endif /* !Windows */
206
+
207
+ void rthc_lock(void) {
208
+ #if defined(_WIN32) || defined(_WIN64)
209
+ EnterCriticalSection(&rthc_critical_section);
210
+ #else
211
+ ENSURE(nullptr, osal_pthread_mutex_lock(&rthc_mutex) == 0);
212
+ #endif
213
+ }
214
+
215
+ void rthc_unlock(void) {
216
+ #if defined(_WIN32) || defined(_WIN64)
217
+ LeaveCriticalSection(&rthc_critical_section);
218
+ #else
219
+ ENSURE(nullptr, pthread_mutex_unlock(&rthc_mutex) == 0);
220
+ #endif
221
+ }
222
+
223
+ static inline int thread_key_create(osal_thread_key_t *key) {
224
+ int rc;
225
+ #if defined(_WIN32) || defined(_WIN64)
226
+ *key = TlsAlloc();
227
+ rc = (*key != TLS_OUT_OF_INDEXES) ? MDBX_SUCCESS : GetLastError();
228
+ #else
229
+ rc = pthread_key_create(key, nullptr);
230
+ #endif
231
+ TRACE("&key = %p, value %" PRIuPTR ", rc %d", __Wpedantic_format_voidptr(key), (uintptr_t)*key, rc);
232
+ return rc;
233
+ }
234
+
235
+ void thread_rthc_set(osal_thread_key_t key, const void *value) {
236
+ #if defined(_WIN32) || defined(_WIN64)
237
+ ENSURE(nullptr, TlsSetValue(key, (void *)value));
238
+ #else
239
+ const uint64_t sign_registered = MDBX_THREAD_RTHC_REGISTERED(&rthc_thread_state);
240
+ const uint64_t sign_counted = MDBX_THREAD_RTHC_COUNTED(&rthc_thread_state);
241
+ if (value && unlikely(rthc_thread_state != sign_registered && rthc_thread_state != sign_counted)) {
242
+ rthc_thread_state = sign_registered;
243
+ TRACE("thread registered 0x%" PRIxPTR, osal_thread_self());
244
+ if (rthc_atexit(rthc_thread_dtor, &rthc_thread_state, (void *)&mdbx_version /* dso_anchor */)) {
245
+ ENSURE(nullptr, pthread_setspecific(rthc_key, &rthc_thread_state) == 0);
246
+ rthc_thread_state = sign_counted;
247
+ const unsigned count_before = atomic_add32(&rthc_pending, 1);
248
+ ENSURE(nullptr, count_before < INT_MAX);
249
+ NOTICE("fallback to pthreads' tsd, key %" PRIuPTR ", count %u", (uintptr_t)rthc_key, count_before);
250
+ (void)count_before;
251
+ }
252
+ }
253
+ ENSURE(nullptr, pthread_setspecific(key, value) == 0);
254
+ #endif
255
+ }
256
+
257
+ /* dtor called for thread, i.e. for all mdbx's environment objects */
258
+ __cold void rthc_thread_dtor(void *rthc) {
259
+ rthc_lock();
260
+ const uint32_t current_pid = osal_getpid();
261
+ #if defined(_WIN32) || defined(_WIN64)
262
+ TRACE(">> pid %d, thread 0x%" PRIxPTR ", module %p", current_pid, osal_thread_self(), rthc);
263
+ #else
264
+ TRACE(">> pid %d, thread 0x%" PRIxPTR ", rthc %p", current_pid, osal_thread_self(), rthc);
265
+ #endif
266
+
267
+ for (size_t i = 0; i < rthc_count; ++i) {
268
+ MDBX_env *const env = rthc_table[i].env;
269
+ if (env->pid != current_pid)
270
+ continue;
271
+ if (!(env->flags & ENV_TXKEY))
272
+ continue;
273
+ reader_slot_t *const reader = thread_rthc_get(env->me_txkey);
274
+ reader_slot_t *const begin = &env->lck_mmap.lck->rdt[0];
275
+ reader_slot_t *const end = &env->lck_mmap.lck->rdt[env->max_readers];
276
+ if (reader < begin || reader >= end)
277
+ continue;
278
+ #if !defined(_WIN32) && !defined(_WIN64)
279
+ if (pthread_setspecific(env->me_txkey, nullptr) != 0) {
280
+ TRACE("== thread 0x%" PRIxPTR ", rthc %p: ignore race with tsd-key deletion", osal_thread_self(),
281
+ __Wpedantic_format_voidptr(reader));
282
+ continue /* ignore race with tsd-key deletion by mdbx_env_close() */;
283
+ }
284
+ #endif
285
+
286
+ TRACE("== thread 0x%" PRIxPTR ", rthc %p, [%zi], %p ... %p (%+i), rtch-pid %i, "
287
+ "current-pid %i",
288
+ osal_thread_self(), __Wpedantic_format_voidptr(reader), i, __Wpedantic_format_voidptr(begin),
289
+ __Wpedantic_format_voidptr(end), (int)(reader - begin), reader->pid.weak, current_pid);
290
+ if (atomic_load32(&reader->pid, mo_Relaxed) == current_pid) {
291
+ TRACE("==== thread 0x%" PRIxPTR ", rthc %p, cleanup", osal_thread_self(), __Wpedantic_format_voidptr(reader));
292
+ (void)atomic_cas32(&reader->pid, current_pid, 0);
293
+ atomic_store32(&env->lck->rdt_refresh_flag, true, mo_Relaxed);
294
+ }
295
+ }
296
+
297
+ #if defined(_WIN32) || defined(_WIN64)
298
+ TRACE("<< thread 0x%" PRIxPTR ", module %p", osal_thread_self(), rthc);
299
+ rthc_unlock();
300
+ #else
301
+ const uint64_t sign_registered = MDBX_THREAD_RTHC_REGISTERED(rthc);
302
+ const uint64_t sign_counted = MDBX_THREAD_RTHC_COUNTED(rthc);
303
+ const uint64_t state = rthc_read(rthc);
304
+ if (state == sign_registered && rthc_compare_and_clean(rthc, sign_registered)) {
305
+ TRACE("== thread 0x%" PRIxPTR ", rthc %p, pid %d, self-status %s (0x%08" PRIx64 ")", osal_thread_self(), rthc,
306
+ osal_getpid(), "registered", state);
307
+ } else if (state == sign_counted && rthc_compare_and_clean(rthc, sign_counted)) {
308
+ TRACE("== thread 0x%" PRIxPTR ", rthc %p, pid %d, self-status %s (0x%08" PRIx64 ")", osal_thread_self(), rthc,
309
+ osal_getpid(), "counted", state);
310
+ ENSURE(nullptr, atomic_sub32(&rthc_pending, 1) > 0);
311
+ } else {
312
+ WARNING("thread 0x%" PRIxPTR ", rthc %p, pid %d, self-status %s (0x%08" PRIx64 ")", osal_thread_self(), rthc,
313
+ osal_getpid(), "wrong", state);
314
+ }
315
+
316
+ if (atomic_load32(&rthc_pending, mo_AcquireRelease) == 0) {
317
+ TRACE("== thread 0x%" PRIxPTR ", rthc %p, pid %d, wake", osal_thread_self(), rthc, osal_getpid());
318
+ ENSURE(nullptr, pthread_cond_broadcast(&rthc_cond) == 0);
319
+ }
320
+
321
+ TRACE("<< thread 0x%" PRIxPTR ", rthc %p", osal_thread_self(), rthc);
322
+ /* Allow tail call optimization, i.e. gcc should generate the jmp instruction
323
+ * instead of a call for pthread_mutex_unlock() and therefore CPU could not
324
+ * return to current DSO's code section, which may be unloaded immediately
325
+ * after the mutex got released. */
326
+ pthread_mutex_unlock(&rthc_mutex);
327
+ #endif
328
+ }
329
+
330
+ __cold int rthc_register(MDBX_env *const env) {
331
+ TRACE(">> env %p, rthc_count %u, rthc_limit %u", __Wpedantic_format_voidptr(env), rthc_count, rthc_limit);
332
+
333
+ int rc = MDBX_SUCCESS;
334
+ for (size_t i = 0; i < rthc_count; ++i)
335
+ if (unlikely(rthc_table[i].env == env)) {
336
+ rc = MDBX_PANIC;
337
+ goto bailout;
338
+ }
339
+
340
+ env->me_txkey = 0;
341
+ if (unlikely(rthc_count == rthc_limit)) {
342
+ rthc_entry_t *new_table =
343
+ osal_realloc((rthc_table == rthc_table_static) ? nullptr : rthc_table, sizeof(rthc_entry_t) * rthc_limit * 2);
344
+ if (unlikely(new_table == nullptr)) {
345
+ rc = MDBX_ENOMEM;
346
+ goto bailout;
347
+ }
348
+ if (rthc_table == rthc_table_static)
349
+ memcpy(new_table, rthc_table, sizeof(rthc_entry_t) * rthc_limit);
350
+ rthc_table = new_table;
351
+ rthc_limit *= 2;
352
+ }
353
+
354
+ if ((env->flags & MDBX_NOSTICKYTHREADS) == 0) {
355
+ rc = thread_key_create(&env->me_txkey);
356
+ if (unlikely(rc != MDBX_SUCCESS))
357
+ goto bailout;
358
+ env->flags |= ENV_TXKEY;
359
+ }
360
+
361
+ rthc_table[rthc_count].env = env;
362
+ TRACE("== [%i] = env %p, key %" PRIuPTR, rthc_count, __Wpedantic_format_voidptr(env), (uintptr_t)env->me_txkey);
363
+ ++rthc_count;
364
+
365
+ bailout:
366
+ TRACE("<< env %p, key %" PRIuPTR ", rthc_count %u, rthc_limit %u, rc %d", __Wpedantic_format_voidptr(env),
367
+ (uintptr_t)env->me_txkey, rthc_count, rthc_limit, rc);
368
+ return rc;
369
+ }
370
+
371
+ __cold static int rthc_drown(MDBX_env *const env) {
372
+ const uint32_t current_pid = osal_getpid();
373
+ int rc = MDBX_SUCCESS;
374
+ MDBX_env *inprocess_neighbor = nullptr;
375
+ if (likely(env->lck_mmap.lck && current_pid == env->pid)) {
376
+ reader_slot_t *const begin = &env->lck_mmap.lck->rdt[0];
377
+ reader_slot_t *const end = &env->lck_mmap.lck->rdt[env->max_readers];
378
+ TRACE("== %s env %p pid %d, readers %p ...%p, current-pid %d", (current_pid == env->pid) ? "cleanup" : "skip",
379
+ __Wpedantic_format_voidptr(env), env->pid, __Wpedantic_format_voidptr(begin), __Wpedantic_format_voidptr(end),
380
+ current_pid);
381
+ bool cleaned = false;
382
+ for (reader_slot_t *r = begin; r < end; ++r) {
383
+ if (atomic_load32(&r->pid, mo_Relaxed) == current_pid) {
384
+ atomic_store32(&r->pid, 0, mo_AcquireRelease);
385
+ TRACE("== cleanup %p", __Wpedantic_format_voidptr(r));
386
+ cleaned = true;
387
+ }
388
+ }
389
+ if (cleaned)
390
+ atomic_store32(&env->lck_mmap.lck->rdt_refresh_flag, true, mo_Relaxed);
391
+ rc = rthc_uniq_check(&env->lck_mmap, &inprocess_neighbor);
392
+ if (!inprocess_neighbor && env->registered_reader_pid && env->lck_mmap.fd != INVALID_HANDLE_VALUE) {
393
+ int err = lck_rpid_clear(env);
394
+ rc = rc ? rc : err;
395
+ }
396
+ }
397
+ int err = lck_destroy(env, inprocess_neighbor, current_pid);
398
+ env->pid = 0;
399
+ return rc ? rc : err;
400
+ }
401
+
402
+ __cold int rthc_remove(MDBX_env *const env) {
403
+ TRACE(">>> env %p, key %zu, rthc_count %u, rthc_limit %u", __Wpedantic_format_voidptr(env), (uintptr_t)env->me_txkey,
404
+ rthc_count, rthc_limit);
405
+
406
+ int rc = MDBX_SUCCESS;
407
+ if (likely(env->pid))
408
+ rc = rthc_drown(env);
409
+
410
+ for (size_t i = 0; i < rthc_count; ++i) {
411
+ if (rthc_table[i].env == env) {
412
+ if (--rthc_count > 0)
413
+ rthc_table[i] = rthc_table[rthc_count];
414
+ else if (rthc_table != rthc_table_static) {
415
+ void *tmp = rthc_table;
416
+ rthc_table = rthc_table_static;
417
+ rthc_limit = RTHC_INITIAL_LIMIT;
418
+ osal_memory_barrier();
419
+ osal_free(tmp);
420
+ }
421
+ break;
422
+ }
423
+ }
424
+
425
+ TRACE("<<< %p, key %zu, rthc_count %u, rthc_limit %u", __Wpedantic_format_voidptr(env), (uintptr_t)env->me_txkey,
426
+ rthc_count, rthc_limit);
427
+ return rc;
428
+ }
429
+
430
+ #if !defined(_WIN32) && !defined(_WIN64)
431
+ __cold void rthc_afterfork(void) {
432
+ NOTICE("drown %d rthc entries", rthc_count);
433
+ for (size_t i = 0; i < rthc_count; ++i) {
434
+ MDBX_env *const env = rthc_table[i].env;
435
+ NOTICE("drown env %p", __Wpedantic_format_voidptr(env));
436
+ if (env->lck_mmap.lck)
437
+ osal_munmap(&env->lck_mmap);
438
+ if (env->dxb_mmap.base) {
439
+ osal_munmap(&env->dxb_mmap);
440
+ #ifdef ENABLE_MEMCHECK
441
+ VALGRIND_DISCARD(env->valgrind_handle);
442
+ env->valgrind_handle = -1;
443
+ #endif /* ENABLE_MEMCHECK */
444
+ }
445
+ env->lck = lckless_stub(env);
446
+ rthc_drown(env);
447
+ }
448
+ if (rthc_table != rthc_table_static)
449
+ osal_free(rthc_table);
450
+ rthc_count = 0;
451
+ rthc_table = rthc_table_static;
452
+ rthc_limit = RTHC_INITIAL_LIMIT;
453
+ rthc_pending.weak = 0;
454
+ }
455
+ #endif /* ! Windows */
456
+
457
+ __cold void rthc_ctor(void) {
458
+ #if defined(_WIN32) || defined(_WIN64)
459
+ InitializeCriticalSection(&rthc_critical_section);
460
+ #else
461
+ ENSURE(nullptr, pthread_atfork(nullptr, nullptr, rthc_afterfork) == 0);
462
+ ENSURE(nullptr, pthread_key_create(&rthc_key, rthc_thread_dtor) == 0);
463
+ TRACE("pid %d, &mdbx_rthc_key = %p, value 0x%x", osal_getpid(), __Wpedantic_format_voidptr(&rthc_key),
464
+ (unsigned)rthc_key);
465
+ #endif
466
+ }
467
+
468
+ __cold void rthc_dtor(const uint32_t current_pid) {
469
+ rthc_lock();
470
+ #if !defined(_WIN32) && !defined(_WIN64)
471
+ uint64_t *rthc = pthread_getspecific(rthc_key);
472
+ TRACE("== thread 0x%" PRIxPTR ", rthc %p, pid %d, self-status 0x%08" PRIx64 ", left %d", osal_thread_self(),
473
+ __Wpedantic_format_voidptr(rthc), current_pid, rthc ? rthc_read(rthc) : ~UINT64_C(0),
474
+ atomic_load32(&rthc_pending, mo_Relaxed));
475
+ if (rthc) {
476
+ const uint64_t sign_registered = MDBX_THREAD_RTHC_REGISTERED(rthc);
477
+ const uint64_t sign_counted = MDBX_THREAD_RTHC_COUNTED(rthc);
478
+ const uint64_t state = rthc_read(rthc);
479
+ if (state == sign_registered && rthc_compare_and_clean(rthc, sign_registered)) {
480
+ TRACE("== thread 0x%" PRIxPTR ", rthc %p, pid %d, self-status %s (0x%08" PRIx64 ")", osal_thread_self(),
481
+ __Wpedantic_format_voidptr(rthc), current_pid, "registered", state);
482
+ } else if (state == sign_counted && rthc_compare_and_clean(rthc, sign_counted)) {
483
+ TRACE("== thread 0x%" PRIxPTR ", rthc %p, pid %d, self-status %s (0x%08" PRIx64 ")", osal_thread_self(),
484
+ __Wpedantic_format_voidptr(rthc), current_pid, "counted", state);
485
+ ENSURE(nullptr, atomic_sub32(&rthc_pending, 1) > 0);
486
+ } else {
487
+ WARNING("thread 0x%" PRIxPTR ", rthc %p, pid %d, self-status %s (0x%08" PRIx64 ")", osal_thread_self(),
488
+ __Wpedantic_format_voidptr(rthc), current_pid, "wrong", state);
489
+ }
490
+ }
491
+
492
+ struct timespec abstime;
493
+ ENSURE(nullptr, clock_gettime(CLOCK_REALTIME, &abstime) == 0);
494
+ abstime.tv_nsec += 1000000000l / 10;
495
+ if (abstime.tv_nsec >= 1000000000l) {
496
+ abstime.tv_nsec -= 1000000000l;
497
+ abstime.tv_sec += 1;
498
+ }
499
+ #if MDBX_DEBUG > 0
500
+ abstime.tv_sec += 600;
501
+ #endif
502
+
503
+ for (unsigned left; (left = atomic_load32(&rthc_pending, mo_AcquireRelease)) > 0;) {
504
+ NOTICE("tls-cleanup: pid %d, pending %u, wait for...", current_pid, left);
505
+ const int rc = pthread_cond_timedwait(&rthc_cond, &rthc_mutex, &abstime);
506
+ if (rc && rc != EINTR)
507
+ break;
508
+ }
509
+ thread_key_delete(rthc_key);
510
+ #endif
511
+
512
+ for (size_t i = 0; i < rthc_count; ++i) {
513
+ MDBX_env *const env = rthc_table[i].env;
514
+ if (env->pid != current_pid)
515
+ continue;
516
+ if (!(env->flags & ENV_TXKEY))
517
+ continue;
518
+ env->flags -= ENV_TXKEY;
519
+ reader_slot_t *const begin = &env->lck_mmap.lck->rdt[0];
520
+ reader_slot_t *const end = &env->lck_mmap.lck->rdt[env->max_readers];
521
+ thread_key_delete(env->me_txkey);
522
+ bool cleaned = false;
523
+ for (reader_slot_t *reader = begin; reader < end; ++reader) {
524
+ TRACE("== [%zi] = key %" PRIuPTR ", %p ... %p, rthc %p (%+i), "
525
+ "rthc-pid %i, current-pid %i",
526
+ i, (uintptr_t)env->me_txkey, __Wpedantic_format_voidptr(begin), __Wpedantic_format_voidptr(end),
527
+ __Wpedantic_format_voidptr(reader), (int)(reader - begin), reader->pid.weak, current_pid);
528
+ if (atomic_load32(&reader->pid, mo_Relaxed) == current_pid) {
529
+ (void)atomic_cas32(&reader->pid, current_pid, 0);
530
+ TRACE("== cleanup %p", __Wpedantic_format_voidptr(reader));
531
+ cleaned = true;
532
+ }
533
+ }
534
+ if (cleaned)
535
+ atomic_store32(&env->lck->rdt_refresh_flag, true, mo_Relaxed);
536
+ }
537
+
538
+ rthc_limit = rthc_count = 0;
539
+ if (rthc_table != rthc_table_static)
540
+ osal_free(rthc_table);
541
+ rthc_table = nullptr;
542
+ rthc_unlock();
543
+
544
+ #if defined(_WIN32) || defined(_WIN64)
545
+ DeleteCriticalSection(&rthc_critical_section);
546
+ #else
547
+ /* LY: yielding a few timeslices to give a more chance
548
+ * to racing destructor(s) for completion. */
549
+ workaround_glibc_bug21031();
550
+ #endif
551
+ }
@@ -0,0 +1,43 @@
1
+ /// \copyright SPDX-License-Identifier: Apache-2.0
2
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
3
+
4
+ #pragma once
5
+
6
+ #include "essentials.h"
7
+
8
+ MDBX_INTERNAL void rthc_ctor(void);
9
+ MDBX_INTERNAL void rthc_dtor(const uint32_t current_pid);
10
+ MDBX_INTERNAL void rthc_lock(void);
11
+ MDBX_INTERNAL void rthc_unlock(void);
12
+
13
+ MDBX_INTERNAL int rthc_register(MDBX_env *const env);
14
+ MDBX_INTERNAL int rthc_remove(MDBX_env *const env);
15
+ MDBX_INTERNAL int rthc_uniq_check(const osal_mmap_t *pending, MDBX_env **found);
16
+
17
+ /* dtor called for thread, i.e. for all mdbx's environment objects */
18
+ MDBX_INTERNAL void rthc_thread_dtor(void *rthc);
19
+
20
+ static inline void *thread_rthc_get(osal_thread_key_t key) {
21
+ #if defined(_WIN32) || defined(_WIN64)
22
+ return TlsGetValue(key);
23
+ #else
24
+ return pthread_getspecific(key);
25
+ #endif
26
+ }
27
+
28
+ MDBX_INTERNAL void thread_rthc_set(osal_thread_key_t key, const void *value);
29
+
30
+ #if !defined(_WIN32) && !defined(_WIN64)
31
+ MDBX_INTERNAL void rthc_afterfork(void);
32
+ MDBX_INTERNAL void workaround_glibc_bug21031(void);
33
+ #endif /* !Windows */
34
+
35
+ static inline void thread_key_delete(osal_thread_key_t key) {
36
+ TRACE("key = %" PRIuPTR, (uintptr_t)key);
37
+ #if defined(_WIN32) || defined(_WIN64)
38
+ ENSURE(nullptr, TlsFree(key));
39
+ #else
40
+ ENSURE(nullptr, pthread_key_delete(key) == 0);
41
+ workaround_glibc_bug21031();
42
+ #endif
43
+ }