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,607 @@
1
+ /// \copyright SPDX-License-Identifier: Apache-2.0
2
+ /// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2015-2025
3
+
4
+ #if defined(_WIN32) || defined(_WIN64)
5
+
6
+ /* PREAMBLE FOR WINDOWS:
7
+ *
8
+ * We are not concerned for performance here.
9
+ * If you are running Windows a performance could NOT be the goal.
10
+ * Otherwise please use Linux. */
11
+
12
+ #include "internals.h"
13
+
14
+ #define LCK_SHARED 0
15
+ #define LCK_EXCLUSIVE LOCKFILE_EXCLUSIVE_LOCK
16
+ #define LCK_WAITFOR 0
17
+ #define LCK_DONTWAIT LOCKFILE_FAIL_IMMEDIATELY
18
+
19
+ static int flock_with_event(HANDLE fd, HANDLE event, unsigned flags, size_t offset, size_t bytes) {
20
+ TRACE("lock>>: fd %p, event %p, flags 0x%x offset %zu, bytes %zu >>", fd, event, flags, offset, bytes);
21
+ OVERLAPPED ov;
22
+ ov.Internal = 0;
23
+ ov.InternalHigh = 0;
24
+ ov.hEvent = event;
25
+ ov.Offset = (DWORD)offset;
26
+ ov.OffsetHigh = HIGH_DWORD(offset);
27
+ if (LockFileEx(fd, flags, 0, (DWORD)bytes, HIGH_DWORD(bytes), &ov)) {
28
+ TRACE("lock<<: fd %p, event %p, flags 0x%x offset %zu, bytes %zu << %s", fd, event, flags, offset, bytes, "done");
29
+ return MDBX_SUCCESS;
30
+ }
31
+
32
+ DWORD rc = GetLastError();
33
+ if (rc == ERROR_IO_PENDING) {
34
+ if (event) {
35
+ if (GetOverlappedResult(fd, &ov, &rc, true)) {
36
+ TRACE("lock<<: fd %p, event %p, flags 0x%x offset %zu, bytes %zu << %s", fd, event, flags, offset, bytes,
37
+ "overlapped-done");
38
+ return MDBX_SUCCESS;
39
+ }
40
+ rc = GetLastError();
41
+ } else
42
+ CancelIo(fd);
43
+ }
44
+ TRACE("lock<<: fd %p, event %p, flags 0x%x offset %zu, bytes %zu << err %d", fd, event, flags, offset, bytes,
45
+ (int)rc);
46
+ return (int)rc;
47
+ }
48
+
49
+ static inline int flock(HANDLE fd, unsigned flags, size_t offset, size_t bytes) {
50
+ return flock_with_event(fd, 0, flags, offset, bytes);
51
+ }
52
+
53
+ static inline int flock_data(const MDBX_env *env, unsigned flags, size_t offset, size_t bytes) {
54
+ const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
55
+ return flock_with_event(fd4data, env->dxb_lock_event, flags, offset, bytes);
56
+ }
57
+
58
+ static int funlock(mdbx_filehandle_t fd, size_t offset, size_t bytes) {
59
+ TRACE("unlock: fd %p, offset %zu, bytes %zu", fd, offset, bytes);
60
+ return UnlockFile(fd, (DWORD)offset, HIGH_DWORD(offset), (DWORD)bytes, HIGH_DWORD(bytes)) ? MDBX_SUCCESS
61
+ : (int)GetLastError();
62
+ }
63
+
64
+ /*----------------------------------------------------------------------------*/
65
+ /* global `write` lock for write-txt processing,
66
+ * exclusive locking both meta-pages) */
67
+
68
+ #ifdef _WIN64
69
+ #define DXB_MAXLEN UINT64_C(0x7fffFFFFfff00000)
70
+ #else
71
+ #define DXB_MAXLEN UINT32_C(0x7ff00000)
72
+ #endif
73
+ #define DXB_BODY (env->ps * (size_t)NUM_METAS), DXB_MAXLEN
74
+ #define DXB_WHOLE 0, DXB_MAXLEN
75
+
76
+ int lck_txn_lock(MDBX_env *env, bool dontwait) {
77
+ if (dontwait) {
78
+ if (!TryEnterCriticalSection(&env->windowsbug_lock))
79
+ return MDBX_BUSY;
80
+ } else {
81
+ __try {
82
+ EnterCriticalSection(&env->windowsbug_lock);
83
+ } __except ((GetExceptionCode() == 0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
84
+ ? EXCEPTION_EXECUTE_HANDLER
85
+ : EXCEPTION_CONTINUE_SEARCH) {
86
+ return MDBX_EDEADLK;
87
+ }
88
+ }
89
+
90
+ eASSERT(env, !env->basal_txn || !env->basal_txn->owner);
91
+ if (env->flags & MDBX_EXCLUSIVE)
92
+ goto done;
93
+
94
+ const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
95
+ int rc = flock_with_event(fd4data, env->dxb_lock_event,
96
+ dontwait ? (LCK_EXCLUSIVE | LCK_DONTWAIT) : (LCK_EXCLUSIVE | LCK_WAITFOR), DXB_BODY);
97
+ if (rc == ERROR_LOCK_VIOLATION && dontwait) {
98
+ SleepEx(0, true);
99
+ rc = flock_with_event(fd4data, env->dxb_lock_event, LCK_EXCLUSIVE | LCK_DONTWAIT, DXB_BODY);
100
+ if (rc == ERROR_LOCK_VIOLATION) {
101
+ SleepEx(0, true);
102
+ rc = flock_with_event(fd4data, env->dxb_lock_event, LCK_EXCLUSIVE | LCK_DONTWAIT, DXB_BODY);
103
+ }
104
+ }
105
+ if (rc == MDBX_SUCCESS) {
106
+ done:
107
+ if (env->basal_txn)
108
+ env->basal_txn->owner = osal_thread_self();
109
+ /* Zap: Failing to release lock 'env->windowsbug_lock'
110
+ * in function 'mdbx_txn_lock' */
111
+ MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
112
+ return MDBX_SUCCESS;
113
+ }
114
+
115
+ LeaveCriticalSection(&env->windowsbug_lock);
116
+ return (!dontwait || rc != ERROR_LOCK_VIOLATION) ? rc : MDBX_BUSY;
117
+ }
118
+
119
+ void lck_txn_unlock(MDBX_env *env) {
120
+ eASSERT(env, !env->basal_txn || env->basal_txn->owner == osal_thread_self());
121
+ if ((env->flags & MDBX_EXCLUSIVE) == 0) {
122
+ const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
123
+ int err = funlock(fd4data, DXB_BODY);
124
+ if (err != MDBX_SUCCESS)
125
+ mdbx_panic("%s failed: err %u", __func__, err);
126
+ }
127
+ if (env->basal_txn)
128
+ env->basal_txn->owner = 0;
129
+ LeaveCriticalSection(&env->windowsbug_lock);
130
+ }
131
+
132
+ /*----------------------------------------------------------------------------*/
133
+ /* global `read` lock for readers registration,
134
+ * exclusive locking `rdt_length` (second) cacheline */
135
+
136
+ #define LCK_LO_OFFSET 0
137
+ #define LCK_LO_LEN offsetof(lck_t, rdt_length)
138
+ #define LCK_UP_OFFSET LCK_LO_LEN
139
+ #define LCK_UP_LEN (sizeof(lck_t) - LCK_UP_OFFSET)
140
+ #define LCK_LOWER LCK_LO_OFFSET, LCK_LO_LEN
141
+ #define LCK_UPPER LCK_UP_OFFSET, LCK_UP_LEN
142
+
143
+ MDBX_INTERNAL int lck_rdt_lock(MDBX_env *env) {
144
+ imports.srwl_AcquireShared(&env->remap_guard);
145
+ if (env->lck_mmap.fd == INVALID_HANDLE_VALUE)
146
+ return MDBX_SUCCESS; /* readonly database in readonly filesystem */
147
+
148
+ /* transition from S-? (used) to S-E (locked),
149
+ * e.g. exclusive lock upper-part */
150
+ if (env->flags & MDBX_EXCLUSIVE)
151
+ return MDBX_SUCCESS;
152
+
153
+ int rc = flock(env->lck_mmap.fd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER);
154
+ if (rc == MDBX_SUCCESS)
155
+ return MDBX_SUCCESS;
156
+
157
+ imports.srwl_ReleaseShared(&env->remap_guard);
158
+ return rc;
159
+ }
160
+
161
+ MDBX_INTERNAL void lck_rdt_unlock(MDBX_env *env) {
162
+ if (env->lck_mmap.fd != INVALID_HANDLE_VALUE && (env->flags & MDBX_EXCLUSIVE) == 0) {
163
+ /* transition from S-E (locked) to S-? (used), e.g. unlock upper-part */
164
+ int err = funlock(env->lck_mmap.fd, LCK_UPPER);
165
+ if (err != MDBX_SUCCESS)
166
+ mdbx_panic("%s failed: err %u", __func__, err);
167
+ }
168
+ imports.srwl_ReleaseShared(&env->remap_guard);
169
+ }
170
+
171
+ MDBX_INTERNAL int osal_lockfile(mdbx_filehandle_t fd, bool wait) {
172
+ return flock(fd, wait ? LCK_EXCLUSIVE | LCK_WAITFOR : LCK_EXCLUSIVE | LCK_DONTWAIT, 0, DXB_MAXLEN);
173
+ }
174
+
175
+ static int suspend_and_append(mdbx_handle_array_t **array, const DWORD ThreadId) {
176
+ const unsigned limit = (*array)->limit;
177
+ if ((*array)->count == limit) {
178
+ mdbx_handle_array_t *const ptr = osal_realloc(
179
+ (limit > ARRAY_LENGTH((*array)->handles)) ? *array : /* don't free initial array on the stack */ nullptr,
180
+ sizeof(mdbx_handle_array_t) + sizeof(HANDLE) * (limit * (size_t)2 - ARRAY_LENGTH((*array)->handles)));
181
+ if (!ptr)
182
+ return MDBX_ENOMEM;
183
+ if (limit == ARRAY_LENGTH((*array)->handles))
184
+ *ptr = **array;
185
+ *array = ptr;
186
+ (*array)->limit = limit * 2;
187
+ }
188
+
189
+ HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, FALSE, ThreadId);
190
+ if (hThread == nullptr)
191
+ return (int)GetLastError();
192
+
193
+ if (SuspendThread(hThread) == (DWORD)-1) {
194
+ int err = (int)GetLastError();
195
+ DWORD ExitCode;
196
+ if (err == /* workaround for Win10 UCRT bug */ ERROR_ACCESS_DENIED || !GetExitCodeThread(hThread, &ExitCode) ||
197
+ ExitCode != STILL_ACTIVE)
198
+ err = MDBX_SUCCESS;
199
+ CloseHandle(hThread);
200
+ return err;
201
+ }
202
+
203
+ (*array)->handles[(*array)->count++] = hThread;
204
+ return MDBX_SUCCESS;
205
+ }
206
+
207
+ MDBX_INTERNAL int osal_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
208
+ eASSERT(env, (env->flags & MDBX_NOSTICKYTHREADS) == 0);
209
+ const uintptr_t CurrentTid = GetCurrentThreadId();
210
+ int rc;
211
+ if (env->lck_mmap.lck) {
212
+ /* Scan LCK for threads of the current process */
213
+ const reader_slot_t *const begin = env->lck_mmap.lck->rdt;
214
+ const reader_slot_t *const end = begin + atomic_load32(&env->lck_mmap.lck->rdt_length, mo_AcquireRelease);
215
+ const uintptr_t WriteTxnOwner = env->basal_txn ? env->basal_txn->owner : 0;
216
+ for (const reader_slot_t *reader = begin; reader < end; ++reader) {
217
+ if (reader->pid.weak != env->pid || !reader->tid.weak || reader->tid.weak >= MDBX_TID_TXN_OUSTED) {
218
+ skip_lck:
219
+ continue;
220
+ }
221
+ if (reader->tid.weak == CurrentTid || reader->tid.weak == WriteTxnOwner)
222
+ goto skip_lck;
223
+
224
+ rc = suspend_and_append(array, (mdbx_tid_t)reader->tid.weak);
225
+ if (rc != MDBX_SUCCESS) {
226
+ bailout_lck:
227
+ (void)osal_resume_threads_after_remap(*array);
228
+ return rc;
229
+ }
230
+ }
231
+ if (WriteTxnOwner && WriteTxnOwner != CurrentTid) {
232
+ rc = suspend_and_append(array, (mdbx_tid_t)WriteTxnOwner);
233
+ if (rc != MDBX_SUCCESS)
234
+ goto bailout_lck;
235
+ }
236
+ } else {
237
+ /* Without LCK (i.e. read-only mode).
238
+ * Walk through a snapshot of all running threads */
239
+ eASSERT(env, env->flags & (MDBX_EXCLUSIVE | MDBX_RDONLY));
240
+ const HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
241
+ if (hSnapshot == INVALID_HANDLE_VALUE)
242
+ return (int)GetLastError();
243
+
244
+ THREADENTRY32 entry;
245
+ entry.dwSize = sizeof(THREADENTRY32);
246
+
247
+ if (!Thread32First(hSnapshot, &entry)) {
248
+ rc = (int)GetLastError();
249
+ bailout_toolhelp:
250
+ CloseHandle(hSnapshot);
251
+ (void)osal_resume_threads_after_remap(*array);
252
+ return rc;
253
+ }
254
+
255
+ do {
256
+ if (entry.th32OwnerProcessID != env->pid || entry.th32ThreadID == CurrentTid)
257
+ continue;
258
+
259
+ rc = suspend_and_append(array, entry.th32ThreadID);
260
+ if (rc != MDBX_SUCCESS)
261
+ goto bailout_toolhelp;
262
+
263
+ } while (Thread32Next(hSnapshot, &entry));
264
+
265
+ rc = (int)GetLastError();
266
+ if (rc != ERROR_NO_MORE_FILES)
267
+ goto bailout_toolhelp;
268
+ CloseHandle(hSnapshot);
269
+ }
270
+
271
+ return MDBX_SUCCESS;
272
+ }
273
+
274
+ MDBX_INTERNAL int osal_resume_threads_after_remap(mdbx_handle_array_t *array) {
275
+ int rc = MDBX_SUCCESS;
276
+ for (unsigned i = 0; i < array->count; ++i) {
277
+ const HANDLE hThread = array->handles[i];
278
+ if (ResumeThread(hThread) == (DWORD)-1) {
279
+ const int err = (int)GetLastError();
280
+ DWORD ExitCode;
281
+ if (err != /* workaround for Win10 UCRT bug */ ERROR_ACCESS_DENIED && GetExitCodeThread(hThread, &ExitCode) &&
282
+ ExitCode == STILL_ACTIVE)
283
+ rc = err;
284
+ }
285
+ CloseHandle(hThread);
286
+ }
287
+ return rc;
288
+ }
289
+
290
+ /*----------------------------------------------------------------------------*/
291
+ /* global `initial` lock for lockfile initialization,
292
+ * exclusive/shared locking first cacheline */
293
+
294
+ /* Briefly description of locking schema/algorithm:
295
+ * - Windows does not support upgrading or downgrading for file locking.
296
+ * - Therefore upgrading/downgrading is emulated by shared and exclusive
297
+ * locking of upper and lower halves.
298
+ * - In other words, we have FSM with possible 9 states,
299
+ * i.e. free/shared/exclusive x free/shared/exclusive == 9.
300
+ * Only 6 states of FSM are used, which 2 of ones are transitive.
301
+ *
302
+ * States:
303
+ * LO HI
304
+ * ?-? = free, i.e. unlocked
305
+ * S-? = used, i.e. shared lock
306
+ * E-? = exclusive-read, i.e. operational exclusive
307
+ * ?-S
308
+ * ?-E = middle (transitive state)
309
+ * S-S
310
+ * S-E = locked (transitive state)
311
+ * E-S
312
+ * E-E = exclusive-write, i.e. exclusive due (re)initialization
313
+ *
314
+ * The lck_seize() moves the locking-FSM from the initial free/unlocked
315
+ * state to the "exclusive write" (and returns MDBX_RESULT_TRUE) if possible,
316
+ * or to the "used" (and returns MDBX_RESULT_FALSE).
317
+ *
318
+ * The lck_downgrade() moves the locking-FSM from "exclusive write"
319
+ * state to the "used" (i.e. shared) state.
320
+ *
321
+ * The lck_upgrade() moves the locking-FSM from "used" (i.e. shared)
322
+ * state to the "exclusive write" state.
323
+ */
324
+
325
+ static void lck_unlock(MDBX_env *env) {
326
+ int err;
327
+
328
+ if (env->lck_mmap.fd != INVALID_HANDLE_VALUE) {
329
+ /* double `unlock` for robustly remove overlapped shared/exclusive locks */
330
+ do
331
+ err = funlock(env->lck_mmap.fd, LCK_LOWER);
332
+ while (err == MDBX_SUCCESS);
333
+ assert(err == ERROR_NOT_LOCKED || (globals.running_under_Wine && err == ERROR_LOCK_VIOLATION));
334
+ SetLastError(ERROR_SUCCESS);
335
+
336
+ do
337
+ err = funlock(env->lck_mmap.fd, LCK_UPPER);
338
+ while (err == MDBX_SUCCESS);
339
+ assert(err == ERROR_NOT_LOCKED || (globals.running_under_Wine && err == ERROR_LOCK_VIOLATION));
340
+ SetLastError(ERROR_SUCCESS);
341
+ }
342
+
343
+ const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
344
+ if (fd4data != INVALID_HANDLE_VALUE) {
345
+ /* explicitly unlock to avoid latency for other processes (windows kernel
346
+ * releases such locks via deferred queues) */
347
+ do
348
+ err = funlock(fd4data, DXB_BODY);
349
+ while (err == MDBX_SUCCESS);
350
+ assert(err == ERROR_NOT_LOCKED || (globals.running_under_Wine && err == ERROR_LOCK_VIOLATION));
351
+ SetLastError(ERROR_SUCCESS);
352
+
353
+ do
354
+ err = funlock(fd4data, DXB_WHOLE);
355
+ while (err == MDBX_SUCCESS);
356
+ assert(err == ERROR_NOT_LOCKED || (globals.running_under_Wine && err == ERROR_LOCK_VIOLATION));
357
+ SetLastError(ERROR_SUCCESS);
358
+ }
359
+ }
360
+
361
+ /* Seize state as 'exclusive-write' (E-E and returns MDBX_RESULT_TRUE)
362
+ * or as 'used' (S-? and returns MDBX_RESULT_FALSE).
363
+ * Otherwise returns an error. */
364
+ static int internal_seize_lck(HANDLE lfd) {
365
+ assert(lfd != INVALID_HANDLE_VALUE);
366
+
367
+ /* 1) now on ?-? (free), get ?-E (middle) */
368
+ jitter4testing(false);
369
+ int rc = flock(lfd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER);
370
+ if (rc != MDBX_SUCCESS) {
371
+ /* 2) something went wrong, give up */;
372
+ ERROR("%s, err %u", "?-?(free) >> ?-E(middle)", rc);
373
+ return rc;
374
+ }
375
+
376
+ /* 3) now on ?-E (middle), try E-E (exclusive-write) */
377
+ jitter4testing(false);
378
+ rc = flock(lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_LOWER);
379
+ if (rc == MDBX_SUCCESS)
380
+ return MDBX_RESULT_TRUE /* 4) got E-E (exclusive-write), done */;
381
+
382
+ /* 5) still on ?-E (middle) */
383
+ jitter4testing(false);
384
+ if (rc != ERROR_SHARING_VIOLATION && rc != ERROR_LOCK_VIOLATION) {
385
+ /* 6) something went wrong, give up */
386
+ rc = funlock(lfd, LCK_UPPER);
387
+ if (rc != MDBX_SUCCESS)
388
+ mdbx_panic("%s(%s) failed: err %u", __func__, "?-E(middle) >> ?-?(free)", rc);
389
+ return rc;
390
+ }
391
+
392
+ /* 7) still on ?-E (middle), try S-E (locked) */
393
+ jitter4testing(false);
394
+ rc = flock(lfd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER);
395
+
396
+ jitter4testing(false);
397
+ if (rc != MDBX_SUCCESS)
398
+ ERROR("%s, err %u", "?-E(middle) >> S-E(locked)", rc);
399
+
400
+ /* 8) now on S-E (locked) or still on ?-E (middle),
401
+ * transition to S-? (used) or ?-? (free) */
402
+ int err = funlock(lfd, LCK_UPPER);
403
+ if (err != MDBX_SUCCESS)
404
+ mdbx_panic("%s(%s) failed: err %u", __func__, "X-E(locked/middle) >> X-?(used/free)", err);
405
+
406
+ /* 9) now on S-? (used, DONE) or ?-? (free, FAILURE) */
407
+ return rc;
408
+ }
409
+
410
+ MDBX_INTERNAL int lck_seize(MDBX_env *env) {
411
+ const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
412
+ assert(fd4data != INVALID_HANDLE_VALUE);
413
+ if (env->flags & MDBX_EXCLUSIVE)
414
+ return MDBX_RESULT_TRUE /* nope since files were must be opened
415
+ non-shareable */
416
+ ;
417
+
418
+ if (env->lck_mmap.fd == INVALID_HANDLE_VALUE) {
419
+ /* LY: without-lck mode (e.g. on read-only filesystem) */
420
+ jitter4testing(false);
421
+ int rc = flock_data(env, LCK_SHARED | LCK_DONTWAIT, DXB_WHOLE);
422
+ if (rc != MDBX_SUCCESS)
423
+ ERROR("%s, err %u", "without-lck", rc);
424
+ return rc;
425
+ }
426
+
427
+ int rc = internal_seize_lck(env->lck_mmap.fd);
428
+ jitter4testing(false);
429
+ if (rc == MDBX_RESULT_TRUE && (env->flags & MDBX_RDONLY) == 0) {
430
+ /* Check that another process don't operates in without-lck mode.
431
+ * Doing such check by exclusive locking the body-part of db. Should be
432
+ * noted:
433
+ * - we need an exclusive lock for do so;
434
+ * - we can't lock meta-pages, otherwise other process could get an error
435
+ * while opening db in valid (non-conflict) mode. */
436
+ int err = flock_data(env, LCK_EXCLUSIVE | LCK_DONTWAIT, DXB_WHOLE);
437
+ if (err != MDBX_SUCCESS) {
438
+ ERROR("%s, err %u", "lock-against-without-lck", err);
439
+ jitter4testing(false);
440
+ lck_unlock(env);
441
+ return err;
442
+ }
443
+ jitter4testing(false);
444
+ err = funlock(fd4data, DXB_WHOLE);
445
+ if (err != MDBX_SUCCESS)
446
+ mdbx_panic("%s(%s) failed: err %u", __func__, "unlock-against-without-lck", err);
447
+ }
448
+
449
+ return rc;
450
+ }
451
+
452
+ MDBX_INTERNAL int lck_downgrade(MDBX_env *env) {
453
+ const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
454
+ /* Transite from exclusive-write state (E-E) to used (S-?) */
455
+ assert(fd4data != INVALID_HANDLE_VALUE);
456
+ assert(env->lck_mmap.fd != INVALID_HANDLE_VALUE);
457
+
458
+ if (env->flags & MDBX_EXCLUSIVE)
459
+ return MDBX_SUCCESS /* nope since files were must be opened non-shareable */
460
+ ;
461
+ /* 1) now at E-E (exclusive-write), transition to ?_E (middle) */
462
+ int rc = funlock(env->lck_mmap.fd, LCK_LOWER);
463
+ if (rc != MDBX_SUCCESS)
464
+ mdbx_panic("%s(%s) failed: err %u", __func__, "E-E(exclusive-write) >> ?-E(middle)", rc);
465
+
466
+ /* 2) now at ?-E (middle), transition to S-E (locked) */
467
+ rc = flock(env->lck_mmap.fd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER);
468
+ if (rc != MDBX_SUCCESS) {
469
+ /* 3) something went wrong, give up */;
470
+ ERROR("%s, err %u", "?-E(middle) >> S-E(locked)", rc);
471
+ return rc;
472
+ }
473
+
474
+ /* 4) got S-E (locked), continue transition to S-? (used) */
475
+ rc = funlock(env->lck_mmap.fd, LCK_UPPER);
476
+ if (rc != MDBX_SUCCESS)
477
+ mdbx_panic("%s(%s) failed: err %u", __func__, "S-E(locked) >> S-?(used)", rc);
478
+
479
+ return MDBX_SUCCESS /* 5) now at S-? (used), done */;
480
+ }
481
+
482
+ MDBX_INTERNAL int lck_upgrade(MDBX_env *env, bool dont_wait) {
483
+ /* Transite from used state (S-?) to exclusive-write (E-E) */
484
+ assert(env->lck_mmap.fd != INVALID_HANDLE_VALUE);
485
+
486
+ if (env->flags & MDBX_EXCLUSIVE)
487
+ return MDBX_SUCCESS /* nope since files were must be opened non-shareable */
488
+ ;
489
+
490
+ /* 1) now on S-? (used), try S-E (locked) */
491
+ jitter4testing(false);
492
+ int rc = flock(env->lck_mmap.fd, dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE, LCK_UPPER);
493
+ if (rc != MDBX_SUCCESS) {
494
+ /* 2) something went wrong, give up */;
495
+ VERBOSE("%s, err %u", "S-?(used) >> S-E(locked)", rc);
496
+ return rc;
497
+ }
498
+
499
+ /* 3) now on S-E (locked), transition to ?-E (middle) */
500
+ rc = funlock(env->lck_mmap.fd, LCK_LOWER);
501
+ if (rc != MDBX_SUCCESS)
502
+ mdbx_panic("%s(%s) failed: err %u", __func__, "S-E(locked) >> ?-E(middle)", rc);
503
+
504
+ /* 4) now on ?-E (middle), try E-E (exclusive-write) */
505
+ jitter4testing(false);
506
+ rc = flock(env->lck_mmap.fd, dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE, LCK_LOWER);
507
+ if (rc != MDBX_SUCCESS) {
508
+ /* 5) something went wrong, give up */;
509
+ VERBOSE("%s, err %u", "?-E(middle) >> E-E(exclusive-write)", rc);
510
+ return rc;
511
+ }
512
+
513
+ return MDBX_SUCCESS /* 6) now at E-E (exclusive-write), done */;
514
+ }
515
+
516
+ MDBX_INTERNAL int lck_init(MDBX_env *env, MDBX_env *inprocess_neighbor, int global_uniqueness_flag) {
517
+ (void)env;
518
+ (void)inprocess_neighbor;
519
+ (void)global_uniqueness_flag;
520
+ if (imports.SetFileIoOverlappedRange && !(env->flags & MDBX_RDONLY)) {
521
+ HANDLE token = INVALID_HANDLE_VALUE;
522
+ TOKEN_PRIVILEGES privileges;
523
+ privileges.PrivilegeCount = 1;
524
+ privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
525
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token) ||
526
+ !LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &privileges.Privileges[0].Luid) ||
527
+ !AdjustTokenPrivileges(token, FALSE, &privileges, sizeof(privileges), nullptr, nullptr) ||
528
+ GetLastError() != ERROR_SUCCESS)
529
+ imports.SetFileIoOverlappedRange = nullptr;
530
+
531
+ if (token != INVALID_HANDLE_VALUE)
532
+ CloseHandle(token);
533
+ }
534
+ return MDBX_SUCCESS;
535
+ }
536
+
537
+ MDBX_INTERNAL int lck_destroy(MDBX_env *env, MDBX_env *inprocess_neighbor, const uint32_t current_pid) {
538
+ (void)current_pid;
539
+ /* LY: should unmap before releasing the locks to avoid race condition and
540
+ * STATUS_USER_MAPPED_FILE/ERROR_USER_MAPPED_FILE */
541
+ if (env->dxb_mmap.base)
542
+ osal_munmap(&env->dxb_mmap);
543
+ if (env->lck_mmap.lck) {
544
+ const bool synced = env->lck_mmap.lck->unsynced_pages.weak == 0;
545
+ osal_munmap(&env->lck_mmap);
546
+ if (synced && !inprocess_neighbor && env->lck_mmap.fd != INVALID_HANDLE_VALUE &&
547
+ lck_upgrade(env, true) == MDBX_SUCCESS)
548
+ /* this will fail if LCK is used/mmapped by other process(es) */
549
+ osal_ftruncate(env->lck_mmap.fd, 0);
550
+ }
551
+ lck_unlock(env);
552
+ return MDBX_SUCCESS;
553
+ }
554
+
555
+ /*----------------------------------------------------------------------------*/
556
+ /* reader checking (by pid) */
557
+
558
+ MDBX_INTERNAL int lck_rpid_set(MDBX_env *env) {
559
+ (void)env;
560
+ return MDBX_SUCCESS;
561
+ }
562
+
563
+ MDBX_INTERNAL int lck_rpid_clear(MDBX_env *env) {
564
+ (void)env;
565
+ return MDBX_SUCCESS;
566
+ }
567
+
568
+ /* Checks reader by pid.
569
+ *
570
+ * Returns:
571
+ * MDBX_RESULT_TRUE, if pid is live (unable to acquire lock)
572
+ * MDBX_RESULT_FALSE, if pid is dead (lock acquired)
573
+ * or otherwise the errcode. */
574
+ MDBX_INTERNAL int lck_rpid_check(MDBX_env *env, uint32_t pid) {
575
+ (void)env;
576
+ HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid);
577
+ int rc;
578
+ if (likely(hProcess)) {
579
+ rc = WaitForSingleObject(hProcess, 0);
580
+ if (unlikely(rc == (int)WAIT_FAILED))
581
+ rc = (int)GetLastError();
582
+ CloseHandle(hProcess);
583
+ } else {
584
+ rc = (int)GetLastError();
585
+ }
586
+
587
+ switch (rc) {
588
+ case ERROR_INVALID_PARAMETER:
589
+ /* pid seems invalid */
590
+ return MDBX_RESULT_FALSE;
591
+ case WAIT_OBJECT_0:
592
+ /* process just exited */
593
+ return MDBX_RESULT_FALSE;
594
+ case ERROR_ACCESS_DENIED:
595
+ /* The ERROR_ACCESS_DENIED would be returned for CSRSS-processes, etc.
596
+ * assume pid exists */
597
+ return MDBX_RESULT_TRUE;
598
+ case WAIT_TIMEOUT:
599
+ /* pid running */
600
+ return MDBX_RESULT_TRUE;
601
+ default:
602
+ /* failure */
603
+ return rc;
604
+ }
605
+ }
606
+
607
+ #endif /* Windows */