@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.
- package/LICENSE +21 -0
- package/README.md +580 -0
- package/SECURITY.md +12 -0
- package/bin/download-prebuilds.js +15 -0
- package/binding.gyp +113 -0
- package/build/Release/lmdb.node +0 -0
- package/caching.js +212 -0
- package/dependencies/lmdb/libraries/liblmdb/COPYRIGHT +20 -0
- package/dependencies/lmdb/libraries/liblmdb/Doxyfile +1631 -0
- package/dependencies/lmdb/libraries/liblmdb/LICENSE +47 -0
- package/dependencies/lmdb/libraries/liblmdb/Makefile +136 -0
- package/dependencies/lmdb/libraries/liblmdb/chacha8.c +183 -0
- package/dependencies/lmdb/libraries/liblmdb/chacha8.h +14 -0
- package/dependencies/lmdb/libraries/liblmdb/crypto.c +121 -0
- package/dependencies/lmdb/libraries/liblmdb/intro.doc +192 -0
- package/dependencies/lmdb/libraries/liblmdb/lmdb.h +1817 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb.c +12555 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_copy.1 +74 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_copy.c +106 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_drop.1 +53 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_drop.c +154 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_dump.1 +94 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_dump.c +333 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_load.1 +97 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_load.c +530 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_stat.1 +83 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_stat.c +276 -0
- package/dependencies/lmdb/libraries/liblmdb/midl-sparse.c +452 -0
- package/dependencies/lmdb/libraries/liblmdb/midl.c +452 -0
- package/dependencies/lmdb/libraries/liblmdb/midl.h +208 -0
- package/dependencies/lmdb/libraries/liblmdb/module.c +101 -0
- package/dependencies/lmdb/libraries/liblmdb/module.h +16 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest.c +178 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest2.c +124 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest3.c +133 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest4.c +168 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest5.c +135 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest6.c +141 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest_enc.c +190 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest_enc2.c +189 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest_remap.c +177 -0
- package/dependencies/lmdb/libraries/liblmdb/sample-bdb.txt +73 -0
- package/dependencies/lmdb/libraries/liblmdb/sample-mdb.txt +62 -0
- package/dependencies/lmdb/libraries/liblmdb/tooltag +27 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/CHANGES +266 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/COPYRIGHT +20 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/Doxyfile +1631 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/LICENSE +47 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/intro.doc +192 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/lmdb.h +1608 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb.c +10320 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.1 +55 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.c +82 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.1 +75 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.c +319 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.1 +84 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.c +496 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.1 +64 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.c +263 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.c +359 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.h +186 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest.c +177 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest2.c +124 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest3.c +133 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest4.c +168 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest5.c +135 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest6.c +141 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-bdb.txt +73 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-mdb.txt +62 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/tooltag +22 -0
- package/dependencies/lz4/LICENSE +11 -0
- package/dependencies/lz4/lib/LICENSE +24 -0
- package/dependencies/lz4/lib/Makefile +225 -0
- package/dependencies/lz4/lib/README.md +169 -0
- package/dependencies/lz4/lib/dll/example/Makefile +63 -0
- package/dependencies/lz4/lib/dll/example/README.md +69 -0
- package/dependencies/lz4/lib/dll/example/fullbench-dll.sln +25 -0
- package/dependencies/lz4/lib/dll/example/fullbench-dll.vcxproj +182 -0
- package/dependencies/lz4/lib/dll/liblz4.def +62 -0
- package/dependencies/lz4/lib/liblz4-dll.rc.in +35 -0
- package/dependencies/lz4/lib/liblz4.pc.in +14 -0
- package/dependencies/lz4/lib/lz4.c +2722 -0
- package/dependencies/lz4/lib/lz4.h +842 -0
- package/dependencies/lz4/lib/lz4file.c +311 -0
- package/dependencies/lz4/lib/lz4file.h +93 -0
- package/dependencies/lz4/lib/lz4frame.c +2078 -0
- package/dependencies/lz4/lib/lz4frame.h +692 -0
- package/dependencies/lz4/lib/lz4frame_static.h +47 -0
- package/dependencies/lz4/lib/lz4hc.c +1631 -0
- package/dependencies/lz4/lib/lz4hc.h +413 -0
- package/dependencies/lz4/lib/xxhash.c +1030 -0
- package/dependencies/lz4/lib/xxhash.h +328 -0
- package/dependencies/v8/v8-fast-api-calls-v16.h +791 -0
- package/dependencies/v8/v8-fast-api-calls.h +888 -0
- package/dict/dict.txt +1 -0
- package/dict/dict2.txt +1 -0
- package/dist/index.cjs +2950 -0
- package/dist/index.cjs.map +1 -0
- package/index.d.cts +420 -0
- package/index.d.ts +450 -0
- package/index.js +38 -0
- package/keys.js +116 -0
- package/level.js +27 -0
- package/native.js +85 -0
- package/node-index.js +32 -0
- package/open.js +448 -0
- package/package.json +124 -0
- package/read.js +833 -0
- package/rollup.config.js +12 -0
- package/src/compression.cpp +225 -0
- package/src/cursor.cpp +395 -0
- package/src/dbi.cpp +382 -0
- package/src/env.cpp +1030 -0
- package/src/lmdb-js.cpp +64 -0
- package/src/lmdb-js.h +633 -0
- package/src/misc.cpp +593 -0
- package/src/ordered-binary.cpp +46 -0
- package/src/txn.cpp +210 -0
- package/src/v8-functions.cpp +170 -0
- package/src/windows.c +35 -0
- package/src/writer.cpp +537 -0
- package/util/RangeIterable.js +236 -0
- package/util/set-optional-deps.cjs +14 -0
- package/util/when.js +8 -0
- 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
|
+
}
|