@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/writer.cpp CHANGED
@@ -13,6 +13,7 @@ inline value?
13
13
  */
14
14
  #include "lmdb-js.h"
15
15
  #include <atomic>
16
+ #include <ctime>
16
17
  #ifndef _WIN32
17
18
  #include <unistd.h>
18
19
  #endif
@@ -34,6 +35,8 @@ const int HAS_KEY = 4;
34
35
  const int HAS_VALUE = 2;
35
36
  const int CONDITIONAL = 8;
36
37
  const int CONDITIONAL_VERSION = 0x100;
38
+ const int CONDITIONAL_VERSION_LESS_THAN = 0x800;
39
+ const int CONDITIONAL_ALLOW_NOTFOUND = 0x1000;
37
40
  const int SET_VERSION = 0x200;
38
41
  //const int HAS_INLINE_VALUE = 0x400;
39
42
  const int COMPRESSIBLE = 0x100000;
@@ -62,25 +65,13 @@ WriteWorker::WriteWorker(MDB_env* env, EnvWrap* envForTxn, uint32_t* instruction
62
65
  env(env) {
63
66
  //fprintf(stdout, "nextCompressibleArg %p\n", nextCompressibleArg);
64
67
  interruptionStatus = 0;
68
+ resultCode = 0;
65
69
  txn = nullptr;
66
70
  }
67
71
 
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
72
  void WriteWorker::SendUpdate() {
80
- fprintf(stderr, "This SendUpdate does not work!\n");
81
- }
82
- void AsyncWriteWorker::SendUpdate() {
83
- executionProgress->Send(nullptr, 0);
73
+ if (WriteWorker::threadSafeCallsEnabled)
74
+ napi_call_threadsafe_function(progress, nullptr, napi_tsfn_blocking);
84
75
  }
85
76
  MDB_txn* WriteWorker::AcquireTxn(int* flags) {
86
77
  bool commitSynchronously = *flags & TXN_SYNCHRONOUS_COMMIT;
@@ -89,16 +80,17 @@ MDB_txn* WriteWorker::AcquireTxn(int* flags) {
89
80
  pthread_mutex_lock(envForTxn->writingLock);
90
81
  retry:
91
82
  if (commitSynchronously && interruptionStatus == WORKER_WAITING) {
92
- //fprintf(stderr, "acquire interupting lock %p %u\n", this, commitSynchronously);
93
83
  interruptionStatus = INTERRUPT_BATCH;
94
84
  pthread_cond_signal(envForTxn->writingCond);
95
85
  pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
96
86
  if (interruptionStatus == RESTART_WORKER_TXN) {
97
87
  *flags |= TXN_FROM_WORKER;
98
88
  return nullptr;
89
+ } else if (interruptionStatus == WORKER_WAITING || interruptionStatus == INTERRUPT_BATCH) {
90
+ interruptionStatus = WORKER_WAITING;
91
+ goto retry;
99
92
  } else {
100
- interruptionStatus = WORKER_WAITING;
101
- goto retry;
93
+ return nullptr;
102
94
  }
103
95
  } else {
104
96
  //if (interruptionStatus == RESTART_WORKER_TXN)
@@ -112,48 +104,48 @@ MDB_txn* WriteWorker::AcquireTxn(int* flags) {
112
104
  }
113
105
 
114
106
  void WriteWorker::UnlockTxn() {
115
- //fprintf(stderr, "release txn %u\n", interruptionStatus);
116
107
  interruptionStatus = 0;
117
108
  pthread_cond_signal(envForTxn->writingCond);
118
109
  pthread_mutex_unlock(envForTxn->writingLock);
119
110
  }
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
111
  int WriteWorker::WaitForCallbacks(MDB_txn** txn, bool allowCommit, uint32_t* target) {
129
112
  int rc;
130
- //fprintf(stderr, "wait for callback %p\n", target);
131
113
  if (!finishedProgress)
132
114
  SendUpdate();
133
115
  pthread_cond_signal(envForTxn->writingCond);
134
116
  interruptionStatus = WORKER_WAITING;
117
+ uint64_t start;
118
+ if (envForTxn->trackMetrics)
119
+ start = get_time64();
135
120
  if (target) {
136
121
  uint64_t delay = 1;
137
122
  do {
138
123
  cond_timedwait(envForTxn->writingCond, envForTxn->writingLock, delay);
139
124
  delay = delay << 1ll;
140
- //if (delay > 500)
141
- //fprintf(stderr, "waited, %llu %p\n", delay, *target);
142
125
  if ((*target & 0xf) || (allowCommit && finishedProgress)) {
143
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
+ }
144
130
  interruptionStatus = 0;
145
131
  return 0;
146
132
  }
147
133
  } while(interruptionStatus != INTERRUPT_BATCH);
148
- } else
134
+ } else {
149
135
  pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
136
+ }
137
+ if (envForTxn->trackMetrics) {
138
+ envForTxn->timeTxnWaiting += get_time64() - start;
139
+ }
150
140
  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
141
  interruptionStatus = RESTART_WORKER_TXN;
153
142
  rc = mdb_txn_commit(*txn);
143
+ #ifdef MDB_EMPTY_TXN
144
+ if (rc == MDB_EMPTY_TXN)
145
+ rc = 0;
146
+ #endif
154
147
  if (rc == 0) {
155
148
  // wait again until the sync transaction is completed
156
- //fprintf(stderr, "Waiting after interruption\n");
157
149
  this->txn = *txn = nullptr;
158
150
  pthread_cond_signal(envForTxn->writingCond);
159
151
  pthread_cond_wait(envForTxn->writingCond, envForTxn->writingLock);
@@ -179,15 +171,16 @@ int WriteWorker::DoWrites(MDB_txn* txn, EnvWrap* envForTxn, uint32_t* instructio
179
171
  double conditionalVersion, setVersion = 0;
180
172
  bool overlappedWord = !!worker;
181
173
  uint32_t* start;
182
- do {
174
+ do {
183
175
  next_inst: start = instruction++;
184
176
  uint32_t flags = *start;
185
177
  MDB_dbi dbi = 0;
178
+ //fprintf(stderr, "do %u %u\n", flags, get_time64());
186
179
  bool validated = conditionDepth == validatedDepth;
187
180
  if (flags & 0xf0c0) {
188
181
  fprintf(stderr, "Unknown flag bits %u %p\n", flags, start);
189
182
  fprintf(stderr, "flags after message %u\n", *start);
190
- worker->ReportError("Unknown flags\n");
183
+ worker->resultCode = 22;
191
184
  return 0;
192
185
  }
193
186
  if (flags & HAS_KEY) {
@@ -228,12 +221,17 @@ next_inst: start = instruction++;
228
221
  instruction += 2;
229
222
  MDB_val conditionalValue;
230
223
  rc = mdb_get(txn, dbi, &key, &conditionalValue);
231
- if (rc)
232
- validated = false;
233
- else if (conditionalVersion != ANY_VERSION) {
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) {
234
232
  double version;
235
233
  memcpy(&version, conditionalValue.mv_data, 8);
236
- validated = validated && conditionalVersion == version;
234
+ validated = validated && ((flags & CONDITIONAL_VERSION_LESS_THAN) ? version <= conditionalVersion : (version == conditionalVersion));
237
235
  }
238
236
  }
239
237
  if (flags & SET_VERSION) {
@@ -242,7 +240,12 @@ next_inst: start = instruction++;
242
240
  }
243
241
  if ((flags & IF_NO_EXISTS) && (flags & START_CONDITION_BLOCK)) {
244
242
  rc = mdb_get(txn, dbi, &key, &value);
245
- validated = validated && rc == MDB_NOTFOUND;
243
+ if (!rc)
244
+ validated = false;
245
+ else if (rc == MDB_NOTFOUND)
246
+ validated = true;
247
+ else
248
+ worker->resultCode = rc;
246
249
  }
247
250
  } else
248
251
  instruction++;
@@ -283,7 +286,7 @@ next_inst: start = instruction++;
283
286
  else
284
287
  rc = mdb_put(txn, dbi, &key, &value, flags & (MDB_NOOVERWRITE | MDB_NODUPDATA | MDB_APPEND | MDB_APPENDDUP));
285
288
  if (flags & COMPRESSIBLE)
286
- free(value.mv_data);
289
+ delete value.mv_data;
287
290
  //fprintf(stdout, "put %u \n", key.mv_size);
288
291
  break;
289
292
  case DEL:
@@ -292,7 +295,7 @@ next_inst: start = instruction++;
292
295
  case DEL_VALUE:
293
296
  rc = mdb_del(txn, dbi, &key, &value);
294
297
  if (flags & COMPRESSIBLE)
295
- free(value.mv_data);
298
+ delete value.mv_data;
296
299
  break;
297
300
  case START_BLOCK: case START_CONDITION_BLOCK:
298
301
  rc = validated ? 0 : MDB_NOTFOUND;
@@ -320,13 +323,13 @@ next_inst: start = instruction++;
320
323
  default:
321
324
  fprintf(stderr, "Unknown flags %u %p\n", flags, start);
322
325
  fprintf(stderr, "flags after message %u\n", *start);
323
- worker->ReportError("Unknown flags\n");
326
+ worker->resultCode = 22;
324
327
  return 22;
325
328
  }
326
329
  if (rc) {
327
330
  if (!(rc == MDB_KEYEXIST || rc == MDB_NOTFOUND)) {
328
331
  if (worker) {
329
- worker->ReportError(mdb_strerror(rc));
332
+ worker->resultCode = rc;
330
333
  } else {
331
334
  return rc;
332
335
  }
@@ -347,83 +350,164 @@ next_inst: start = instruction++;
347
350
  return rc;
348
351
  }
349
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
350
367
  void WriteWorker::Write() {
351
368
  int rc;
352
369
  finishedProgress = true;
353
370
  unsigned int envFlags;
354
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
+ }
355
378
  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
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
370
396
  if (rc != 0) {
371
- return ReportError(mdb_strerror(rc));
397
+ resultCode = rc;
398
+ return;
372
399
  }
373
- hasError = false;
374
400
  rc = DoWrites(txn, envForTxn, instructions, this);
375
-
376
- if (rc || hasError)
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)
377
409
  mdb_txn_abort(txn);
378
410
  else
379
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
380
418
  txn = nullptr;
419
+ interruptionStatus = 0;
420
+ pthread_cond_signal(envForTxn->writingCond); // in case there a sync txn waiting for us
381
421
  pthread_mutex_unlock(envForTxn->writingLock);
382
- if (rc || hasError) {
422
+ if (rc || resultCode) {
383
423
  std::atomic_fetch_or((std::atomic<uint32_t>*) instructions, (uint32_t) TXN_HAD_ERROR);
384
424
  if (rc)
385
- ReportError(mdb_strerror(rc));
425
+ resultCode = rc ? rc : resultCode;
386
426
  return;
387
427
  }
428
+ *(instructions - 1) = txnId;
388
429
  std::atomic_fetch_or((std::atomic<uint32_t>*) instructions, (uint32_t) TXN_COMMITTED);
389
430
  }
390
431
 
391
- void AsyncWriteWorker::OnProgress(const char* data, size_t count) {
392
- if (progressStatus == 1) {
393
- Callback().Call({ Number::New(Env(), progressStatus)});
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);
394
446
  return;
395
447
  }
396
- if (finishedProgress)
448
+ if (worker->finishedProgress)
397
449
  return;
450
+ auto envForTxn = worker->envForTxn;
398
451
  pthread_mutex_lock(envForTxn->writingLock);
399
- while(!txn) // possible to jump in after an interrupted txn here
452
+ while(!worker->txn) // possible to jump in after an interrupted txn here
400
453
  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);
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
+ }
410
466
  }
411
467
 
412
- void AsyncWriteWorker::OnOK() {
413
- finishedProgress = true;
468
+ void writes_complete(napi_env env,
469
+ napi_status status,
470
+ void* data) {
471
+ auto worker = (WriteWorker*) data;
472
+ worker->finishedProgress = true;
414
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
415
- napi_create_int32(Env(), 0, &arg);
416
- napi_call_function(Env(), Env().Undefined(), Callback().Value(), 1, &arg, &result);
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();
417
490
  }
418
491
 
419
492
  Value EnvWrap::startWriting(const Napi::CallbackInfo& info) {
493
+ napi_env n_env = info.Env();
420
494
  if (!this->env) {
421
495
  return throwError(info.Env(), "The environment is already closed.");
422
496
  }
497
+ hasWrites = true;
423
498
  size_t instructionAddress = info[0].As<Number>().Int64Value();
424
- AsyncWriteWorker* worker = new AsyncWriteWorker(this->env, this, (uint32_t*) instructionAddress, info[1].As<Function>());
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);
425
505
  this->writeWorker = worker;
426
- worker->Queue();
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
+
427
511
  return info.Env().Undefined();
428
512
  }
429
513
 
@@ -435,6 +519,7 @@ NAPI_FUNCTION(EnvWrap::write) {
435
519
  napi_throw_error(env, nullptr, "The environment is already closed.");
436
520
  RETURN_UNDEFINED;
437
521
  }
522
+ ew->hasWrites = true;
438
523
 
439
524
  napi_get_value_int64(env, args[1], &i64);
440
525
  uint32_t* instructionAddress = (uint32_t*) i64;
@@ -1,4 +1,8 @@
1
- const SKIP = {};
1
+ export const SKIP = {};
2
+ const DONE = {
3
+ value: null,
4
+ done: true,
5
+ }
2
6
  if (!Symbol.asyncIterator) {
3
7
  Symbol.asyncIterator = Symbol.for('Symbol.asyncIterator');
4
8
  }
@@ -11,9 +15,10 @@ export class RangeIterable {
11
15
  }
12
16
  map(func) {
13
17
  let source = this;
14
- let result = new RangeIterable();
15
- result.iterate = (async) => {
18
+ let iterable = new RangeIterable();
19
+ iterable.iterate = (async) => {
16
20
  let iterator = source[Symbol.iterator](async);
21
+ let i = 0;
17
22
  return {
18
23
  next(resolvedResult) {
19
24
  let result;
@@ -30,31 +35,38 @@ export class RangeIterable {
30
35
  }
31
36
  if (iteratorResult.done === true) {
32
37
  this.done = true;
38
+ if (iterable.onDone) iterable.onDone();
33
39
  return iteratorResult;
34
40
  }
35
- result = func(iteratorResult.value);
41
+ result = func(iteratorResult.value, i++);
36
42
  if (result && result.then) {
37
43
  return result.then(result =>
38
- result == SKIP ?
44
+ result === SKIP ?
39
45
  this.next() :
40
46
  {
41
47
  value: result
42
48
  });
43
49
  }
44
- } while(result == SKIP)
50
+ } while(result === SKIP);
51
+ if (result === DONE) {
52
+ if (iterable.onDone) iterable.onDone();
53
+ return result;
54
+ }
45
55
  return {
46
56
  value: result
47
57
  };
48
58
  },
49
59
  return() {
60
+ if (iterable.onDone) iterable.onDone();
50
61
  return iterator.return();
51
62
  },
52
63
  throw() {
64
+ if (iterable.onDone) iterable.onDone();
53
65
  return iterator.throw();
54
66
  }
55
67
  };
56
68
  };
57
- return result;
69
+ return iterable;
58
70
  }
59
71
  [Symbol.asyncIterator]() {
60
72
  return this.iterator = this.iterate();
@@ -63,7 +75,12 @@ export class RangeIterable {
63
75
  return this.iterator = this.iterate();
64
76
  }
65
77
  filter(func) {
66
- return this.map(element => func(element) ? element : SKIP);
78
+ return this.map(element => {
79
+ let result = func(element);
80
+ // handle promise
81
+ if (result?.then) return result.then((result) => result ? element : SKIP);
82
+ else return result ? element : SKIP;
83
+ });
67
84
  }
68
85
 
69
86
  forEach(callback) {
@@ -78,27 +95,104 @@ export class RangeIterable {
78
95
  concatIterable.iterate = (async) => {
79
96
  let iterator = this.iterator = this.iterate();
80
97
  let isFirst = true;
81
- let concatIterator = {
98
+ function iteratorDone(result) {
99
+ if (isFirst) {
100
+ isFirst = false;
101
+ iterator = secondIterable[Symbol.iterator](async);
102
+ result = iterator.next();
103
+ if (concatIterable.onDone) {
104
+ if (result.then)
105
+ result.then((result) => {
106
+ if (result.done()) concatIterable.onDone();
107
+ });
108
+ else if (result.done) concatIterable.onDone();
109
+ }
110
+ } else {
111
+ if (concatIterable.onDone) concatIterable.onDone();
112
+ }
113
+ return result;
114
+ }
115
+ return {
82
116
  next() {
83
117
  let result = iterator.next();
84
- if (isFirst && result.done) {
85
- isFirst = false;
86
- iterator = secondIterable[Symbol.iterator](async);
87
- return iterator.next();
88
- }
118
+ if (result.then)
119
+ return result.then((result) => {
120
+ if (result.done) return iteratorDone(result);
121
+ return result;
122
+ });
123
+ if (result.done) return iteratorDone(result);
89
124
  return result;
90
125
  },
91
126
  return() {
127
+ if (concatIterable.onDone) concatIterable.onDone();
92
128
  return iterator.return();
93
129
  },
94
130
  throw() {
131
+ if (concatIterable.onDone) concatIterable.onDone();
95
132
  return iterator.throw();
96
133
  }
97
134
  };
98
- return concatIterator;
99
135
  };
100
136
  return concatIterable;
101
137
  }
138
+
139
+ flatMap(callback) {
140
+ let mappedIterable = new RangeIterable();
141
+ mappedIterable.iterate = (async) => {
142
+ let iterator = this.iterator = this.iterate(async);
143
+ let isFirst = true;
144
+ let currentSubIterator;
145
+ return {
146
+ next() {
147
+ do {
148
+ if (currentSubIterator) {
149
+ let result = currentSubIterator.next();
150
+ if (!result.done) {
151
+ return result;
152
+ }
153
+ }
154
+ let result = iterator.next();
155
+ if (result.done) {
156
+ if (mappedIterable.onDone) mappedIterable.onDone();
157
+ return result;
158
+ }
159
+ let value = callback(result.value);
160
+ if (Array.isArray(value) || value instanceof RangeIterable)
161
+ currentSubIterator = value[Symbol.iterator]();
162
+ else {
163
+ currentSubIterator = null;
164
+ return { value };
165
+ }
166
+ } while(true);
167
+ },
168
+ return() {
169
+ if (mappedIterable.onDone) mappedIterable.onDone();
170
+ if (currentSubIterator)
171
+ currentSubIterator.return();
172
+ return iterator.return();
173
+ },
174
+ throw() {
175
+ if (mappedIterable.onDone) mappedIterable.onDone();
176
+ if (currentSubIterator)
177
+ currentSubIterator.throw();
178
+ return iterator.throw();
179
+ }
180
+ };
181
+ };
182
+ return mappedIterable;
183
+ }
184
+
185
+ slice(start, end) {
186
+ return this.map((element, i) => {
187
+ if (i < start)
188
+ return SKIP;
189
+ if (i >= end) {
190
+ DONE.value = element;
191
+ return DONE;
192
+ }
193
+ return element;
194
+ });
195
+ }
102
196
  next() {
103
197
  if (!this.iterator)
104
198
  this.iterator = this.iterate();
@@ -108,7 +202,7 @@ export class RangeIterable {
108
202
  if (this.asArray && this.asArray.forEach) {
109
203
  return this.asArray;
110
204
  }
111
- throw new Error('Can not serialize async iteratables without first calling resolveJSON');
205
+ throw new Error('Can not serialize async iterables without first calling resolveJSON');
112
206
  //return Array.from(this)
113
207
  }
114
208
  get asArray() {
@@ -118,6 +212,7 @@ export class RangeIterable {
118
212
  let iterator = this.iterate();
119
213
  let array = [];
120
214
  let iterable = this;
215
+ Object.defineProperty(array, 'iterable', { value: iterable });
121
216
  function next(result) {
122
217
  while (result.done !== true) {
123
218
  if (result.then) {
@@ -127,7 +222,6 @@ export class RangeIterable {
127
222
  }
128
223
  result = iterator.next();
129
224
  }
130
- array.iterable = iterable;
131
225
  resolve(iterable._asArray = array);
132
226
  }
133
227
  next(iterator.next());
@@ -139,3 +233,4 @@ export class RangeIterable {
139
233
  return this.asArray;
140
234
  }
141
235
  }
236
+ RangeIterable.prototype.DONE = DONE;
@@ -1,10 +1,14 @@
1
- let fs = require('fs');
2
- packageData = JSON.parse(fs.readFileSync('package.json'));
3
- let prebuilds = fs.readdirSync('prebuilds');
4
- let platformDeps = packageData.optionalDependencies = {};
5
- let packageName = packageData.name;
6
- let version = packageData.version;
7
- for (let prebuild of prebuilds) {
8
- platformDeps['@' + packageName + '/' + packageName + '-' + prebuild] = version;
9
- }
10
- fs.writeFileSync('package.json', JSON.stringify(packageData, null, 2));
1
+ let fs = require('fs');
2
+ packageData = JSON.parse(fs.readFileSync('package.json'));
3
+ let prebuilds = fs.readdirSync('prebuilds');
4
+ let platformDeps = packageData.optionalDependencies = {};
5
+ let packageName = packageData.name;
6
+ let version = packageData.version;
7
+ for (let prebuild of prebuilds) {
8
+ platformDeps['@' + packageName + '/' + packageName + '-' + prebuild] = version;
9
+ }
10
+ /*if (version.endsWith('-v1')) {
11
+ packageData.version = version.slice(0, -3);
12
+ packageData.name = packageName + '-v1';
13
+ }*/
14
+ fs.writeFileSync('package.json', JSON.stringify(packageData, null, 2));
package/util/when.js CHANGED
@@ -1,8 +1,8 @@
1
- export function when(promise, callback, errback) {
2
- if (promise && promise.then) {
3
- return errback ?
4
- promise.then(callback, errback) :
5
- promise.then(callback);
6
- }
7
- return callback(promise);
8
- }
1
+ export function when(promise, callback, errback) {
2
+ if (promise && promise.then) {
3
+ return errback ?
4
+ promise.then(callback, errback) :
5
+ promise.then(callback);
6
+ }
7
+ return callback(promise);
8
+ }