@ohos-ports/lmdb 2.5.3-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 +536 -0
- package/SECURITY.md +12 -0
- package/binding.gyp +111 -0
- package/build/Release/lmdb.node +0 -0
- package/caching.js +156 -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 +1792 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb.c +12429 -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.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/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/Makefile +118 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/intro.doc +192 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/lmdb.h +1653 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb.c +11359 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.1 +61 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.c +84 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_drop.1 +40 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_drop.c +135 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.1 +81 -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 +492 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.1 +70 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.c +264 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.c +421 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.h +200 -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 +27 -0
- package/dependencies/lz4/LICENSE +11 -0
- package/dependencies/lz4/lib/LICENSE +24 -0
- package/dependencies/lz4/lib/README.md +137 -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 +2495 -0
- package/dependencies/lz4/lib/lz4.h +774 -0
- package/dependencies/lz4/lib/lz4frame.c +1899 -0
- package/dependencies/lz4/lib/lz4frame.h +623 -0
- package/dependencies/lz4/lib/lz4frame_static.h +47 -0
- package/dependencies/lz4/lib/lz4hc.c +1615 -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 +2478 -0
- package/dist/index.cjs.map +1 -0
- package/index.d.ts +385 -0
- package/index.js +37 -0
- package/keys.js +102 -0
- package/level.js +27 -0
- package/native.js +82 -0
- package/node-index.js +28 -0
- package/open.js +414 -0
- package/package.json +70 -0
- package/read.js +590 -0
- package/rollup.config.js +12 -0
- package/src/compression.cpp +222 -0
- package/src/cursor.cpp +344 -0
- package/src/dbi.cpp +344 -0
- package/src/env.cpp +762 -0
- package/src/lmdb-js.cpp +64 -0
- package/src/lmdb-js.h +599 -0
- package/src/misc.cpp +324 -0
- package/src/ordered-binary.cpp +46 -0
- package/src/txn.cpp +257 -0
- package/src/v8-functions.cpp +166 -0
- package/src/windows.c +35 -0
- package/src/writer.cpp +452 -0
- package/util/RangeIterable.js +141 -0
- package/util/set-optional-deps.cjs +10 -0
- package/util/when.js +8 -0
- package/write.js +882 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#include "lmdb-js.h"
|
|
2
|
+
#if ENABLE_V8_API
|
|
3
|
+
#include <string.h>
|
|
4
|
+
#include <stdio.h>
|
|
5
|
+
#include <v8.h>
|
|
6
|
+
#include <node.h>
|
|
7
|
+
|
|
8
|
+
#if NODE_VERSION_AT_LEAST(16,6,0)
|
|
9
|
+
#if NODE_VERSION_AT_LEAST(17,0,0)
|
|
10
|
+
#include "../dependencies/v8/v8-fast-api-calls.h"
|
|
11
|
+
#else
|
|
12
|
+
#include "../dependencies/v8/v8-fast-api-calls-v16.h"
|
|
13
|
+
#endif
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
using namespace v8;
|
|
17
|
+
int32_t getByBinaryFast(Local<v8::Object> instance, double dwPointer, uint32_t keySize) {
|
|
18
|
+
DbiWrap* dw = (DbiWrap*) (size_t) dwPointer;
|
|
19
|
+
return dw->doGetByBinary(keySize);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//class NanWrap : public Nan::ObjectWrap {};
|
|
23
|
+
void getByBinaryV8(const FunctionCallbackInfo<v8::Value>& info) {
|
|
24
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
25
|
+
DbiWrap* dw = (DbiWrap*) (size_t) info[0]->NumberValue(isolate->GetCurrentContext()).FromJust();
|
|
26
|
+
info.GetReturnValue().Set(v8::Number::New(isolate, dw->doGetByBinary(info[1]->Uint32Value(isolate->GetCurrentContext()).FromJust())));
|
|
27
|
+
}
|
|
28
|
+
int32_t positionFast(Local<v8::Object> instance, double cwPointer, uint32_t flags, uint32_t offset, uint32_t keySize, uint64_t endKeyAddress) {
|
|
29
|
+
CursorWrap* cw = (CursorWrap*) (size_t) cwPointer;
|
|
30
|
+
DbiWrap* dw = cw->dw;
|
|
31
|
+
dw->getFast = true;
|
|
32
|
+
cw->flags = flags;
|
|
33
|
+
return cw->doPosition(offset, keySize, endKeyAddress);
|
|
34
|
+
}
|
|
35
|
+
void positionV8(const FunctionCallbackInfo<v8::Value>& info) {
|
|
36
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
37
|
+
CursorWrap* cw = (CursorWrap*) (size_t) info[0]->NumberValue(isolate->GetCurrentContext()).FromJust();
|
|
38
|
+
cw->flags = info[1]->Uint32Value(isolate->GetCurrentContext()).FromJust();
|
|
39
|
+
uint32_t offset = info[2]->Uint32Value(isolate->GetCurrentContext()).FromJust();
|
|
40
|
+
uint32_t keySize = info[3]->Uint32Value(isolate->GetCurrentContext()).FromJust();
|
|
41
|
+
uint64_t endKeyAddress = info[4]->IntegerValue(isolate->GetCurrentContext()).FromJust();
|
|
42
|
+
info.GetReturnValue().Set(v8::Number::New(isolate, cw->doPosition(offset, keySize, endKeyAddress)));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
int32_t iterateFast(Local<v8::Object> instance, double cwPointer) {
|
|
46
|
+
CursorWrap* cw = (CursorWrap*) (size_t) cwPointer;
|
|
47
|
+
DbiWrap* dw = cw->dw;
|
|
48
|
+
dw->getFast = true;
|
|
49
|
+
MDB_val key, data;
|
|
50
|
+
int rc = mdb_cursor_get(cw->cursor, &key, &data, cw->iteratingOp);
|
|
51
|
+
return cw->returnEntry(rc, key, data);
|
|
52
|
+
}
|
|
53
|
+
void iterateV8(const FunctionCallbackInfo<v8::Value>& info) {
|
|
54
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
55
|
+
CursorWrap* cw = (CursorWrap*) (size_t) info[0]->NumberValue(isolate->GetCurrentContext()).FromJust();
|
|
56
|
+
DbiWrap* dw = cw->dw;
|
|
57
|
+
dw->getFast = true;
|
|
58
|
+
MDB_val key, data;
|
|
59
|
+
int rc = mdb_cursor_get(cw->cursor, &key, &data, cw->iteratingOp);
|
|
60
|
+
info.GetReturnValue().Set(v8::Number::New(isolate, cw->returnEntry(rc, key, data)));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
int32_t writeFast(Local<v8::Object> instance, double ewPointer, uint64_t instructionAddress) {
|
|
64
|
+
EnvWrap* ew = (EnvWrap*) (size_t) ewPointer;
|
|
65
|
+
int rc;
|
|
66
|
+
if (instructionAddress)
|
|
67
|
+
rc = WriteWorker::DoWrites(ew->writeTxn->txn, ew, (uint32_t*)instructionAddress, nullptr);
|
|
68
|
+
else {
|
|
69
|
+
pthread_cond_signal(ew->writingCond);
|
|
70
|
+
rc = 0;
|
|
71
|
+
}
|
|
72
|
+
return rc;
|
|
73
|
+
// if (rc && !(rc == MDB_KEYEXIST || rc == MDB_NOTFOUND))
|
|
74
|
+
// options.fallback = true;
|
|
75
|
+
}
|
|
76
|
+
void writeV8(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
|
77
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
78
|
+
EnvWrap* ew = (EnvWrap*) (size_t) info[0]->NumberValue(isolate->GetCurrentContext()).FromJust();
|
|
79
|
+
uint64_t instructionAddress = info[1]->IntegerValue(isolate->GetCurrentContext()).FromJust();
|
|
80
|
+
int rc;
|
|
81
|
+
if (instructionAddress)
|
|
82
|
+
rc = WriteWorker::DoWrites(ew->writeTxn->txn, ew, (uint32_t*)instructionAddress, nullptr);
|
|
83
|
+
else {
|
|
84
|
+
pthread_cond_signal(ew->writingCond);
|
|
85
|
+
rc = 0;
|
|
86
|
+
}
|
|
87
|
+
info.GetReturnValue().Set(v8::Number::New(isolate, rc));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
void resetTxnFast(Local<v8::Object> instance, double twPointer) {
|
|
91
|
+
TxnWrap* tw = (TxnWrap*) (size_t) twPointer;
|
|
92
|
+
tw->reset();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
void resetTxnV8(const FunctionCallbackInfo<v8::Value>& info) {
|
|
96
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
97
|
+
TxnWrap* tw = (TxnWrap*) (size_t) info[0]->NumberValue(isolate->GetCurrentContext()).FromJust();
|
|
98
|
+
tw->reset();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
void noopFast(Local<v8::Object> instance) {
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
void noopV8(const FunctionCallbackInfo<v8::Value>& info) {
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
void clearKeptObjects(const FunctionCallbackInfo<v8::Value>& info) {
|
|
109
|
+
#if NODE_VERSION_AT_LEAST(14,0,0)
|
|
110
|
+
v8::Isolate::GetCurrent()->ClearKeptObjects();
|
|
111
|
+
#endif
|
|
112
|
+
}
|
|
113
|
+
void detachBuffer(const FunctionCallbackInfo<v8::Value>& info) {
|
|
114
|
+
#if NODE_VERSION_AT_LEAST(12,0,0)
|
|
115
|
+
v8::Local<v8::ArrayBuffer> v8Buffer = Local<v8::ArrayBuffer>::Cast(info[0]);
|
|
116
|
+
v8Buffer->Detach();
|
|
117
|
+
#endif
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#endif
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
#define EXPORT_FAST(exportName, slowName, fastName) {\
|
|
124
|
+
auto fast = CFunction::Make(fastName);\
|
|
125
|
+
exports->Set(isolate->GetCurrentContext(), v8::String::NewFromUtf8(isolate, exportName, NewStringType::kInternalized).ToLocalChecked(), FunctionTemplate::New(\
|
|
126
|
+
isolate, slowName, Local<v8::Value>(),\
|
|
127
|
+
Local<Signature>(), 0, ConstructorBehavior::kThrow,\
|
|
128
|
+
SideEffectType::kHasNoSideEffect, &fast)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked());\
|
|
129
|
+
}
|
|
130
|
+
#define EXPORT_FUNCTION(exportName, funcName) \
|
|
131
|
+
exports->Set(isolate->GetCurrentContext(), v8::String::NewFromUtf8(isolate, exportName, NewStringType::kInternalized).ToLocalChecked(), FunctionTemplate::New(\
|
|
132
|
+
isolate, funcName, Local<v8::Value>(),\
|
|
133
|
+
Local<Signature>(), 0, ConstructorBehavior::kThrow,\
|
|
134
|
+
SideEffectType::kHasNoSideEffect)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked());
|
|
135
|
+
|
|
136
|
+
Napi::Value enableDirectV8(const Napi::CallbackInfo& info) {
|
|
137
|
+
#if ENABLE_V8_API
|
|
138
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
139
|
+
napi_value exportsValue = info[0];
|
|
140
|
+
Local<v8::Object> exports;
|
|
141
|
+
memcpy((void*) &exports, (void*) &exportsValue, sizeof(exportsValue));
|
|
142
|
+
#if NODE_VERSION_AT_LEAST(16,6,1)
|
|
143
|
+
bool useFastApi;
|
|
144
|
+
napi_get_value_bool(info.Env(), info[1], &useFastApi);
|
|
145
|
+
if (useFastApi) {
|
|
146
|
+
EXPORT_FAST("getByBinary", getByBinaryV8, getByBinaryFast);
|
|
147
|
+
EXPORT_FAST("position", positionV8, positionFast);
|
|
148
|
+
EXPORT_FAST("iterate", iterateV8, iterateFast);
|
|
149
|
+
EXPORT_FAST("write", writeV8, writeFast);
|
|
150
|
+
EXPORT_FAST("resetTxn", resetTxnV8, resetTxnFast);
|
|
151
|
+
EXPORT_FAST("noop", noopV8, noopFast);
|
|
152
|
+
} else {
|
|
153
|
+
#endif
|
|
154
|
+
EXPORT_FUNCTION("getByBinary", getByBinaryV8);
|
|
155
|
+
EXPORT_FUNCTION("position", positionV8);
|
|
156
|
+
EXPORT_FUNCTION("iterate", iterateV8);
|
|
157
|
+
EXPORT_FUNCTION("write", writeV8);
|
|
158
|
+
EXPORT_FUNCTION("noop", noopV8);
|
|
159
|
+
#if NODE_VERSION_AT_LEAST(16,6,1)
|
|
160
|
+
}
|
|
161
|
+
#endif
|
|
162
|
+
EXPORT_FUNCTION("clearKeptObjects", clearKeptObjects);
|
|
163
|
+
EXPORT_FUNCTION("detachBuffer", detachBuffer);
|
|
164
|
+
#endif
|
|
165
|
+
return info.Env().Undefined();
|
|
166
|
+
}
|
package/src/windows.c
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#ifdef _WIN32
|
|
2
|
+
#include <windows.h>
|
|
3
|
+
#include <synchapi.h>
|
|
4
|
+
static int initializeMemoryPriority = 1;
|
|
5
|
+
static MEMORY_PRIORITY_INFORMATION lowMemPriority;
|
|
6
|
+
static MEMORY_PRIORITY_INFORMATION normalMemPriority;
|
|
7
|
+
int lowerMemoryPriority(int priority) {
|
|
8
|
+
if (initializeMemoryPriority) {
|
|
9
|
+
GetThreadInformation(GetCurrentThread(), ThreadMemoryPriority, &normalMemPriority, sizeof(normalMemPriority));
|
|
10
|
+
// fprintf(stderr, "initialized memory %u setting to %u\n", normalMemPriority.MemoryPriority, priority);
|
|
11
|
+
ZeroMemory(&lowMemPriority, sizeof(lowMemPriority));
|
|
12
|
+
lowMemPriority.MemoryPriority = priority;
|
|
13
|
+
initializeMemoryPriority = 0;
|
|
14
|
+
}
|
|
15
|
+
void* instruction;
|
|
16
|
+
void* pointer;
|
|
17
|
+
WaitOnAddress(instruction, pointer, 8, INFINITE);
|
|
18
|
+
|
|
19
|
+
return SetThreadInformation(GetCurrentThread(), ThreadMemoryPriority, &lowMemPriority, sizeof(lowMemPriority));
|
|
20
|
+
}
|
|
21
|
+
int setProcessMemoryPriority(int priority) {
|
|
22
|
+
if (initializeMemoryPriority) {
|
|
23
|
+
GetThreadInformation(GetCurrentThread(), ThreadMemoryPriority, &normalMemPriority, sizeof(normalMemPriority));
|
|
24
|
+
// fprintf(stderr, "initialized memory %u setting to %u\n", normalMemPriority.MemoryPriority, priority);
|
|
25
|
+
ZeroMemory(&lowMemPriority, sizeof(lowMemPriority));
|
|
26
|
+
lowMemPriority.MemoryPriority = priority;
|
|
27
|
+
initializeMemoryPriority = 0;
|
|
28
|
+
}
|
|
29
|
+
return SetProcessInformation(GetCurrentProcess(), ProcessMemoryPriority, &lowMemPriority, sizeof(lowMemPriority));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
int restoreMemoryPriority() {
|
|
33
|
+
return SetThreadInformation(GetCurrentThread(), ThreadMemoryPriority, &normalMemPriority, sizeof(normalMemPriority));
|
|
34
|
+
}
|
|
35
|
+
#endif
|
package/src/writer.cpp
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
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
|
+
#ifndef _WIN32
|
|
17
|
+
#include <unistd.h>
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
// flags:
|
|
21
|
+
const uint32_t NO_INSTRUCTION_YET = 0;
|
|
22
|
+
const int PUT = 15;
|
|
23
|
+
const int DEL = 13;
|
|
24
|
+
const int DEL_VALUE = 14;
|
|
25
|
+
const int START_CONDITION_BLOCK = 4;
|
|
26
|
+
//const int START_CONDITION_VALUE_BLOCK = 6;
|
|
27
|
+
const int START_BLOCK = 1;
|
|
28
|
+
const int BLOCK_END = 2;
|
|
29
|
+
const int POINTER_NEXT = 3;
|
|
30
|
+
const int USER_CALLBACK = 8;
|
|
31
|
+
const int USER_CALLBACK_STRICT_ORDER = 0x100000;
|
|
32
|
+
const int DROP_DB = 12;
|
|
33
|
+
const int HAS_KEY = 4;
|
|
34
|
+
const int HAS_VALUE = 2;
|
|
35
|
+
const int CONDITIONAL = 8;
|
|
36
|
+
const int CONDITIONAL_VERSION = 0x100;
|
|
37
|
+
const int SET_VERSION = 0x200;
|
|
38
|
+
//const int HAS_INLINE_VALUE = 0x400;
|
|
39
|
+
const int COMPRESSIBLE = 0x100000;
|
|
40
|
+
const int DELETE_DATABASE = 0x400;
|
|
41
|
+
const int TXN_HAD_ERROR = 0x40000000;
|
|
42
|
+
const int TXN_DELIMITER = 0x8000000;
|
|
43
|
+
const int TXN_COMMITTED = 0x10000000;
|
|
44
|
+
//const int TXN_FLUSHED = 0x20000000;
|
|
45
|
+
const int WAITING_OPERATION = 0x2000000;
|
|
46
|
+
const int IF_NO_EXISTS = MDB_NOOVERWRITE; //0x10;
|
|
47
|
+
// result codes:
|
|
48
|
+
const int FAILED_CONDITION = 0x4000000;
|
|
49
|
+
const int FINISHED_OPERATION = 0x1000000;
|
|
50
|
+
const double ANY_VERSION = 3.542694326329068e-103; // special marker for any version
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
WriteWorker::~WriteWorker() {
|
|
54
|
+
// TODO: Make sure this runs on the JS main thread, or we need to move it
|
|
55
|
+
if (envForTxn->writeWorker == this)
|
|
56
|
+
envForTxn->writeWorker = nullptr;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
WriteWorker::WriteWorker(MDB_env* env, EnvWrap* envForTxn, uint32_t* instructions)
|
|
60
|
+
: envForTxn(envForTxn),
|
|
61
|
+
instructions(instructions),
|
|
62
|
+
env(env) {
|
|
63
|
+
//fprintf(stdout, "nextCompressibleArg %p\n", nextCompressibleArg);
|
|
64
|
+
interruptionStatus = 0;
|
|
65
|
+
txn = nullptr;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
AsyncWriteWorker::AsyncWriteWorker(MDB_env* env, EnvWrap* envForTxn, uint32_t* instructions, const Function& callback)
|
|
69
|
+
: WriteWorker(env, envForTxn, instructions), AsyncProgressWorker(callback, "lmdb:write") {
|
|
70
|
+
//fprintf(stdout, "nextCompressibleArg %p\n", nextCompressibleArg);
|
|
71
|
+
interruptionStatus = 0;
|
|
72
|
+
txn = nullptr;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
void AsyncWriteWorker::Execute(const AsyncProgressWorker::ExecutionProgress& execution) {
|
|
76
|
+
executionProgress = (AsyncProgressWorker::ExecutionProgress*) &execution;
|
|
77
|
+
Write();
|
|
78
|
+
}
|
|
79
|
+
void WriteWorker::SendUpdate() {
|
|
80
|
+
fprintf(stderr, "This SendUpdate does not work!\n");
|
|
81
|
+
}
|
|
82
|
+
void AsyncWriteWorker::SendUpdate() {
|
|
83
|
+
executionProgress->Send(nullptr, 0);
|
|
84
|
+
}
|
|
85
|
+
MDB_txn* WriteWorker::AcquireTxn(int* flags) {
|
|
86
|
+
bool commitSynchronously = *flags & TXN_SYNCHRONOUS_COMMIT;
|
|
87
|
+
|
|
88
|
+
// TODO: if the conditionDepth is 0, we could allow the current worker's txn to be continued, committed and restarted
|
|
89
|
+
pthread_mutex_lock(envForTxn->writingLock);
|
|
90
|
+
retry:
|
|
91
|
+
if (commitSynchronously && interruptionStatus == WORKER_WAITING) {
|
|
92
|
+
//fprintf(stderr, "acquire interupting lock %p %u\n", this, commitSynchronously);
|
|
93
|
+
interruptionStatus = INTERRUPT_BATCH;
|
|
94
|
+
pthread_cond_signal(envForTxn->writingCond);
|
|
95
|
+
pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
|
|
96
|
+
if (interruptionStatus == RESTART_WORKER_TXN) {
|
|
97
|
+
*flags |= TXN_FROM_WORKER;
|
|
98
|
+
return nullptr;
|
|
99
|
+
} else {
|
|
100
|
+
interruptionStatus = WORKER_WAITING;
|
|
101
|
+
goto retry;
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
//if (interruptionStatus == RESTART_WORKER_TXN)
|
|
105
|
+
// pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
|
|
106
|
+
interruptionStatus = USER_HAS_LOCK;
|
|
107
|
+
*flags |= TXN_FROM_WORKER;
|
|
108
|
+
//if (txn)
|
|
109
|
+
//fprintf(stderr, "acquire lock from worker %p %u\n", txn, commitSynchronously);
|
|
110
|
+
return txn;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
void WriteWorker::UnlockTxn() {
|
|
115
|
+
//fprintf(stderr, "release txn %u\n", interruptionStatus);
|
|
116
|
+
interruptionStatus = 0;
|
|
117
|
+
pthread_cond_signal(envForTxn->writingCond);
|
|
118
|
+
pthread_mutex_unlock(envForTxn->writingLock);
|
|
119
|
+
}
|
|
120
|
+
void WriteWorker::ReportError(const char* error) {
|
|
121
|
+
hasError = true;
|
|
122
|
+
fprintf(stderr, "Error %s\n", error);
|
|
123
|
+
}
|
|
124
|
+
void AsyncWriteWorker::ReportError(const char* error) {
|
|
125
|
+
hasError = true;
|
|
126
|
+
SetError(error);
|
|
127
|
+
}
|
|
128
|
+
int WriteWorker::WaitForCallbacks(MDB_txn** txn, bool allowCommit, uint32_t* target) {
|
|
129
|
+
int rc;
|
|
130
|
+
//fprintf(stderr, "wait for callback %p\n", target);
|
|
131
|
+
if (!finishedProgress)
|
|
132
|
+
SendUpdate();
|
|
133
|
+
pthread_cond_signal(envForTxn->writingCond);
|
|
134
|
+
interruptionStatus = WORKER_WAITING;
|
|
135
|
+
if (target) {
|
|
136
|
+
uint64_t delay = 1;
|
|
137
|
+
do {
|
|
138
|
+
cond_timedwait(envForTxn->writingCond, envForTxn->writingLock, delay);
|
|
139
|
+
delay = delay << 1ll;
|
|
140
|
+
//if (delay > 500)
|
|
141
|
+
//fprintf(stderr, "waited, %llu %p\n", delay, *target);
|
|
142
|
+
if ((*target & 0xf) || (allowCommit && finishedProgress)) {
|
|
143
|
+
// we are in position to continue writing or commit, so forward progress can be made without interrupting yet
|
|
144
|
+
interruptionStatus = 0;
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
} while(interruptionStatus != INTERRUPT_BATCH);
|
|
148
|
+
} else
|
|
149
|
+
pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
|
|
150
|
+
if (interruptionStatus == INTERRUPT_BATCH) { // interrupted by JS code that wants to run a synchronous transaction
|
|
151
|
+
// fprintf(stderr, "Performing batch interruption %u\n", allowCommit);
|
|
152
|
+
interruptionStatus = RESTART_WORKER_TXN;
|
|
153
|
+
rc = mdb_txn_commit(*txn);
|
|
154
|
+
if (rc == 0) {
|
|
155
|
+
// wait again until the sync transaction is completed
|
|
156
|
+
//fprintf(stderr, "Waiting after interruption\n");
|
|
157
|
+
this->txn = *txn = nullptr;
|
|
158
|
+
pthread_cond_signal(envForTxn->writingCond);
|
|
159
|
+
pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
|
|
160
|
+
// now restart our transaction
|
|
161
|
+
rc = mdb_txn_begin(env, nullptr, 0, txn);
|
|
162
|
+
this->txn = *txn;
|
|
163
|
+
//fprintf(stderr, "Restarted txn after interruption\n");
|
|
164
|
+
interruptionStatus = 0;
|
|
165
|
+
}
|
|
166
|
+
if (rc != 0) {
|
|
167
|
+
fprintf(stdout, "wfc unlock due to error %u\n", rc);
|
|
168
|
+
return rc;
|
|
169
|
+
}
|
|
170
|
+
} else
|
|
171
|
+
interruptionStatus = 0;
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
int WriteWorker::DoWrites(MDB_txn* txn, EnvWrap* envForTxn, uint32_t* instruction, WriteWorker* worker) {
|
|
175
|
+
MDB_val key, value;
|
|
176
|
+
int rc = 0;
|
|
177
|
+
int conditionDepth = 0;
|
|
178
|
+
int validatedDepth = 0;
|
|
179
|
+
double conditionalVersion, setVersion = 0;
|
|
180
|
+
bool overlappedWord = !!worker;
|
|
181
|
+
uint32_t* start;
|
|
182
|
+
do {
|
|
183
|
+
next_inst: start = instruction++;
|
|
184
|
+
uint32_t flags = *start;
|
|
185
|
+
MDB_dbi dbi = 0;
|
|
186
|
+
bool validated = conditionDepth == validatedDepth;
|
|
187
|
+
if (flags & 0xf0c0) {
|
|
188
|
+
fprintf(stderr, "Unknown flag bits %u %p\n", flags, start);
|
|
189
|
+
fprintf(stderr, "flags after message %u\n", *start);
|
|
190
|
+
worker->ReportError("Unknown flags\n");
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
if (flags & HAS_KEY) {
|
|
194
|
+
// a key based instruction, get the key
|
|
195
|
+
dbi = (MDB_dbi) *instruction++;
|
|
196
|
+
key.mv_size = *instruction++;
|
|
197
|
+
key.mv_data = instruction;
|
|
198
|
+
instruction = (uint32_t*) (((size_t) instruction + key.mv_size + 16) & (~7));
|
|
199
|
+
if (flags & HAS_VALUE) {
|
|
200
|
+
if (flags & COMPRESSIBLE) {
|
|
201
|
+
int64_t status = -1;
|
|
202
|
+
status = std::atomic_exchange((std::atomic<int64_t>*)(instruction + 2), (int64_t)1);
|
|
203
|
+
if (status == 2) {
|
|
204
|
+
//fprintf(stderr, "wait on compression %p\n", instruction);
|
|
205
|
+
worker->interruptionStatus = WORKER_WAITING;
|
|
206
|
+
do {
|
|
207
|
+
pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
|
|
208
|
+
} while (std::atomic_load((std::atomic<int64_t>*)(instruction + 2)));
|
|
209
|
+
worker->interruptionStatus = 0;
|
|
210
|
+
} else if (status > 2) {
|
|
211
|
+
//fprintf(stderr, "doing the compression ourselves\n");
|
|
212
|
+
((Compression*) (size_t) *((double*)&status))->compressInstruction(nullptr, (double*) (instruction + 2));
|
|
213
|
+
} // else status is 0 and compression is done
|
|
214
|
+
// compressed
|
|
215
|
+
value.mv_data = (void*)(size_t) * ((size_t*)instruction);
|
|
216
|
+
if ((size_t)value.mv_data > 0x1000000000000)
|
|
217
|
+
fprintf(stderr, "compression not completed %p %i\n", value.mv_data, (int) status);
|
|
218
|
+
value.mv_size = *(instruction - 1);
|
|
219
|
+
instruction += 4; // skip compression pointers
|
|
220
|
+
} else {
|
|
221
|
+
value.mv_data = (void*)(size_t) * ((double*)instruction);
|
|
222
|
+
value.mv_size = *(instruction - 1);
|
|
223
|
+
instruction += 2;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (flags & CONDITIONAL_VERSION) {
|
|
227
|
+
conditionalVersion = *((double*) instruction);
|
|
228
|
+
instruction += 2;
|
|
229
|
+
MDB_val conditionalValue;
|
|
230
|
+
rc = mdb_get(txn, dbi, &key, &conditionalValue);
|
|
231
|
+
if (rc)
|
|
232
|
+
validated = false;
|
|
233
|
+
else if (conditionalVersion != ANY_VERSION) {
|
|
234
|
+
double version;
|
|
235
|
+
memcpy(&version, conditionalValue.mv_data, 8);
|
|
236
|
+
validated = validated && conditionalVersion == version;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (flags & SET_VERSION) {
|
|
240
|
+
setVersion = *((double*) instruction);
|
|
241
|
+
instruction += 2;
|
|
242
|
+
}
|
|
243
|
+
if ((flags & IF_NO_EXISTS) && (flags & START_CONDITION_BLOCK)) {
|
|
244
|
+
rc = mdb_get(txn, dbi, &key, &value);
|
|
245
|
+
validated = validated && rc == MDB_NOTFOUND;
|
|
246
|
+
}
|
|
247
|
+
} else
|
|
248
|
+
instruction++;
|
|
249
|
+
//fprintf(stderr, "instr flags %p %p %u\n", start, flags, conditionDepth);
|
|
250
|
+
if (validated || !(flags & CONDITIONAL)) {
|
|
251
|
+
switch (flags & 0xf) {
|
|
252
|
+
case NO_INSTRUCTION_YET:
|
|
253
|
+
instruction -= 2; // reset back to the previous flag as the current instruction
|
|
254
|
+
rc = 0;
|
|
255
|
+
// in windows InterlockedCompareExchange might be faster
|
|
256
|
+
if (!worker->finishedProgress || conditionDepth) {
|
|
257
|
+
if (std::atomic_compare_exchange_strong((std::atomic<uint32_t>*) start,
|
|
258
|
+
(uint32_t*) &flags,
|
|
259
|
+
(uint32_t)WAITING_OPERATION)) {
|
|
260
|
+
worker->WaitForCallbacks(&txn, conditionDepth == 0, start);
|
|
261
|
+
}
|
|
262
|
+
goto next_inst;
|
|
263
|
+
} else {
|
|
264
|
+
if (std::atomic_compare_exchange_strong((std::atomic<uint32_t>*) start,
|
|
265
|
+
(uint32_t*) &flags,
|
|
266
|
+
(uint32_t)TXN_DELIMITER)) {
|
|
267
|
+
worker->instructions = start;
|
|
268
|
+
return 0;
|
|
269
|
+
} else
|
|
270
|
+
goto next_inst;
|
|
271
|
+
}
|
|
272
|
+
case BLOCK_END:
|
|
273
|
+
conditionDepth--;
|
|
274
|
+
if (validatedDepth > conditionDepth)
|
|
275
|
+
validatedDepth--;
|
|
276
|
+
if (conditionDepth < 0) {
|
|
277
|
+
fprintf(stderr, "Negative condition depth");
|
|
278
|
+
}
|
|
279
|
+
goto next_inst;
|
|
280
|
+
case PUT:
|
|
281
|
+
if (flags & SET_VERSION)
|
|
282
|
+
rc = putWithVersion(txn, dbi, &key, &value, flags & (MDB_NOOVERWRITE | MDB_NODUPDATA | MDB_APPEND | MDB_APPENDDUP), setVersion);
|
|
283
|
+
else
|
|
284
|
+
rc = mdb_put(txn, dbi, &key, &value, flags & (MDB_NOOVERWRITE | MDB_NODUPDATA | MDB_APPEND | MDB_APPENDDUP));
|
|
285
|
+
if (flags & COMPRESSIBLE)
|
|
286
|
+
free(value.mv_data);
|
|
287
|
+
//fprintf(stdout, "put %u \n", key.mv_size);
|
|
288
|
+
break;
|
|
289
|
+
case DEL:
|
|
290
|
+
rc = mdb_del(txn, dbi, &key, nullptr);
|
|
291
|
+
break;
|
|
292
|
+
case DEL_VALUE:
|
|
293
|
+
rc = mdb_del(txn, dbi, &key, &value);
|
|
294
|
+
if (flags & COMPRESSIBLE)
|
|
295
|
+
free(value.mv_data);
|
|
296
|
+
break;
|
|
297
|
+
case START_BLOCK: case START_CONDITION_BLOCK:
|
|
298
|
+
rc = validated ? 0 : MDB_NOTFOUND;
|
|
299
|
+
if (validated)
|
|
300
|
+
validatedDepth++;
|
|
301
|
+
conditionDepth++;
|
|
302
|
+
break;
|
|
303
|
+
case USER_CALLBACK:
|
|
304
|
+
worker->finishedProgress = false;
|
|
305
|
+
worker->progressStatus = 2;
|
|
306
|
+
rc = 0;
|
|
307
|
+
if (flags & USER_CALLBACK_STRICT_ORDER) {
|
|
308
|
+
std::atomic_fetch_or((std::atomic<uint32_t>*) start, (uint32_t) FINISHED_OPERATION); // mark it as finished so it is processed
|
|
309
|
+
while (!worker->finishedProgress) {
|
|
310
|
+
worker->WaitForCallbacks(&txn, conditionDepth == 0, nullptr);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
break;
|
|
314
|
+
case DROP_DB:
|
|
315
|
+
rc = mdb_drop(txn, dbi, (flags & DELETE_DATABASE) ? 1 : 0);
|
|
316
|
+
break;
|
|
317
|
+
case POINTER_NEXT:
|
|
318
|
+
instruction = (uint32_t*)(size_t) * ((double*)instruction);
|
|
319
|
+
goto next_inst;
|
|
320
|
+
default:
|
|
321
|
+
fprintf(stderr, "Unknown flags %u %p\n", flags, start);
|
|
322
|
+
fprintf(stderr, "flags after message %u\n", *start);
|
|
323
|
+
worker->ReportError("Unknown flags\n");
|
|
324
|
+
return 22;
|
|
325
|
+
}
|
|
326
|
+
if (rc) {
|
|
327
|
+
if (!(rc == MDB_KEYEXIST || rc == MDB_NOTFOUND)) {
|
|
328
|
+
if (worker) {
|
|
329
|
+
worker->ReportError(mdb_strerror(rc));
|
|
330
|
+
} else {
|
|
331
|
+
return rc;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
flags = FINISHED_OPERATION | FAILED_CONDITION;
|
|
335
|
+
}
|
|
336
|
+
else
|
|
337
|
+
flags = FINISHED_OPERATION;
|
|
338
|
+
} else
|
|
339
|
+
flags = FINISHED_OPERATION | FAILED_CONDITION;
|
|
340
|
+
//fprintf(stderr, "finished flag %p\n", flags);
|
|
341
|
+
if (overlappedWord) {
|
|
342
|
+
std::atomic_fetch_or((std::atomic<uint32_t>*) start, flags);
|
|
343
|
+
overlappedWord = false;
|
|
344
|
+
} else
|
|
345
|
+
*start |= flags;
|
|
346
|
+
} while(worker); // keep iterating in async/multiple-instruction mode, just one instruction in sync mode
|
|
347
|
+
return rc;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
void WriteWorker::Write() {
|
|
351
|
+
int rc;
|
|
352
|
+
finishedProgress = true;
|
|
353
|
+
unsigned int envFlags;
|
|
354
|
+
mdb_env_get_flags(env, &envFlags);
|
|
355
|
+
pthread_mutex_lock(envForTxn->writingLock);
|
|
356
|
+
#ifdef _WIN32
|
|
357
|
+
rc = mdb_txn_begin(env, nullptr, (envForTxn->jsFlags & MDB_OVERLAPPINGSYNC) ? MDB_NOSYNC : 0, &txn);
|
|
358
|
+
#else
|
|
359
|
+
int retries = 0;
|
|
360
|
+
retry:
|
|
361
|
+
rc = mdb_txn_begin(env, nullptr, (envForTxn->jsFlags & MDB_OVERLAPPINGSYNC) ? MDB_NOSYNC : 0, &txn);
|
|
362
|
+
if (rc == EINVAL) {
|
|
363
|
+
if (retries++ < 10) {
|
|
364
|
+
sleep(1);
|
|
365
|
+
goto retry;
|
|
366
|
+
}
|
|
367
|
+
return ReportError("Invalid parameter, which is often due to more transactions than available robust locked mutexes or semaphors (see docs for more info)");
|
|
368
|
+
}
|
|
369
|
+
#endif
|
|
370
|
+
if (rc != 0) {
|
|
371
|
+
return ReportError(mdb_strerror(rc));
|
|
372
|
+
}
|
|
373
|
+
hasError = false;
|
|
374
|
+
rc = DoWrites(txn, envForTxn, instructions, this);
|
|
375
|
+
|
|
376
|
+
if (rc || hasError)
|
|
377
|
+
mdb_txn_abort(txn);
|
|
378
|
+
else
|
|
379
|
+
rc = mdb_txn_commit(txn);
|
|
380
|
+
txn = nullptr;
|
|
381
|
+
pthread_mutex_unlock(envForTxn->writingLock);
|
|
382
|
+
if (rc || hasError) {
|
|
383
|
+
std::atomic_fetch_or((std::atomic<uint32_t>*) instructions, (uint32_t) TXN_HAD_ERROR);
|
|
384
|
+
if (rc)
|
|
385
|
+
ReportError(mdb_strerror(rc));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
std::atomic_fetch_or((std::atomic<uint32_t>*) instructions, (uint32_t) TXN_COMMITTED);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
void AsyncWriteWorker::OnProgress(const char* data, size_t count) {
|
|
392
|
+
if (progressStatus == 1) {
|
|
393
|
+
Callback().Call({ Number::New(Env(), progressStatus)});
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (finishedProgress)
|
|
397
|
+
return;
|
|
398
|
+
pthread_mutex_lock(envForTxn->writingLock);
|
|
399
|
+
while(!txn) // possible to jump in after an interrupted txn here
|
|
400
|
+
pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
|
|
401
|
+
envForTxn->writeTxn = new TxnTracked(txn, 0);
|
|
402
|
+
finishedProgress = true;
|
|
403
|
+
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
|
|
404
|
+
napi_create_int32(Env(), progressStatus, &arg);
|
|
405
|
+
napi_call_function(Env(), Env().Undefined(), Callback().Value(), 1, &arg, &result);
|
|
406
|
+
delete envForTxn->writeTxn;
|
|
407
|
+
envForTxn->writeTxn = nullptr;
|
|
408
|
+
pthread_cond_signal(envForTxn->writingCond);
|
|
409
|
+
pthread_mutex_unlock(envForTxn->writingLock);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
void AsyncWriteWorker::OnOK() {
|
|
413
|
+
finishedProgress = true;
|
|
414
|
+
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
|
|
415
|
+
napi_create_int32(Env(), 0, &arg);
|
|
416
|
+
napi_call_function(Env(), Env().Undefined(), Callback().Value(), 1, &arg, &result);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
Value EnvWrap::startWriting(const Napi::CallbackInfo& info) {
|
|
420
|
+
if (!this->env) {
|
|
421
|
+
return throwError(info.Env(), "The environment is already closed.");
|
|
422
|
+
}
|
|
423
|
+
size_t instructionAddress = info[0].As<Number>().Int64Value();
|
|
424
|
+
AsyncWriteWorker* worker = new AsyncWriteWorker(this->env, this, (uint32_t*) instructionAddress, info[1].As<Function>());
|
|
425
|
+
this->writeWorker = worker;
|
|
426
|
+
worker->Queue();
|
|
427
|
+
return info.Env().Undefined();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
NAPI_FUNCTION(EnvWrap::write) {
|
|
431
|
+
ARGS(2)
|
|
432
|
+
GET_INT64_ARG(0);
|
|
433
|
+
EnvWrap* ew = (EnvWrap*) i64;
|
|
434
|
+
if (!ew->env) {
|
|
435
|
+
napi_throw_error(env, nullptr, "The environment is already closed.");
|
|
436
|
+
RETURN_UNDEFINED;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
napi_get_value_int64(env, args[1], &i64);
|
|
440
|
+
uint32_t* instructionAddress = (uint32_t*) i64;
|
|
441
|
+
int rc = 0;
|
|
442
|
+
if (instructionAddress)
|
|
443
|
+
rc = WriteWorker::DoWrites(ew->writeTxn->txn, ew, instructionAddress, nullptr);
|
|
444
|
+
else if (ew->writeWorker) {
|
|
445
|
+
pthread_cond_signal(ew->writingCond);
|
|
446
|
+
}
|
|
447
|
+
if (rc && !(rc == MDB_KEYEXIST || rc == MDB_NOTFOUND)) {
|
|
448
|
+
throwLmdbError(env, rc);
|
|
449
|
+
RETURN_UNDEFINED;
|
|
450
|
+
}
|
|
451
|
+
RETURN_UNDEFINED;
|
|
452
|
+
}
|