@ohos-ports/lmdb 2.5.3 → 2.8.5-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 (124) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +72 -28
  3. package/SECURITY.md +12 -12
  4. package/bin/download-prebuilds.js +15 -0
  5. package/binding.gyp +115 -111
  6. package/build/Release/lmdb.node +0 -0
  7. package/caching.js +212 -156
  8. package/dependencies/lmdb/libraries/liblmdb/COPYRIGHT +20 -20
  9. package/dependencies/lmdb/libraries/liblmdb/Doxyfile +1631 -1631
  10. package/dependencies/lmdb/libraries/liblmdb/LICENSE +47 -47
  11. package/dependencies/lmdb/libraries/liblmdb/Makefile +136 -136
  12. package/dependencies/lmdb/libraries/liblmdb/chacha8.c +183 -183
  13. package/dependencies/lmdb/libraries/liblmdb/chacha8.h +14 -14
  14. package/dependencies/lmdb/libraries/liblmdb/crypto.c +121 -121
  15. package/dependencies/lmdb/libraries/liblmdb/intro.doc +192 -192
  16. package/dependencies/lmdb/libraries/liblmdb/lmdb.h +28 -3
  17. package/dependencies/lmdb/libraries/liblmdb/mdb.c +157 -31
  18. package/dependencies/lmdb/libraries/liblmdb/mdb_copy.1 +74 -74
  19. package/dependencies/lmdb/libraries/liblmdb/mdb_copy.c +106 -106
  20. package/dependencies/lmdb/libraries/liblmdb/mdb_drop.1 +53 -53
  21. package/dependencies/lmdb/libraries/liblmdb/mdb_drop.c +154 -154
  22. package/dependencies/lmdb/libraries/liblmdb/mdb_dump.1 +94 -94
  23. package/dependencies/lmdb/libraries/liblmdb/mdb_dump.c +333 -333
  24. package/dependencies/lmdb/libraries/liblmdb/mdb_load.1 +97 -97
  25. package/dependencies/lmdb/libraries/liblmdb/mdb_load.c +530 -530
  26. package/dependencies/lmdb/libraries/liblmdb/mdb_stat.1 +83 -83
  27. package/dependencies/lmdb/libraries/liblmdb/mdb_stat.c +276 -276
  28. package/dependencies/lmdb/libraries/liblmdb/midl-sparse.c +452 -0
  29. package/dependencies/lmdb/libraries/liblmdb/midl.c +452 -452
  30. package/dependencies/lmdb/libraries/liblmdb/midl.h +208 -208
  31. package/dependencies/lmdb/libraries/liblmdb/module.c +101 -101
  32. package/dependencies/lmdb/libraries/liblmdb/module.h +16 -16
  33. package/dependencies/lmdb/libraries/liblmdb/mtest.c +178 -178
  34. package/dependencies/lmdb/libraries/liblmdb/mtest2.c +124 -124
  35. package/dependencies/lmdb/libraries/liblmdb/mtest3.c +133 -133
  36. package/dependencies/lmdb/libraries/liblmdb/mtest4.c +168 -168
  37. package/dependencies/lmdb/libraries/liblmdb/mtest5.c +135 -135
  38. package/dependencies/lmdb/libraries/liblmdb/mtest6.c +141 -141
  39. package/dependencies/lmdb/libraries/liblmdb/mtest_enc.c +190 -190
  40. package/dependencies/lmdb/libraries/liblmdb/mtest_enc2.c +189 -189
  41. package/dependencies/lmdb/libraries/liblmdb/mtest_remap.c +177 -177
  42. package/dependencies/lmdb/libraries/liblmdb/sample-bdb.txt +73 -73
  43. package/dependencies/lmdb/libraries/liblmdb/sample-mdb.txt +62 -62
  44. package/dependencies/lmdb/libraries/liblmdb/tooltag +27 -27
  45. package/dependencies/lmdb-data-v1/libraries/liblmdb/CHANGES +266 -0
  46. package/dependencies/lmdb-data-v1/libraries/liblmdb/COPYRIGHT +20 -20
  47. package/dependencies/lmdb-data-v1/libraries/liblmdb/Doxyfile +1631 -1631
  48. package/dependencies/lmdb-data-v1/libraries/liblmdb/LICENSE +47 -47
  49. package/dependencies/lmdb-data-v1/libraries/liblmdb/intro.doc +192 -192
  50. package/dependencies/lmdb-data-v1/libraries/liblmdb/lmdb.h +1608 -1653
  51. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb.c +10320 -11359
  52. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.1 +55 -61
  53. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.c +82 -84
  54. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.1 +75 -81
  55. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.c +319 -319
  56. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.1 +84 -84
  57. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.c +496 -492
  58. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.1 +64 -70
  59. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.c +263 -264
  60. package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.c +359 -421
  61. package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.h +186 -200
  62. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest.c +177 -177
  63. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest2.c +124 -124
  64. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest3.c +133 -133
  65. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest4.c +168 -168
  66. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest5.c +135 -135
  67. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest6.c +141 -141
  68. package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-bdb.txt +73 -73
  69. package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-mdb.txt +62 -62
  70. package/dependencies/lmdb-data-v1/libraries/liblmdb/tooltag +22 -27
  71. package/dependencies/lz4/LICENSE +11 -11
  72. package/dependencies/lz4/lib/LICENSE +24 -24
  73. package/dependencies/lz4/lib/Makefile +225 -0
  74. package/dependencies/lz4/lib/README.md +169 -137
  75. package/dependencies/lz4/lib/dll/example/Makefile +63 -0
  76. package/dependencies/lz4/lib/dll/example/README.md +69 -69
  77. package/dependencies/lz4/lib/dll/liblz4.def +62 -62
  78. package/dependencies/lz4/lib/liblz4-dll.rc.in +35 -35
  79. package/dependencies/lz4/lib/liblz4.pc.in +14 -14
  80. package/dependencies/lz4/lib/lz4.c +2722 -2495
  81. package/dependencies/lz4/lib/lz4.h +842 -774
  82. package/dependencies/lz4/lib/lz4file.c +311 -0
  83. package/dependencies/lz4/lib/lz4file.h +93 -0
  84. package/dependencies/lz4/lib/lz4frame.c +2078 -1899
  85. package/dependencies/lz4/lib/lz4frame.h +692 -623
  86. package/dependencies/lz4/lib/lz4frame_static.h +47 -47
  87. package/dependencies/lz4/lib/lz4hc.c +1631 -1615
  88. package/dependencies/lz4/lib/lz4hc.h +413 -413
  89. package/dependencies/lz4/lib/xxhash.c +1030 -1030
  90. package/dependencies/lz4/lib/xxhash.h +328 -328
  91. package/dependencies/v8/v8-fast-api-calls-v16.h +790 -790
  92. package/dependencies/v8/v8-fast-api-calls.h +784 -888
  93. package/dist/index.cjs +1050 -584
  94. package/dist/index.cjs.map +1 -1
  95. package/index.d.cts +420 -0
  96. package/index.d.ts +450 -385
  97. package/index.js +38 -37
  98. package/keys.js +116 -102
  99. package/level.js +26 -26
  100. package/native.js +8 -5
  101. package/node-index.js +21 -17
  102. package/open.js +51 -17
  103. package/package.json +63 -21
  104. package/read.js +338 -95
  105. package/src/compression.cpp +12 -9
  106. package/src/cursor.cpp +82 -31
  107. package/src/dbi.cpp +78 -40
  108. package/src/env.cpp +336 -68
  109. package/src/lmdb-js.cpp +64 -64
  110. package/src/lmdb-js.h +83 -49
  111. package/src/misc.cpp +297 -28
  112. package/src/ordered-binary.cpp +46 -46
  113. package/src/txn.cpp +210 -257
  114. package/src/v8-functions.cpp +170 -166
  115. package/src/windows.c +34 -34
  116. package/src/writer.cpp +169 -84
  117. package/util/RangeIterable.js +112 -17
  118. package/util/set-optional-deps.cjs +14 -10
  119. package/util/when.js +8 -8
  120. package/write.js +145 -121
  121. package/dependencies/lmdb-data-v1/libraries/liblmdb/Makefile +0 -118
  122. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_drop.1 +0 -40
  123. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_drop.c +0 -135
  124. package/rollup.config.js +0 -12
package/src/env.cpp CHANGED
@@ -1,4 +1,5 @@
1
- #include "lmdb-js.h"
1
+ #include "lmdb-js.h"
2
+ #include <atomic>
2
3
  #ifndef _WIN32
3
4
  #include <sys/types.h>
4
5
  #include <sys/stat.h>
@@ -9,14 +10,22 @@ using namespace Napi;
9
10
  #define IGNORE_NOTFOUND (1)
10
11
 
11
12
  env_tracking_t* EnvWrap::envTracking = EnvWrap::initTracking();
13
+ thread_local std::vector<EnvWrap*>* EnvWrap::openEnvWraps = nullptr;
14
+ thread_local js_buffers_t* EnvWrap::sharedBuffers = nullptr;
15
+ //thread_local std::unordered_map<void*, buffer_info_t>* EnvWrap::sharedBuffers = nullptr;
16
+ void* getSharedBuffers() {
17
+ return (void*) EnvWrap::sharedBuffers;
18
+ }
12
19
 
13
20
  env_tracking_t* EnvWrap::initTracking() {
14
21
  env_tracking_t* tracking = new env_tracking_t;
15
22
  tracking->envsLock = new pthread_mutex_t;
16
23
  pthread_mutex_init(tracking->envsLock, nullptr);
24
+ tracking->getSharedBuffers = getSharedBuffers;
17
25
  return tracking;
18
26
  }
19
- thread_local std::vector<EnvWrap*>* EnvWrap::openEnvWraps = nullptr;
27
+ static napi_ref testRef;
28
+ static napi_env testRefEnv;
20
29
  void EnvWrap::cleanupEnvWraps(void* data) {
21
30
  if (openEnvWraps)
22
31
  free(openEnvWraps);
@@ -39,11 +48,12 @@ EnvWrap::EnvWrap(const CallbackInfo& info) : ObjectWrap<EnvWrap>(info) {
39
48
  this->writeTxn = nullptr;
40
49
  this->writeWorker = nullptr;
41
50
  this->readTxnRenewed = false;
51
+ this->hasWrites = false;
42
52
  this->writingLock = new pthread_mutex_t;
43
53
  this->writingCond = new pthread_cond_t;
44
54
  info.This().As<Object>().Set("address", Number::New(info.Env(), (size_t) this));
45
55
  pthread_mutex_init(this->writingLock, nullptr);
46
- pthread_cond_init(this->writingCond, nullptr);
56
+ cond_init(this->writingCond);
47
57
  }
48
58
  MDB_env* foundEnv;
49
59
  const int EXISTING_ENV_FOUND = 10;
@@ -77,6 +87,7 @@ int checkExistingEnvs(mdb_filehandle_t fd, MDB_env* env) {
77
87
  envRef.inode = inode;
78
88
  envRef.env = env;
79
89
  envRef.count = 1;
90
+ envRef.hasWrites = false;
80
91
  EnvWrap::envTracking->envs.push_back(envRef);
81
92
  return 0;
82
93
  }
@@ -105,6 +116,10 @@ void EnvWrap::cleanupStrayTxns() {
105
116
  tw->removeFromEnvWrap();
106
117
  }
107
118
  }
119
+ void EnvWrap::consolidateTxns() {
120
+ // sort read txns by txn id, and then abort newer ones that we can just reference older ones with.
121
+
122
+ }
108
123
 
109
124
  class SyncWorker : public AsyncWorker {
110
125
  public:
@@ -123,22 +138,28 @@ class SyncWorker : public AsyncWorker {
123
138
  napi_value result; // we use direct napi call here because node-addon-api interface with throw a fatal error if a worker thread is terminating
124
139
  napi_call_function(Env(), Env().Undefined(), Callback().Value(), 0, {}, &result);
125
140
  }
141
+ void OnError(const Error& e) {
142
+ napi_value result; // we use direct napi call here because node-addon-api interface with throw a fatal error if a worker thread is terminating
143
+ napi_value arg = e.Value();
144
+ napi_call_function(Env(), Env().Undefined(), Callback().Value(), 1, &arg, &result);
145
+ }
126
146
 
127
147
  void Execute() {
128
- #ifdef _WIN32
129
- int rc = mdb_env_sync(env->env, 1);
130
- #else
131
- int retries = 0;
132
- retry:
133
- int rc = mdb_env_sync(env->env, 1);
134
- if (rc == EINVAL) {
135
- if (retries++ < 10) {
136
- sleep(1);
137
- goto retry;
138
- }
139
- return SetError("Invalid parameter, which is often due to more transactions than available robust locked mutexes or semaphors (see docs for more info)");
140
- }
141
- #endif
148
+ #ifdef _WIN32
149
+ int rc = mdb_env_sync(env->env, 1);
150
+ #else
151
+ int retries = 0;
152
+ retry:
153
+ int rc = mdb_env_sync(env->env, 1);
154
+ #ifdef MDB_LOCK_FAILURE
155
+ if (rc == MDB_LOCK_FAILURE) {
156
+ if (retries++ < 4) {
157
+ sleep(1);
158
+ goto retry;
159
+ }
160
+ }
161
+ #endif
162
+ #endif
142
163
  if (rc != 0) {
143
164
  SetError(mdb_strerror(rc));
144
165
  }
@@ -160,8 +181,7 @@ class CopyWorker : public AsyncWorker {
160
181
  void Execute() {
161
182
  int rc = mdb_env_copy2(env, path.c_str(), flags);
162
183
  if (rc != 0) {
163
- fprintf(stderr, "Error on copy code: %u\n", rc);
164
- SetError("Error on copy");
184
+ SetError(mdb_strerror(rc));
165
185
  }
166
186
  }
167
187
 
@@ -170,21 +190,22 @@ class CopyWorker : public AsyncWorker {
170
190
  std::string path;
171
191
  int flags;
172
192
  };
173
- MDB_txn* EnvWrap::getReadTxn() {
174
- MDB_txn* txn = writeTxn ? writeTxn->txn : nullptr;
175
- if (txn)
176
- return txn;
177
- txn = currentReadTxn;
178
- if (readTxnRenewed)
179
- return txn;
180
- if (txn) {
181
- if (mdb_txn_renew(txn))
182
- return nullptr; // if there was an error, signal with nullptr and let error propagate with last_error
183
- } else {
184
- fprintf(stderr, "No current read transaction available");
185
- return nullptr;
193
+
194
+ MDB_txn* EnvWrap::getReadTxn(int64_t tw_address) {
195
+ MDB_txn* txn;
196
+ if (tw_address) // explicit txn
197
+ txn = ((TxnWrap*)tw_address)->txn;
198
+ else if (writeTxn && (txn = writeTxn->txn)) {
199
+ return txn; // no need to renew write txn
200
+ } else // default to current read txn
201
+ txn = currentReadTxn;
202
+ int rc = mdb_txn_renew(txn); // always try to renew
203
+ if (rc) {
204
+ if (!txn)
205
+ fprintf(stderr, "No current read transaction available");
206
+ if (rc != EINVAL)
207
+ return nullptr; // if there was a real error, signal with nullptr and let error propagate with last_error
186
208
  }
187
- readTxnRenewed = true;
188
209
  return txn;
189
210
  }
190
211
 
@@ -221,8 +242,7 @@ Napi::Value EnvWrap::open(const CallbackInfo& info) {
221
242
  if (!keyBytesValue.IsTypedArray())
222
243
  fprintf(stderr, "Invalid key buffer\n");
223
244
  size_t keyBufferLength;
224
- napi_get_typedarray_info(info.Env(), keyBytesValue, nullptr, &keyBufferLength, &keyBuffer, nullptr, nullptr);
225
- setFlagFromValue(&jsFlags, SEPARATE_FLUSHED, "separateFlushed", false, options);
245
+ napi_get_buffer_info(info.Env(), keyBytesValue, &keyBuffer, &keyBufferLength);
226
246
  String path = options.Get("path").As<String>();
227
247
  std::string pathString = path.Utf8Value();
228
248
  // Parse the maxDbs option
@@ -236,7 +256,7 @@ Napi::Value EnvWrap::open(const CallbackInfo& info) {
236
256
  option = options.Get("mapSize");
237
257
  if (option.IsNumber())
238
258
  mapSize = option.As<Number>().Int64Value();
239
- int pageSize = 4096;
259
+ int pageSize = 0;
240
260
  // Parse the mapSize option
241
261
  option = options.Get("pageSize");
242
262
  if (option.IsNumber())
@@ -262,7 +282,7 @@ Napi::Value EnvWrap::open(const CallbackInfo& info) {
262
282
  napiEnv = info.Env();
263
283
  rc = openEnv(flags, jsFlags, (const char*)pathString.c_str(), (char*) keyBuffer, compression, maxDbs, maxReaders, mapSize, pageSize, encryptKey.empty() ? nullptr : (char*)encryptKey.c_str());
264
284
  //delete[] pathBytes;
265
- if (rc < 0)
285
+ if (rc != 0)
266
286
  return throwLmdbError(info.Env(), rc);
267
287
  napi_add_env_cleanup_hook(napiEnv, cleanup, this);
268
288
  return info.Env().Undefined();
@@ -272,7 +292,9 @@ int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer,
272
292
  this->keyBuffer = keyBuffer;
273
293
  this->compression = compression;
274
294
  this->jsFlags = jsFlags;
275
-
295
+ #ifdef MDB_OVERLAPPINGSYNC
296
+ MDB_metrics* metrics;
297
+ #endif
276
298
  int rc;
277
299
  rc = mdb_env_set_maxdbs(env, maxDbs);
278
300
  if (rc) goto fail;
@@ -281,7 +303,8 @@ int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer,
281
303
  rc = mdb_env_set_mapsize(env, mapSize);
282
304
  if (rc) goto fail;
283
305
  #ifdef MDB_RPAGE_CACHE
284
- rc = mdb_env_set_pagesize(env, pageSize);
306
+ if (pageSize)
307
+ rc = mdb_env_set_pagesize(env, pageSize);
285
308
  if (rc) goto fail;
286
309
  #endif
287
310
  if ((size_t) encryptionKey > 100) {
@@ -296,15 +319,24 @@ int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer,
296
319
  if (rc != 0) goto fail;
297
320
  }
298
321
 
322
+ if (flags & MDB_NOLOCK) {
323
+ fprintf(stderr, "You chose to use MDB_NOLOCK which is not officially supported by node-lmdb. You have been warned!\n");
324
+ }
325
+ #ifdef MDB_OVERLAPPINGSYNC
299
326
  if (flags & MDB_OVERLAPPINGSYNC) {
300
327
  flags |= MDB_PREVSNAPSHOT;
301
328
  }
302
-
303
- if (flags & MDB_NOLOCK) {
304
- fprintf(stderr, "You chose to use MDB_NOLOCK which is not officially supported by node-lmdb. You have been warned!\n");
329
+ mdb_env_set_callback(env, checkExistingEnvs);
330
+ trackMetrics = !!(flags & MDB_TRACK_METRICS);
331
+ if (trackMetrics) {
332
+ metrics = new MDB_metrics;
333
+ memset(metrics, 0, sizeof(MDB_metrics));
334
+ rc = mdb_env_set_userctx(env, (void*) metrics);
335
+ if (rc) goto fail;
305
336
  }
306
- mdb_env_set_check_fd(env, checkExistingEnvs);
337
+ #endif
307
338
 
339
+ timeTxnWaiting = 0;
308
340
  // Set MDB_NOTLS to enable multiple read-only transactions on the same thread (in this case, the nodejs main thread)
309
341
  flags |= MDB_NOTLS;
310
342
  // TODO: make file attributes configurable
@@ -313,14 +345,27 @@ int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer,
313
345
  rc = mdb_env_open(env, path, flags, 0664);
314
346
 
315
347
  if (rc != 0) {
316
- mdb_env_close(env);
348
+ #ifdef MDB_OVERLAPPINGSYNC
349
+ if (trackMetrics) {
350
+ delete metrics;
351
+ }
352
+ #endif
317
353
  if (rc == EXISTING_ENV_FOUND) {
354
+ mdb_env_close(env);
318
355
  env = foundEnv;
319
- } else
356
+ } else {
357
+ this->jsFlags |= OPEN_FAILED;
358
+ closeEnv(true);
359
+ pthread_mutex_unlock(envTracking->envsLock);
320
360
  goto fail;
361
+ }
321
362
  }
322
363
  mdb_env_get_flags(env, (unsigned int*) &flags);
323
- if ((jsFlags & DELETE_ON_CLOSE) || (flags & MDB_OVERLAPPINGSYNC)) {
364
+ if ((jsFlags & DELETE_ON_CLOSE)
365
+ #ifdef MDB_OVERLAPPINGSYNC
366
+ || (flags & MDB_OVERLAPPINGSYNC)
367
+ #endif
368
+ ) {
324
369
  if (!openEnvWraps) {
325
370
  openEnvWraps = new std::vector<EnvWrap*>;
326
371
  napi_add_env_cleanup_hook(napiEnv, cleanupEnvWraps, nullptr);
@@ -331,13 +376,13 @@ int EnvWrap::openEnv(int flags, int jsFlags, const char* path, char* keyBuffer,
331
376
  return 0;
332
377
 
333
378
  fail:
334
- pthread_mutex_unlock(envTracking->envsLock);
335
379
  env = nullptr;
336
380
  return rc;
337
381
  }
338
382
  Napi::Value EnvWrap::getMaxKeySize(const CallbackInfo& info) {
339
383
  return Number::New(info.Env(), mdb_env_get_maxkeysize(this->env));
340
384
  }
385
+
341
386
  NAPI_FUNCTION(getEnvFlags) {
342
387
  ARGS(1)
343
388
  GET_INT64_ARG(0);
@@ -350,9 +395,9 @@ NAPI_FUNCTION(getEnvFlags) {
350
395
  NAPI_FUNCTION(setJSFlags) {
351
396
  ARGS(2)
352
397
  GET_INT64_ARG(0);
353
- EnvWrap* ew = (EnvWrap*) i64;
354
- int64_t jsFlags;
355
- napi_get_value_int64(env, args[1], &jsFlags);
398
+ EnvWrap* ew = (EnvWrap*) i64;
399
+ int64_t jsFlags;
400
+ napi_get_value_int64(env, args[1], &jsFlags);
356
401
  ew->jsFlags = jsFlags;
357
402
  RETURN_UNDEFINED;
358
403
  }
@@ -377,21 +422,200 @@ NAPI_FUNCTION(EnvWrap::onExit) {
377
422
  NAPI_FUNCTION(getEnvsPointer) {
378
423
  napi_value returnValue;
379
424
  napi_create_double(env, (double) (size_t) EnvWrap::envTracking, &returnValue);
425
+ if (!EnvWrap::sharedBuffers) {
426
+ EnvWrap::sharedBuffers = new js_buffers_t;
427
+ EnvWrap::sharedBuffers->nextId = 0;
428
+ pthread_mutex_init(&EnvWrap::sharedBuffers->modification_lock, nullptr);
429
+ }
380
430
  return returnValue;
381
431
  }
382
432
 
383
433
  NAPI_FUNCTION(setEnvsPointer) {
384
434
  // If another version of lmdb-js is running, switch to using its list of envs
385
- ARGS(1)
435
+ ARGS(2)
386
436
  GET_INT64_ARG(0);
387
- env_tracking_t* adoptedTracking = (env_tracking_t*) i64;
437
+ env_tracking_t* adoptedTracking = (env_tracking_t*) i64;
388
438
  // copy any existing ones over to the central one
389
439
  adoptedTracking->envs.assign(EnvWrap::envTracking->envs.begin(), EnvWrap::envTracking->envs.end());
390
440
  EnvWrap::envTracking = adoptedTracking;
441
+ js_buffers_t* adoptedBuffers = (js_buffers_t*) adoptedTracking->getSharedBuffers();
442
+ if (EnvWrap::sharedBuffers && adoptedBuffers != EnvWrap::sharedBuffers) {
443
+ free(EnvWrap::sharedBuffers);
444
+ }
445
+ EnvWrap::sharedBuffers = adoptedBuffers;
446
+ RETURN_UNDEFINED;
447
+ }
448
+
449
+ napi_finalize cleanupSharedExternal = [](napi_env env, void* data, void* buffer_info) {
450
+ // Data belongs to LMDB, we shouldn't free it here
451
+ };
452
+
453
+ napi_finalize cleanupAllocatedExternal = [](napi_env env, void* data, void* buffer_info) {
454
+ int32_t id = ((buffer_info_t*) buffer_info)->id;
455
+ pthread_mutex_lock(&EnvWrap::sharedBuffers->modification_lock);
456
+ for (auto bufferRef = EnvWrap::sharedBuffers->buffers.begin(); bufferRef != EnvWrap::sharedBuffers->buffers.end();) {
457
+ if (bufferRef->second.id == id) {
458
+ // fprintf(stderr, "erasing buffer on cleanpu %p\n", bufferRef->first);
459
+ bufferRef = EnvWrap::sharedBuffers->buffers.erase(bufferRef);
460
+ break;
461
+ }
462
+ bufferRef++;
463
+ }
464
+ pthread_mutex_unlock(&EnvWrap::sharedBuffers->modification_lock);
465
+ // We malloc'ed this data so free it
466
+ free(data);
467
+ };
468
+
469
+
470
+ NAPI_FUNCTION(getSharedBuffer) {
471
+ ARGS(2)
472
+ int32_t bufferId;
473
+ GET_UINT32_ARG(bufferId, 0);
474
+ GET_INT64_ARG(1);
475
+ EnvWrap* ew = (EnvWrap*) i64;
476
+ pthread_mutex_lock(&EnvWrap::sharedBuffers->modification_lock);
477
+ for (auto bufferRef = EnvWrap::sharedBuffers->buffers.begin(); bufferRef != EnvWrap::sharedBuffers->buffers.end(); bufferRef++) {
478
+ if (bufferRef->second.id == bufferId) {
479
+ char* start = bufferRef->first;
480
+ buffer_info_t* buffer = &bufferRef->second;
481
+ if (buffer->env == ew->env) {
482
+ //fprintf(stderr, "found exiting buffer for %u\n", bufferId);
483
+ napi_get_reference_value(env, buffer->ref, &returnValue);
484
+ pthread_mutex_unlock(&EnvWrap::sharedBuffers->modification_lock);
485
+ return returnValue;
486
+ }
487
+ if (buffer->env) {
488
+ // if for some reason it is different env that didn't get cleaned up
489
+ napi_value arrayBuffer;
490
+ napi_get_reference_value(env, buffer->ref, &arrayBuffer);
491
+ napi_detach_arraybuffer(env, arrayBuffer);
492
+ napi_delete_reference(env, buffer->ref);
493
+ }
494
+ char* end = buffer->end;
495
+ if (buffer->isSharedMap) // only memory mapped buffers are tied to envs
496
+ buffer->env = ew->env;
497
+ size_t size = end - start;
498
+ if (size > 0x100000000)
499
+ fprintf(stderr, "Getting invalid shared buffer size %llu from start: %llu to %end: %llu", size, start, end);
500
+ napi_create_external_arraybuffer(env, start, size,
501
+ buffer->isSharedMap ? cleanupSharedExternal : cleanupAllocatedExternal, (void*) buffer, &returnValue);
502
+ int64_t result;
503
+ napi_create_reference(env, returnValue, 1, &buffer->ref);
504
+ if (buffer->isSharedMap) {
505
+ napi_adjust_external_memory(env, -(int64_t) size, &result);
506
+ napi_value true_value;
507
+ napi_get_boolean(env, true, &true_value);
508
+ napi_set_named_property(env, returnValue, "isSharedMap", true_value);
509
+ }
510
+ pthread_mutex_unlock(&EnvWrap::sharedBuffers->modification_lock);
511
+ return returnValue;
512
+ }
513
+ }
514
+ pthread_mutex_unlock(&EnvWrap::sharedBuffers->modification_lock);
515
+ RETURN_UNDEFINED;
516
+ }
517
+ NAPI_FUNCTION(setTestRef) {
518
+ ARGS(1)
519
+ napi_create_reference(env, args[0], 1, &testRef);
520
+ testRefEnv = env;
521
+ RETURN_UNDEFINED
522
+ }
523
+
524
+ NAPI_FUNCTION(getTestRef) {
525
+ napi_value returnValue;
526
+ fprintf(stderr,"trying to get refernec\n");
527
+ napi_get_reference_value(env, testRef, &returnValue);
528
+ fprintf(stderr,"got refernec\n");
529
+ return returnValue;
530
+ }
531
+
532
+ NAPI_FUNCTION(directWrite) {
533
+ ARGS(4)
534
+ GET_INT64_ARG(0);
535
+ EnvWrap* ew = (EnvWrap*) i64;
536
+ napi_get_value_int64(env, args[1], &i64);
537
+ char* target = (char*) i64;
538
+ napi_get_value_int64(env, args[2], &i64);
539
+ void* source = (void*) i64;
540
+ uint32_t length;
541
+ GET_UINT32_ARG(length, 3);
542
+ mdb_filehandle_t fd;
543
+ mdb_env_get_fd(ew->env, &fd);
544
+ MDB_envinfo stat;
545
+ mdb_env_info(ew->env, &stat);
546
+ int64_t offset = target - (char*) stat.me_mapaddr;
547
+ if (offset > 0 && offset < (int64_t) stat.me_mapsize) {
548
+ #ifdef _WIN32
549
+ OVERLAPPED ov;
550
+ ov.Offset = offset;
551
+ ov.OffsetHigh = 0;
552
+ WriteFile(fd, source, length, nullptr, &ov);
553
+ #else
554
+ pwrite(fd, source, length, offset);
555
+ #endif
556
+ }
391
557
  RETURN_UNDEFINED;
392
558
  }
393
559
 
394
- void EnvWrap::closeEnv() {
560
+ int32_t EnvWrap::toSharedBuffer(MDB_env* env, uint32_t* keyBuffer, MDB_val data) {
561
+ unsigned int flags;
562
+ mdb_env_get_flags(env, (unsigned int*) &flags);
563
+ #ifdef MDB_RPAGE_CACHE
564
+ if (flags & MDB_REMAP_CHUNKS) {
565
+ *((uint32_t*)keyBuffer) = data.mv_size;
566
+ *((uint32_t*) (keyBuffer + 4)) = 0;
567
+ return -30000;
568
+ }
569
+ #endif
570
+ MDB_envinfo stat;
571
+ mdb_env_info(env, &stat);
572
+ size_t mapAddress = (size_t) (char*) stat.me_mapaddr;
573
+ size_t dataAddress = (size_t) (char*) data.mv_data;
574
+ size_t bufferStart;
575
+ uint64_t end;
576
+ if (dataAddress > mapAddress && (dataAddress + data.mv_size) <= (mapAddress + stat.me_mapsize)) {
577
+ // an address within the memory map
578
+ int64_t mapOffset = dataAddress - mapAddress;
579
+ size_t bufferPosition = (mapOffset + (mapOffset >> 4)) >> 32;
580
+ bufferStart = bufferPosition << 32;
581
+ bufferStart += mapAddress - (bufferStart >> 4);
582
+ end = bufferStart + 0xffffffffll;
583
+ if (end > mapAddress + stat.me_mapsize)
584
+ end = mapAddress + stat.me_mapsize;
585
+ } else {
586
+ // outside the memory map, usually because this is from the heap during a write txn or the mmap has been reallocated
587
+ bufferStart = (dataAddress >> 32) << 32;
588
+ if (!bufferStart) // can't use a memory address of 0 because it is considered a nullptr
589
+ bufferStart = 8;
590
+ end = bufferStart + 0xffffffffll;
591
+ }
592
+ if ((dataAddress + data.mv_size) > end) {
593
+ // crosses boundaries, create one-off for this address
594
+ bufferStart = dataAddress;
595
+ end = bufferStart + 0xffffffffll;
596
+ }
597
+ //fprintf(stderr, "mapAddress %p bufferStart %p", mapAddress, bufferStart);
598
+ pthread_mutex_lock(&sharedBuffers->modification_lock);
599
+ auto bufferSearch = sharedBuffers->buffers.find((char*)bufferStart);
600
+ size_t offset = dataAddress - bufferStart;
601
+ buffer_info_t bufferInfo;
602
+ if (bufferSearch == sharedBuffers->buffers.end()) {
603
+ bufferInfo.end = (char*) end;
604
+ bufferInfo.env = nullptr;
605
+ bufferInfo.isSharedMap = true;
606
+ bufferInfo.id = sharedBuffers->nextId++;
607
+ sharedBuffers->buffers.emplace((char*)bufferStart, bufferInfo);
608
+ } else {
609
+ bufferInfo = bufferSearch->second;
610
+ }
611
+ pthread_mutex_unlock(&sharedBuffers->modification_lock);
612
+ *keyBuffer = data.mv_size;
613
+ *(keyBuffer + 1) = bufferInfo.id;
614
+ *(keyBuffer + 2) = offset;
615
+ return -30001;
616
+ }
617
+
618
+ void EnvWrap::closeEnv(bool hasLock) {
395
619
  if (!env)
396
620
  return;
397
621
  if (openEnvWraps) {
@@ -405,21 +629,44 @@ void EnvWrap::closeEnv() {
405
629
  }
406
630
  napi_remove_env_cleanup_hook(napiEnv, cleanup, this);
407
631
  cleanupStrayTxns();
408
- pthread_mutex_lock(envTracking->envsLock);
632
+ if (!hasLock)
633
+ pthread_mutex_lock(envTracking->envsLock);
409
634
  for (auto envPath = envTracking->envs.begin(); envPath != envTracking->envs.end(); ) {
410
635
  if (envPath->env == env) {
411
636
  envPath->count--;
637
+ if (hasWrites)
638
+ envPath->hasWrites = true;
412
639
  if (envPath->count <= 0) {
413
640
  // last thread using it, we can really close it now
414
641
  unsigned int envFlags; // This is primarily useful for detecting termination of threads and sync'ing on their termination
415
642
  mdb_env_get_flags(env, &envFlags);
416
- if (envFlags & MDB_OVERLAPPINGSYNC) {
643
+ #ifdef MDB_OVERLAPPINGSYNC
644
+ if ((envFlags & MDB_OVERLAPPINGSYNC) && envPath->hasWrites) {
417
645
  mdb_env_sync(env, 1);
418
646
  }
647
+ if (envFlags & MDB_TRACK_METRICS) {
648
+ delete (MDB_metrics*) mdb_env_get_userctx(env);
649
+ }
650
+ #endif
419
651
  char* path;
420
652
  mdb_env_get_path(env, (const char**)&path);
421
653
  path = strdup(path);
422
654
  mdb_env_close(env);
655
+ pthread_mutex_lock(&sharedBuffers->modification_lock);
656
+ for (auto bufferRef = EnvWrap::sharedBuffers->buffers.begin(); bufferRef != EnvWrap::sharedBuffers->buffers.end();) {
657
+ if (bufferRef->second.env == env) {
658
+ napi_value arrayBuffer;
659
+ napi_get_reference_value(napiEnv, bufferRef->second.ref, &arrayBuffer);
660
+ napi_detach_arraybuffer(napiEnv, arrayBuffer);
661
+ napi_delete_reference(napiEnv, bufferRef->second.ref);
662
+ int64_t result;
663
+ if (bufferRef->second.id >= 0)
664
+ napi_adjust_external_memory(napiEnv, bufferRef->second.end - bufferRef->first, &result);
665
+ bufferRef = EnvWrap::sharedBuffers->buffers.erase(bufferRef);
666
+ } else
667
+ bufferRef++;
668
+ }
669
+ pthread_mutex_unlock(&sharedBuffers->modification_lock);
423
670
  if (jsFlags & DELETE_ON_CLOSE) {
424
671
  unlink(path);
425
672
  //unlink(strcat(envPath->path, "-lock"));
@@ -430,7 +677,8 @@ void EnvWrap::closeEnv() {
430
677
  }
431
678
  ++envPath;
432
679
  }
433
- pthread_mutex_unlock(envTracking->envsLock);
680
+ if (!hasLock)
681
+ pthread_mutex_unlock(envTracking->envsLock);
434
682
  env = nullptr;
435
683
  }
436
684
 
@@ -469,9 +717,8 @@ Napi::Value EnvWrap::freeStat(const CallbackInfo& info) {
469
717
  }
470
718
  int rc;
471
719
  MDB_stat stat;
472
- TxnWrap *txn;
473
- napi_unwrap(info.Env(), info[0], (void**)&txn);
474
- rc = mdb_stat(txn->txn, 0, &stat);
720
+ MDB_txn *txn = getReadTxn();
721
+ rc = mdb_stat(txn, 0, &stat);
475
722
  if (rc != 0) {
476
723
  return throwLmdbError(info.Env(), rc);
477
724
  }
@@ -502,6 +749,22 @@ Napi::Value EnvWrap::info(const CallbackInfo& info) {
502
749
  stats.Set("lastTxnId", Number::New(info.Env(), envinfo.me_last_txnid));
503
750
  stats.Set("maxReaders", Number::New(info.Env(), envinfo.me_maxreaders));
504
751
  stats.Set("numReaders", Number::New(info.Env(), envinfo.me_numreaders));
752
+ #ifdef MDB_OVERLAPPINGSYNC
753
+ if (this->trackMetrics) {
754
+ MDB_metrics* metrics = (MDB_metrics*) mdb_env_get_userctx(this->env);
755
+ stats.Set("timeStartTxns", Number::New(info.Env(), (double) metrics->time_start_txns / TICKS_PER_SECOND));
756
+ stats.Set("timeDuringTxns", Number::New(info.Env(), (double) metrics->time_during_txns / TICKS_PER_SECOND));
757
+ stats.Set("timePageFlushes", Number::New(info.Env(), (double) metrics->time_page_flushes / TICKS_PER_SECOND));
758
+ stats.Set("timeSync", Number::New(info.Env(), (double) metrics->time_sync / TICKS_PER_SECOND));
759
+ stats.Set("timeTxnWaiting", Number::New(info.Env(), (double) timeTxnWaiting / TICKS_PER_SECOND));
760
+ stats.Set("txns", Number::New(info.Env(), metrics->txns));
761
+ stats.Set("pageFlushes", Number::New(info.Env(), metrics->page_flushes));
762
+ stats.Set("pagesWritten", Number::New(info.Env(), metrics->pages_written));
763
+ stats.Set("writes", Number::New(info.Env(), metrics->writes));
764
+ stats.Set("puts", Number::New(info.Env(), metrics->puts));
765
+ stats.Set("deletes", Number::New(info.Env(), metrics->deletes));
766
+ }
767
+ #endif
505
768
  return stats;
506
769
  }
507
770
 
@@ -627,9 +890,16 @@ Napi::Value EnvWrap::commitTxn(const CallbackInfo& info) {
627
890
  pthread_mutex_unlock(this->writingLock);
628
891
  }
629
892
  delete currentTxn;
630
- if (rc)
631
- throwLmdbError(info.Env(), rc);
632
- return info.Env().Undefined();
893
+ if (rc == 0) {
894
+ hasWrites = true;
895
+ return Napi::Boolean::New(info.Env(), true);
896
+ }
897
+ #ifdef MDB_EMPTY_TXN
898
+ else if (rc == MDB_EMPTY_TXN)
899
+ return Napi::Boolean::New(info.Env(), false);
900
+ #endif
901
+ else
902
+ return throwLmdbError(info.Env(), rc);
633
903
  }
634
904
  Napi::Value EnvWrap::abortTxn(const CallbackInfo& info) {
635
905
  TxnTracked *currentTxn = this->writeTxn;
@@ -687,11 +957,6 @@ Napi::Value EnvWrap::sync(const CallbackInfo& info) {
687
957
  return info.Env().Undefined();
688
958
  }
689
959
 
690
- Napi::Value EnvWrap::resetCurrentReadTxn(const CallbackInfo& info) {
691
- mdb_txn_reset(this->currentReadTxn);
692
- this->readTxnRenewed = false;
693
- return info.Env().Undefined();
694
- }
695
960
  int32_t writeFFI(double ewPointer, uint64_t instructionAddress) {
696
961
  EnvWrap* ew = (EnvWrap*) (size_t) ewPointer;
697
962
  int rc;
@@ -715,6 +980,7 @@ void EnvWrap::setupExports(Napi::Env env, Object exports) {
715
980
  EnvWrap::InstanceMethod("commitTxn", &EnvWrap::commitTxn),
716
981
  EnvWrap::InstanceMethod("abortTxn", &EnvWrap::abortTxn),
717
982
  EnvWrap::InstanceMethod("sync", &EnvWrap::sync),
983
+ EnvWrap::InstanceMethod("resumeWriting", &EnvWrap::resumeWriting),
718
984
  EnvWrap::InstanceMethod("startWriting", &EnvWrap::startWriting),
719
985
  EnvWrap::InstanceMethod("stat", &EnvWrap::stat),
720
986
  EnvWrap::InstanceMethod("freeStat", &EnvWrap::freeStat),
@@ -723,7 +989,6 @@ void EnvWrap::setupExports(Napi::Env env, Object exports) {
723
989
  EnvWrap::InstanceMethod("readerList", &EnvWrap::readerList),
724
990
  EnvWrap::InstanceMethod("copy", &EnvWrap::copy),
725
991
  //EnvWrap::InstanceMethod("detachBuffer", &EnvWrap::detachBuffer),
726
- EnvWrap::InstanceMethod("resetCurrentReadTxn", &EnvWrap::resetCurrentReadTxn),
727
992
  });
728
993
  EXPORT_NAPI_FUNCTION("compress", compress);
729
994
  EXPORT_NAPI_FUNCTION("write", write);
@@ -732,6 +997,9 @@ void EnvWrap::setupExports(Napi::Env env, Object exports) {
732
997
  EXPORT_NAPI_FUNCTION("setEnvsPointer", setEnvsPointer);
733
998
  EXPORT_NAPI_FUNCTION("getEnvFlags", getEnvFlags);
734
999
  EXPORT_NAPI_FUNCTION("setJSFlags", setJSFlags);
1000
+ EXPORT_NAPI_FUNCTION("getSharedBuffer", getSharedBuffer);
1001
+ EXPORT_NAPI_FUNCTION("setTestRef", setTestRef);
1002
+ EXPORT_NAPI_FUNCTION("getTestRef", getTestRef);
735
1003
  EXPORT_FUNCTION_ADDRESS("writePtr", writeFFI);
736
1004
  //envTpl->InstanceTemplate()->SetInternalFieldCount(1);
737
1005
  exports.Set("Env", EnvClass);