@ohos-ports/lmdb 2.8.6-beta.0

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 (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +580 -0
  3. package/SECURITY.md +12 -0
  4. package/bin/download-prebuilds.js +15 -0
  5. package/binding.gyp +113 -0
  6. package/build/Release/lmdb.node +0 -0
  7. package/caching.js +212 -0
  8. package/dependencies/lmdb/libraries/liblmdb/COPYRIGHT +20 -0
  9. package/dependencies/lmdb/libraries/liblmdb/Doxyfile +1631 -0
  10. package/dependencies/lmdb/libraries/liblmdb/LICENSE +47 -0
  11. package/dependencies/lmdb/libraries/liblmdb/Makefile +136 -0
  12. package/dependencies/lmdb/libraries/liblmdb/chacha8.c +183 -0
  13. package/dependencies/lmdb/libraries/liblmdb/chacha8.h +14 -0
  14. package/dependencies/lmdb/libraries/liblmdb/crypto.c +121 -0
  15. package/dependencies/lmdb/libraries/liblmdb/intro.doc +192 -0
  16. package/dependencies/lmdb/libraries/liblmdb/lmdb.h +1817 -0
  17. package/dependencies/lmdb/libraries/liblmdb/mdb.c +12555 -0
  18. package/dependencies/lmdb/libraries/liblmdb/mdb_copy.1 +74 -0
  19. package/dependencies/lmdb/libraries/liblmdb/mdb_copy.c +106 -0
  20. package/dependencies/lmdb/libraries/liblmdb/mdb_drop.1 +53 -0
  21. package/dependencies/lmdb/libraries/liblmdb/mdb_drop.c +154 -0
  22. package/dependencies/lmdb/libraries/liblmdb/mdb_dump.1 +94 -0
  23. package/dependencies/lmdb/libraries/liblmdb/mdb_dump.c +333 -0
  24. package/dependencies/lmdb/libraries/liblmdb/mdb_load.1 +97 -0
  25. package/dependencies/lmdb/libraries/liblmdb/mdb_load.c +530 -0
  26. package/dependencies/lmdb/libraries/liblmdb/mdb_stat.1 +83 -0
  27. package/dependencies/lmdb/libraries/liblmdb/mdb_stat.c +276 -0
  28. package/dependencies/lmdb/libraries/liblmdb/midl-sparse.c +452 -0
  29. package/dependencies/lmdb/libraries/liblmdb/midl.c +452 -0
  30. package/dependencies/lmdb/libraries/liblmdb/midl.h +208 -0
  31. package/dependencies/lmdb/libraries/liblmdb/module.c +101 -0
  32. package/dependencies/lmdb/libraries/liblmdb/module.h +16 -0
  33. package/dependencies/lmdb/libraries/liblmdb/mtest.c +178 -0
  34. package/dependencies/lmdb/libraries/liblmdb/mtest2.c +124 -0
  35. package/dependencies/lmdb/libraries/liblmdb/mtest3.c +133 -0
  36. package/dependencies/lmdb/libraries/liblmdb/mtest4.c +168 -0
  37. package/dependencies/lmdb/libraries/liblmdb/mtest5.c +135 -0
  38. package/dependencies/lmdb/libraries/liblmdb/mtest6.c +141 -0
  39. package/dependencies/lmdb/libraries/liblmdb/mtest_enc.c +190 -0
  40. package/dependencies/lmdb/libraries/liblmdb/mtest_enc2.c +189 -0
  41. package/dependencies/lmdb/libraries/liblmdb/mtest_remap.c +177 -0
  42. package/dependencies/lmdb/libraries/liblmdb/sample-bdb.txt +73 -0
  43. package/dependencies/lmdb/libraries/liblmdb/sample-mdb.txt +62 -0
  44. package/dependencies/lmdb/libraries/liblmdb/tooltag +27 -0
  45. package/dependencies/lmdb-data-v1/libraries/liblmdb/CHANGES +266 -0
  46. package/dependencies/lmdb-data-v1/libraries/liblmdb/COPYRIGHT +20 -0
  47. package/dependencies/lmdb-data-v1/libraries/liblmdb/Doxyfile +1631 -0
  48. package/dependencies/lmdb-data-v1/libraries/liblmdb/LICENSE +47 -0
  49. package/dependencies/lmdb-data-v1/libraries/liblmdb/intro.doc +192 -0
  50. package/dependencies/lmdb-data-v1/libraries/liblmdb/lmdb.h +1608 -0
  51. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb.c +10320 -0
  52. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.1 +55 -0
  53. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.c +82 -0
  54. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.1 +75 -0
  55. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.c +319 -0
  56. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.1 +84 -0
  57. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.c +496 -0
  58. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.1 +64 -0
  59. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.c +263 -0
  60. package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.c +359 -0
  61. package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.h +186 -0
  62. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest.c +177 -0
  63. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest2.c +124 -0
  64. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest3.c +133 -0
  65. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest4.c +168 -0
  66. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest5.c +135 -0
  67. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest6.c +141 -0
  68. package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-bdb.txt +73 -0
  69. package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-mdb.txt +62 -0
  70. package/dependencies/lmdb-data-v1/libraries/liblmdb/tooltag +22 -0
  71. package/dependencies/lz4/LICENSE +11 -0
  72. package/dependencies/lz4/lib/LICENSE +24 -0
  73. package/dependencies/lz4/lib/Makefile +225 -0
  74. package/dependencies/lz4/lib/README.md +169 -0
  75. package/dependencies/lz4/lib/dll/example/Makefile +63 -0
  76. package/dependencies/lz4/lib/dll/example/README.md +69 -0
  77. package/dependencies/lz4/lib/dll/example/fullbench-dll.sln +25 -0
  78. package/dependencies/lz4/lib/dll/example/fullbench-dll.vcxproj +182 -0
  79. package/dependencies/lz4/lib/dll/liblz4.def +62 -0
  80. package/dependencies/lz4/lib/liblz4-dll.rc.in +35 -0
  81. package/dependencies/lz4/lib/liblz4.pc.in +14 -0
  82. package/dependencies/lz4/lib/lz4.c +2722 -0
  83. package/dependencies/lz4/lib/lz4.h +842 -0
  84. package/dependencies/lz4/lib/lz4file.c +311 -0
  85. package/dependencies/lz4/lib/lz4file.h +93 -0
  86. package/dependencies/lz4/lib/lz4frame.c +2078 -0
  87. package/dependencies/lz4/lib/lz4frame.h +692 -0
  88. package/dependencies/lz4/lib/lz4frame_static.h +47 -0
  89. package/dependencies/lz4/lib/lz4hc.c +1631 -0
  90. package/dependencies/lz4/lib/lz4hc.h +413 -0
  91. package/dependencies/lz4/lib/xxhash.c +1030 -0
  92. package/dependencies/lz4/lib/xxhash.h +328 -0
  93. package/dependencies/v8/v8-fast-api-calls-v16.h +791 -0
  94. package/dependencies/v8/v8-fast-api-calls.h +888 -0
  95. package/dict/dict.txt +1 -0
  96. package/dict/dict2.txt +1 -0
  97. package/dist/index.cjs +2950 -0
  98. package/dist/index.cjs.map +1 -0
  99. package/index.d.cts +420 -0
  100. package/index.d.ts +450 -0
  101. package/index.js +38 -0
  102. package/keys.js +116 -0
  103. package/level.js +27 -0
  104. package/native.js +85 -0
  105. package/node-index.js +32 -0
  106. package/open.js +448 -0
  107. package/package.json +124 -0
  108. package/read.js +833 -0
  109. package/rollup.config.js +12 -0
  110. package/src/compression.cpp +225 -0
  111. package/src/cursor.cpp +395 -0
  112. package/src/dbi.cpp +382 -0
  113. package/src/env.cpp +1030 -0
  114. package/src/lmdb-js.cpp +64 -0
  115. package/src/lmdb-js.h +633 -0
  116. package/src/misc.cpp +593 -0
  117. package/src/ordered-binary.cpp +46 -0
  118. package/src/txn.cpp +210 -0
  119. package/src/v8-functions.cpp +170 -0
  120. package/src/windows.c +35 -0
  121. package/src/writer.cpp +537 -0
  122. package/util/RangeIterable.js +236 -0
  123. package/util/set-optional-deps.cjs +14 -0
  124. package/util/when.js +8 -0
  125. package/write.js +912 -0
package/src/writer.cpp ADDED
@@ -0,0 +1,537 @@
1
+ /* write instructions
2
+
3
+ 0-3 flags
4
+ 4-7 dbi
5
+ 8-11 key-size
6
+ 12 ... key followed by at least 2 32-bit zeros
7
+ 4 value-size
8
+ 8 bytes: value pointer (or value itself)
9
+ 8 compressor pointer?
10
+ 8 bytes (optional): conditional version
11
+ 8 bytes (optional): version
12
+ inline value?
13
+ */
14
+ #include "lmdb-js.h"
15
+ #include <atomic>
16
+ #include <ctime>
17
+ #ifndef _WIN32
18
+ #include <unistd.h>
19
+ #endif
20
+
21
+ // flags:
22
+ const uint32_t NO_INSTRUCTION_YET = 0;
23
+ const int PUT = 15;
24
+ const int DEL = 13;
25
+ const int DEL_VALUE = 14;
26
+ const int START_CONDITION_BLOCK = 4;
27
+ //const int START_CONDITION_VALUE_BLOCK = 6;
28
+ const int START_BLOCK = 1;
29
+ const int BLOCK_END = 2;
30
+ const int POINTER_NEXT = 3;
31
+ const int USER_CALLBACK = 8;
32
+ const int USER_CALLBACK_STRICT_ORDER = 0x100000;
33
+ const int DROP_DB = 12;
34
+ const int HAS_KEY = 4;
35
+ const int HAS_VALUE = 2;
36
+ const int CONDITIONAL = 8;
37
+ const int CONDITIONAL_VERSION = 0x100;
38
+ const int CONDITIONAL_VERSION_LESS_THAN = 0x800;
39
+ const int CONDITIONAL_ALLOW_NOTFOUND = 0x1000;
40
+ const int SET_VERSION = 0x200;
41
+ //const int HAS_INLINE_VALUE = 0x400;
42
+ const int COMPRESSIBLE = 0x100000;
43
+ const int DELETE_DATABASE = 0x400;
44
+ const int TXN_HAD_ERROR = 0x40000000;
45
+ const int TXN_DELIMITER = 0x8000000;
46
+ const int TXN_COMMITTED = 0x10000000;
47
+ //const int TXN_FLUSHED = 0x20000000;
48
+ const int WAITING_OPERATION = 0x2000000;
49
+ const int IF_NO_EXISTS = MDB_NOOVERWRITE; //0x10;
50
+ // result codes:
51
+ const int FAILED_CONDITION = 0x4000000;
52
+ const int FINISHED_OPERATION = 0x1000000;
53
+ const double ANY_VERSION = 3.542694326329068e-103; // special marker for any version
54
+
55
+
56
+ WriteWorker::~WriteWorker() {
57
+ // TODO: Make sure this runs on the JS main thread, or we need to move it
58
+ if (envForTxn->writeWorker == this)
59
+ envForTxn->writeWorker = nullptr;
60
+ }
61
+
62
+ WriteWorker::WriteWorker(MDB_env* env, EnvWrap* envForTxn, uint32_t* instructions)
63
+ : envForTxn(envForTxn),
64
+ instructions(instructions),
65
+ env(env) {
66
+ //fprintf(stdout, "nextCompressibleArg %p\n", nextCompressibleArg);
67
+ interruptionStatus = 0;
68
+ resultCode = 0;
69
+ txn = nullptr;
70
+ }
71
+
72
+ void WriteWorker::SendUpdate() {
73
+ if (WriteWorker::threadSafeCallsEnabled)
74
+ napi_call_threadsafe_function(progress, nullptr, napi_tsfn_blocking);
75
+ }
76
+ MDB_txn* WriteWorker::AcquireTxn(int* flags) {
77
+ bool commitSynchronously = *flags & TXN_SYNCHRONOUS_COMMIT;
78
+
79
+ // TODO: if the conditionDepth is 0, we could allow the current worker's txn to be continued, committed and restarted
80
+ pthread_mutex_lock(envForTxn->writingLock);
81
+ retry:
82
+ if (commitSynchronously && interruptionStatus == WORKER_WAITING) {
83
+ interruptionStatus = INTERRUPT_BATCH;
84
+ pthread_cond_signal(envForTxn->writingCond);
85
+ pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
86
+ if (interruptionStatus == RESTART_WORKER_TXN) {
87
+ *flags |= TXN_FROM_WORKER;
88
+ return nullptr;
89
+ } else if (interruptionStatus == WORKER_WAITING || interruptionStatus == INTERRUPT_BATCH) {
90
+ interruptionStatus = WORKER_WAITING;
91
+ goto retry;
92
+ } else {
93
+ return nullptr;
94
+ }
95
+ } else {
96
+ //if (interruptionStatus == RESTART_WORKER_TXN)
97
+ // pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
98
+ interruptionStatus = USER_HAS_LOCK;
99
+ *flags |= TXN_FROM_WORKER;
100
+ //if (txn)
101
+ //fprintf(stderr, "acquire lock from worker %p %u\n", txn, commitSynchronously);
102
+ return txn;
103
+ }
104
+ }
105
+
106
+ void WriteWorker::UnlockTxn() {
107
+ interruptionStatus = 0;
108
+ pthread_cond_signal(envForTxn->writingCond);
109
+ pthread_mutex_unlock(envForTxn->writingLock);
110
+ }
111
+ int WriteWorker::WaitForCallbacks(MDB_txn** txn, bool allowCommit, uint32_t* target) {
112
+ int rc;
113
+ if (!finishedProgress)
114
+ SendUpdate();
115
+ pthread_cond_signal(envForTxn->writingCond);
116
+ interruptionStatus = WORKER_WAITING;
117
+ uint64_t start;
118
+ if (envForTxn->trackMetrics)
119
+ start = get_time64();
120
+ if (target) {
121
+ uint64_t delay = 1;
122
+ do {
123
+ cond_timedwait(envForTxn->writingCond, envForTxn->writingLock, delay);
124
+ delay = delay << 1ll;
125
+ if ((*target & 0xf) || (allowCommit && finishedProgress)) {
126
+ // we are in position to continue writing or commit, so forward progress can be made without interrupting yet
127
+ if (envForTxn->trackMetrics) {
128
+ envForTxn->timeTxnWaiting += get_time64() - start;
129
+ }
130
+ interruptionStatus = 0;
131
+ return 0;
132
+ }
133
+ } while(interruptionStatus != INTERRUPT_BATCH);
134
+ } else {
135
+ pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
136
+ }
137
+ if (envForTxn->trackMetrics) {
138
+ envForTxn->timeTxnWaiting += get_time64() - start;
139
+ }
140
+ if (interruptionStatus == INTERRUPT_BATCH) { // interrupted by JS code that wants to run a synchronous transaction
141
+ interruptionStatus = RESTART_WORKER_TXN;
142
+ rc = mdb_txn_commit(*txn);
143
+ #ifdef MDB_EMPTY_TXN
144
+ if (rc == MDB_EMPTY_TXN)
145
+ rc = 0;
146
+ #endif
147
+ if (rc == 0) {
148
+ // wait again until the sync transaction is completed
149
+ this->txn = *txn = nullptr;
150
+ pthread_cond_signal(envForTxn->writingCond);
151
+ pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
152
+ // now restart our transaction
153
+ rc = mdb_txn_begin(env, nullptr, 0, txn);
154
+ this->txn = *txn;
155
+ //fprintf(stderr, "Restarted txn after interruption\n");
156
+ interruptionStatus = 0;
157
+ }
158
+ if (rc != 0) {
159
+ fprintf(stdout, "wfc unlock due to error %u\n", rc);
160
+ return rc;
161
+ }
162
+ } else
163
+ interruptionStatus = 0;
164
+ return 0;
165
+ }
166
+ int WriteWorker::DoWrites(MDB_txn* txn, EnvWrap* envForTxn, uint32_t* instruction, WriteWorker* worker) {
167
+ MDB_val key, value;
168
+ int rc = 0;
169
+ int conditionDepth = 0;
170
+ int validatedDepth = 0;
171
+ double conditionalVersion, setVersion = 0;
172
+ bool overlappedWord = !!worker;
173
+ uint32_t* start;
174
+ do {
175
+ next_inst: start = instruction++;
176
+ uint32_t flags = *start;
177
+ MDB_dbi dbi = 0;
178
+ //fprintf(stderr, "do %u %u\n", flags, get_time64());
179
+ bool validated = conditionDepth == validatedDepth;
180
+ if (flags & 0xf0c0) {
181
+ fprintf(stderr, "Unknown flag bits %u %p\n", flags, start);
182
+ fprintf(stderr, "flags after message %u\n", *start);
183
+ worker->resultCode = 22;
184
+ return 0;
185
+ }
186
+ if (flags & HAS_KEY) {
187
+ // a key based instruction, get the key
188
+ dbi = (MDB_dbi) *instruction++;
189
+ key.mv_size = *instruction++;
190
+ key.mv_data = instruction;
191
+ instruction = (uint32_t*) (((size_t) instruction + key.mv_size + 16) & (~7));
192
+ if (flags & HAS_VALUE) {
193
+ if (flags & COMPRESSIBLE) {
194
+ int64_t status = -1;
195
+ status = std::atomic_exchange((std::atomic<int64_t>*)(instruction + 2), (int64_t)1);
196
+ if (status == 2) {
197
+ //fprintf(stderr, "wait on compression %p\n", instruction);
198
+ worker->interruptionStatus = WORKER_WAITING;
199
+ do {
200
+ pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
201
+ } while (std::atomic_load((std::atomic<int64_t>*)(instruction + 2)));
202
+ worker->interruptionStatus = 0;
203
+ } else if (status > 2) {
204
+ //fprintf(stderr, "doing the compression ourselves\n");
205
+ ((Compression*) (size_t) *((double*)&status))->compressInstruction(nullptr, (double*) (instruction + 2));
206
+ } // else status is 0 and compression is done
207
+ // compressed
208
+ value.mv_data = (void*)(size_t) * ((size_t*)instruction);
209
+ if ((size_t)value.mv_data > 0x1000000000000)
210
+ fprintf(stderr, "compression not completed %p %i\n", value.mv_data, (int) status);
211
+ value.mv_size = *(instruction - 1);
212
+ instruction += 4; // skip compression pointers
213
+ } else {
214
+ value.mv_data = (void*)(size_t) * ((double*)instruction);
215
+ value.mv_size = *(instruction - 1);
216
+ instruction += 2;
217
+ }
218
+ }
219
+ if (flags & CONDITIONAL_VERSION) {
220
+ conditionalVersion = *((double*) instruction);
221
+ instruction += 2;
222
+ MDB_val conditionalValue;
223
+ rc = mdb_get(txn, dbi, &key, &conditionalValue);
224
+ if (rc) {
225
+ // not found counts as version 0, so this is acceptable for conditional less than,
226
+ // otherwise does not validate
227
+ if (rc == MDB_NOTFOUND)
228
+ validated = flags & CONDITIONAL_ALLOW_NOTFOUND;
229
+ else
230
+ worker->resultCode = rc;
231
+ } else if (conditionalVersion != ANY_VERSION) {
232
+ double version;
233
+ memcpy(&version, conditionalValue.mv_data, 8);
234
+ validated = validated && ((flags & CONDITIONAL_VERSION_LESS_THAN) ? version <= conditionalVersion : (version == conditionalVersion));
235
+ }
236
+ }
237
+ if (flags & SET_VERSION) {
238
+ setVersion = *((double*) instruction);
239
+ instruction += 2;
240
+ }
241
+ if ((flags & IF_NO_EXISTS) && (flags & START_CONDITION_BLOCK)) {
242
+ rc = mdb_get(txn, dbi, &key, &value);
243
+ if (!rc)
244
+ validated = false;
245
+ else if (rc == MDB_NOTFOUND)
246
+ validated = true;
247
+ else
248
+ worker->resultCode = rc;
249
+ }
250
+ } else
251
+ instruction++;
252
+ //fprintf(stderr, "instr flags %p %p %u\n", start, flags, conditionDepth);
253
+ if (validated || !(flags & CONDITIONAL)) {
254
+ switch (flags & 0xf) {
255
+ case NO_INSTRUCTION_YET:
256
+ instruction -= 2; // reset back to the previous flag as the current instruction
257
+ rc = 0;
258
+ // in windows InterlockedCompareExchange might be faster
259
+ if (!worker->finishedProgress || conditionDepth) {
260
+ if (std::atomic_compare_exchange_strong((std::atomic<uint32_t>*) start,
261
+ (uint32_t*) &flags,
262
+ (uint32_t)WAITING_OPERATION)) {
263
+ worker->WaitForCallbacks(&txn, conditionDepth == 0, start);
264
+ }
265
+ goto next_inst;
266
+ } else {
267
+ if (std::atomic_compare_exchange_strong((std::atomic<uint32_t>*) start,
268
+ (uint32_t*) &flags,
269
+ (uint32_t)TXN_DELIMITER)) {
270
+ worker->instructions = start;
271
+ return 0;
272
+ } else
273
+ goto next_inst;
274
+ }
275
+ case BLOCK_END:
276
+ conditionDepth--;
277
+ if (validatedDepth > conditionDepth)
278
+ validatedDepth--;
279
+ if (conditionDepth < 0) {
280
+ fprintf(stderr, "Negative condition depth");
281
+ }
282
+ goto next_inst;
283
+ case PUT:
284
+ if (flags & SET_VERSION)
285
+ rc = putWithVersion(txn, dbi, &key, &value, flags & (MDB_NOOVERWRITE | MDB_NODUPDATA | MDB_APPEND | MDB_APPENDDUP), setVersion);
286
+ else
287
+ rc = mdb_put(txn, dbi, &key, &value, flags & (MDB_NOOVERWRITE | MDB_NODUPDATA | MDB_APPEND | MDB_APPENDDUP));
288
+ if (flags & COMPRESSIBLE)
289
+ delete value.mv_data;
290
+ //fprintf(stdout, "put %u \n", key.mv_size);
291
+ break;
292
+ case DEL:
293
+ rc = mdb_del(txn, dbi, &key, nullptr);
294
+ break;
295
+ case DEL_VALUE:
296
+ rc = mdb_del(txn, dbi, &key, &value);
297
+ if (flags & COMPRESSIBLE)
298
+ delete value.mv_data;
299
+ break;
300
+ case START_BLOCK: case START_CONDITION_BLOCK:
301
+ rc = validated ? 0 : MDB_NOTFOUND;
302
+ if (validated)
303
+ validatedDepth++;
304
+ conditionDepth++;
305
+ break;
306
+ case USER_CALLBACK:
307
+ worker->finishedProgress = false;
308
+ worker->progressStatus = 2;
309
+ rc = 0;
310
+ if (flags & USER_CALLBACK_STRICT_ORDER) {
311
+ std::atomic_fetch_or((std::atomic<uint32_t>*) start, (uint32_t) FINISHED_OPERATION); // mark it as finished so it is processed
312
+ while (!worker->finishedProgress) {
313
+ worker->WaitForCallbacks(&txn, conditionDepth == 0, nullptr);
314
+ }
315
+ }
316
+ break;
317
+ case DROP_DB:
318
+ rc = mdb_drop(txn, dbi, (flags & DELETE_DATABASE) ? 1 : 0);
319
+ break;
320
+ case POINTER_NEXT:
321
+ instruction = (uint32_t*)(size_t) * ((double*)instruction);
322
+ goto next_inst;
323
+ default:
324
+ fprintf(stderr, "Unknown flags %u %p\n", flags, start);
325
+ fprintf(stderr, "flags after message %u\n", *start);
326
+ worker->resultCode = 22;
327
+ return 22;
328
+ }
329
+ if (rc) {
330
+ if (!(rc == MDB_KEYEXIST || rc == MDB_NOTFOUND)) {
331
+ if (worker) {
332
+ worker->resultCode = rc;
333
+ } else {
334
+ return rc;
335
+ }
336
+ }
337
+ flags = FINISHED_OPERATION | FAILED_CONDITION;
338
+ }
339
+ else
340
+ flags = FINISHED_OPERATION;
341
+ } else
342
+ flags = FINISHED_OPERATION | FAILED_CONDITION;
343
+ //fprintf(stderr, "finished flag %p\n", flags);
344
+ if (overlappedWord) {
345
+ std::atomic_fetch_or((std::atomic<uint32_t>*) start, flags);
346
+ overlappedWord = false;
347
+ } else
348
+ *start |= flags;
349
+ } while(worker); // keep iterating in async/multiple-instruction mode, just one instruction in sync mode
350
+ return rc;
351
+ }
352
+
353
+ bool WriteWorker::threadSafeCallsEnabled = false;
354
+ void txn_visible(const void* data) {
355
+ auto worker = (WriteWorker*) data;
356
+ worker->SendUpdate();
357
+ }
358
+
359
+
360
+ void do_write(napi_env env, void* data) {
361
+ auto worker = (WriteWorker*) data;
362
+ worker->Write();
363
+ napi_release_threadsafe_function(worker->progress, napi_tsfn_abort);
364
+ }
365
+
366
+ const int READER_CHECK_INTERVAL = 600; // ten minutes
367
+ void WriteWorker::Write() {
368
+ int rc;
369
+ finishedProgress = true;
370
+ unsigned int envFlags;
371
+ mdb_env_get_flags(env, &envFlags);
372
+ time_t now = time(0);
373
+ if (now - envForTxn->lastReaderCheck > READER_CHECK_INTERVAL) {
374
+ int dead;
375
+ mdb_reader_check(env, &dead);
376
+ envForTxn->lastReaderCheck = now;
377
+ }
378
+ pthread_mutex_lock(envForTxn->writingLock);
379
+ #ifndef _WIN32
380
+ int retries = 0;
381
+ retry:
382
+ #endif
383
+ rc = mdb_txn_begin(env, nullptr,
384
+ #ifdef MDB_OVERLAPPINGSYNC
385
+ (envForTxn->jsFlags & MDB_OVERLAPPINGSYNC) ? MDB_NOSYNC :
386
+ #endif
387
+ 0, &txn);
388
+ #if !defined(_WIN32) && defined(MDB_RPAGE_CACHE)
389
+ if (rc == MDB_LOCK_FAILURE) {
390
+ if (retries++ < 4) {
391
+ sleep(1);
392
+ goto retry;
393
+ }
394
+ }
395
+ #endif
396
+ if (rc != 0) {
397
+ resultCode = rc;
398
+ return;
399
+ }
400
+ rc = DoWrites(txn, envForTxn, instructions, this);
401
+ uint32_t txnId = (uint32_t) mdb_txn_id(txn);
402
+ progressStatus = 1;
403
+ #ifdef MDB_OVERLAPPINGSYNC
404
+ if (envForTxn->jsFlags & MDB_OVERLAPPINGSYNC) {
405
+ mdb_txn_set_callback(txn, txn_visible, this);
406
+ }
407
+ #endif
408
+ if (rc || resultCode)
409
+ mdb_txn_abort(txn);
410
+ else
411
+ rc = mdb_txn_commit(txn);
412
+ #ifdef MDB_OVERLAPPINGSYNC
413
+ #endif
414
+ #ifdef MDB_EMPTY_TXN
415
+ if (rc == MDB_EMPTY_TXN)
416
+ rc = 0;
417
+ #endif
418
+ txn = nullptr;
419
+ interruptionStatus = 0;
420
+ pthread_cond_signal(envForTxn->writingCond); // in case there a sync txn waiting for us
421
+ pthread_mutex_unlock(envForTxn->writingLock);
422
+ if (rc || resultCode) {
423
+ std::atomic_fetch_or((std::atomic<uint32_t>*) instructions, (uint32_t) TXN_HAD_ERROR);
424
+ if (rc)
425
+ resultCode = rc ? rc : resultCode;
426
+ return;
427
+ }
428
+ *(instructions - 1) = txnId;
429
+ std::atomic_fetch_or((std::atomic<uint32_t>*) instructions, (uint32_t) TXN_COMMITTED);
430
+ }
431
+
432
+ void write_progress(napi_env env,
433
+ napi_value js_callback,
434
+ void* context,
435
+ void* data) {
436
+ if (!js_callback)
437
+ return;
438
+ auto worker = (WriteWorker*) context;
439
+ napi_value result;
440
+ napi_value undefined;
441
+ napi_value arg;
442
+ napi_create_int32(env, worker->progressStatus, &arg);
443
+ napi_get_undefined(env, &undefined);
444
+ if (worker->progressStatus == 1) {
445
+ napi_call_function(env, undefined, js_callback, 1, &arg, &result);
446
+ return;
447
+ }
448
+ if (worker->finishedProgress)
449
+ return;
450
+ auto envForTxn = worker->envForTxn;
451
+ pthread_mutex_lock(envForTxn->writingLock);
452
+ while(!worker->txn) // possible to jump in after an interrupted txn here
453
+ pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
454
+ envForTxn->writeTxn = new TxnTracked(worker->txn, 0);
455
+ worker->finishedProgress = true;
456
+ napi_create_int32(env, worker->progressStatus, &arg);
457
+ napi_call_function(env, undefined, js_callback, 1, &arg, &result);
458
+ bool is_async = false;
459
+ napi_get_value_bool(env, result, &is_async);
460
+ if (!is_async) {
461
+ delete envForTxn->writeTxn;
462
+ envForTxn->writeTxn = nullptr;
463
+ pthread_cond_signal(envForTxn->writingCond);
464
+ pthread_mutex_unlock(envForTxn->writingLock);
465
+ }
466
+ }
467
+
468
+ void writes_complete(napi_env env,
469
+ napi_status status,
470
+ void* data) {
471
+ auto worker = (WriteWorker*) data;
472
+ worker->finishedProgress = true;
473
+ napi_value result, arg; // we use direct napi call here because node-addon-api interface with throw a fatal error if a worker thread is terminating, and bun doesn't support escapable scopes yet
474
+ napi_create_int32(env, worker->resultCode, &arg);
475
+ napi_value callback;
476
+ napi_get_reference_value(env, worker->callback, &callback);
477
+ napi_call_function(env, callback, callback, 1, &arg, &result);
478
+ napi_delete_reference(env, worker->callback);
479
+ napi_delete_async_work(env, worker->work);
480
+ delete worker;
481
+ }
482
+
483
+ Value EnvWrap::resumeWriting(const Napi::CallbackInfo& info) {
484
+ // if we had async txns, now we resume
485
+ delete writeTxn;
486
+ writeTxn = nullptr;
487
+ pthread_cond_signal(writingCond);
488
+ pthread_mutex_unlock(writingLock);
489
+ return info.Env().Undefined();
490
+ }
491
+
492
+ Value EnvWrap::startWriting(const Napi::CallbackInfo& info) {
493
+ napi_env n_env = info.Env();
494
+ if (!this->env) {
495
+ return throwError(info.Env(), "The environment is already closed.");
496
+ }
497
+ hasWrites = true;
498
+ size_t instructionAddress = info[0].As<Number>().Int64Value();
499
+ napi_value resource;
500
+ napi_status status;
501
+ status = napi_create_object(n_env, &resource);
502
+ napi_value resource_name;
503
+ status = napi_create_string_latin1(n_env, "write", NAPI_AUTO_LENGTH, &resource_name);
504
+ auto worker = new WriteWorker(this->env, this, (uint32_t*) instructionAddress);
505
+ this->writeWorker = worker;
506
+ napi_create_reference(n_env, info[1].As<Function>(), 1, &worker->callback);
507
+ status = napi_create_async_work(n_env, resource, resource_name, do_write, writes_complete, worker, &worker->work);
508
+ napi_create_threadsafe_function(n_env, info[1].As<Function>(), resource, resource_name, 0, 1, nullptr, nullptr, worker, write_progress, &worker->progress);
509
+ status = napi_queue_async_work(n_env, worker->work);
510
+
511
+ return info.Env().Undefined();
512
+ }
513
+
514
+ NAPI_FUNCTION(EnvWrap::write) {
515
+ ARGS(2)
516
+ GET_INT64_ARG(0);
517
+ EnvWrap* ew = (EnvWrap*) i64;
518
+ if (!ew->env) {
519
+ napi_throw_error(env, nullptr, "The environment is already closed.");
520
+ RETURN_UNDEFINED;
521
+ }
522
+ ew->hasWrites = true;
523
+
524
+ napi_get_value_int64(env, args[1], &i64);
525
+ uint32_t* instructionAddress = (uint32_t*) i64;
526
+ int rc = 0;
527
+ if (instructionAddress)
528
+ rc = WriteWorker::DoWrites(ew->writeTxn->txn, ew, instructionAddress, nullptr);
529
+ else if (ew->writeWorker) {
530
+ pthread_cond_signal(ew->writingCond);
531
+ }
532
+ if (rc && !(rc == MDB_KEYEXIST || rc == MDB_NOTFOUND)) {
533
+ throwLmdbError(env, rc);
534
+ RETURN_UNDEFINED;
535
+ }
536
+ RETURN_UNDEFINED;
537
+ }