From 198b4f85ce6265fb138831d677128efed31bb6d5 Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Thu, 4 Jan 2024 07:23:35 -0700 Subject: [PATCH 01/12] Try debug build --- package.json | 16 ++++++++-------- src/env.cpp | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 62f74c327..442be8aa9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lmdb", "author": "Kris Zyp", - "version": "2.9.3-beta.2", + "version": "2.9.3-beta.2-debug", "description": "Simple, efficient, scalable, high-performance LMDB interface", "license": "MIT", "repository": { @@ -62,13 +62,13 @@ "build-js": "rollup -c", "prepare": "rollup -c", "before-publish": "rollup -c && cpy index.d.ts . --rename=index.d.cts && prebuildify-ci download && node util/set-optional-deps.cjs && npm run test", - "prebuild-libc-musl": "ENABLE_V8_FUNCTIONS=false prebuildify-platform-packages --tag-libc --napi --platform-packages --target 18.17.1", - "prebuild-libc": "prebuildify-platform-packages --tag-libc --target 20.0.0 || true && prebuildify-platform-packages --platform-packages --tag-libc --target 18.17.1 && ENABLE_V8_FUNCTIONS=false prebuildify-platform-packages --napi --platform-packages --tag-libc --target 18.17.1", - "prebuild-macos": "prebuildify-platform-packages --target 20.0.0 && prebuildify-platform-packages --platform-packages --target 18.17.1 && ENABLE_V8_FUNCTIONS=false prebuildify-platform-packages --napi --platform-packages --target 18.17.1", - "prebuild-win32": "prebuildify-platform-packages --target 20.0.0 && prebuildify-platform-packages --target 18.17.1 && set ENABLE_V8_FUNCTIONS=false&& prebuildify-platform-packages --napi --platform-packages --target 18.17.1", - "prebuild-libc-arm7": "ENABLE_V8_FUNCTIONS=false prebuildify-platform-packages --napi --platform-packages --tag-libc --target 18.17.1", - "prebuildify": "prebuildify-platform-packages --napi --target 18.17.1", - "full-publish": "cd prebuilds/win32-x64 && npm publish --access public && cd ../darwin-x64 && npm publish --access public && cd ../darwin-arm64 && npm publish --access public && cd ../linux-x64 && npm publish --access public && cd ../linux-arm64 && npm publish --access public && cd ../linux-arm && npm publish --access public && cd ../.. && npm publish --tag next", + "prebuild-libc-musl": "ENABLE_V8_FUNCTIONS=false prebuildify-platform-packages --debug --tag-libc --napi --platform-packages --target 18.17.1", + "prebuild-libc": "prebuildify-platform-packages --debug --tag-libc --target 20.0.0 || true && prebuildify-platform-packages --debug --platform-packages --tag-libc --target 18.17.1 && ENABLE_V8_FUNCTIONS=false prebuildify-platform-packages --debug --napi --platform-packages --tag-libc --target 18.17.1", + "prebuild-macos": "prebuildify-platform-packages --debug --target 20.0.0 && prebuildify-platform-packages --debug --platform-packages --target 18.17.1 && ENABLE_V8_FUNCTIONS=false prebuildify-platform-packages --debug --napi --platform-packages --target 18.17.1", + "prebuild-win32": "prebuildify-platform-packages --debug --target 20.0.0 && prebuildify-platform-packages --debug --target 18.17.1 && set ENABLE_V8_FUNCTIONS=false&& prebuildify-platform-packages --debug --napi --platform-packages --target 18.17.1", + "prebuild-libc-arm7": "ENABLE_V8_FUNCTIONS=false prebuildify-platform-packages --debug --napi --platform-packages --tag-libc --target 18.17.1", + "prebuildify": "prebuildify-platform-packages --debug --napi --target 18.17.1", + "full-publish": "cd prebuilds/win32-x64 && npm publish --tag next --access public && cd ../darwin-x64 && npm publish --tag next --access public && cd ../darwin-arm64 && npm publish --tag next --access public && cd ../linux-x64 && npm publish --tag next --access public && cd ../linux-arm64 && npm publish --tag next --access public && cd ../linux-arm && npm publish --tag next --access public && cd ../.. && npm publish --tag next", "recompile": "node-gyp clean && node-gyp configure && node-gyp build", "test": "mocha test/**.test.js --expose-gc --recursive", "deno-test": "deno run --allow-ffi --allow-write --allow-read --allow-env --allow-net --unstable test/deno.ts", diff --git a/src/env.cpp b/src/env.cpp index 3d5a09a5b..eb9518bd6 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -697,7 +697,7 @@ void EnvWrap::closeEnv(bool hasLock) { if ((envFlags & MDB_OVERLAPPINGSYNC) && envPath->hasWrites) { mdb_env_sync(env, 1); } - //delete (ExtendedEnv*) mdb_env_get_userctx(env); + delete (ExtendedEnv*) mdb_env_get_userctx(env); #endif char* path; mdb_env_get_path(env, (const char**)&path); From 6ea48c5379bf9ab3682bb2a4a5bf2a54e56feefc Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Sat, 6 Jan 2024 17:47:24 -0700 Subject: [PATCH 02/12] More debugging --- package.json | 2 +- src/txn.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 442be8aa9..cc8bc4001 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lmdb", "author": "Kris Zyp", - "version": "2.9.3-beta.2-debug", + "version": "2.9.3-beta.3-debug", "description": "Simple, efficient, scalable, high-performance LMDB interface", "license": "MIT", "repository": { diff --git a/src/txn.cpp b/src/txn.cpp index f0a45df6d..d3ed927ce 100644 --- a/src/txn.cpp +++ b/src/txn.cpp @@ -62,6 +62,7 @@ TxnWrap::TxnWrap(const Napi::CallbackInfo& info) : ObjectWrap(info) { info.This().As().Set("address", Number::New(info.Env(), 0)); return; } + parentTw = nullptr; parentTxn = nullptr; } int rc = mdb_txn_begin(ew->env, parentTxn, flags, &txn); @@ -105,6 +106,9 @@ void TxnWrap::removeFromEnvWrap() { if (this->ew) { if (this->ew->currentWriteTxn == this) { this->ew->currentWriteTxn = this->parentTw; + if (this->parentTw) { + fprintf(stderr, "ending child transaction\n"); + } } else { auto it = std::find(ew->readTxns.begin(), ew->readTxns.end(), this); From 5fa9777c8f40d7691d904bc0becc977d07f9e4b5 Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Tue, 16 Jan 2024 22:01:05 -0700 Subject: [PATCH 03/12] Fix error return on bad txn --- package.json | 2 +- src/env.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index cc8bc4001..0d09e440c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lmdb", "author": "Kris Zyp", - "version": "2.9.3-beta.3-debug", + "version": "2.9.3-beta.4-debug", "description": "Simple, efficient, scalable, high-performance LMDB interface", "license": "MIT", "repository": { diff --git a/src/env.cpp b/src/env.cpp index eb9518bd6..fb8a0da0d 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -213,8 +213,7 @@ MDB_txn* EnvWrap::getReadTxn(int64_t tw_address) { if (rc) { if (!txn) fprintf(stderr, "No current read transaction available"); - if (rc != EINVAL) - return nullptr; // if there was a real error, signal with nullptr and let error propagate with last_error + return nullptr; // if there was an error, signal with nullptr and let error propagate with last_error } return txn; } From cde01e744e2e1722814dcad631a81fe267305b17 Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Wed, 17 Jan 2024 06:25:50 -0700 Subject: [PATCH 04/12] Add bad transaction warnings --- package.json | 2 +- read.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0d09e440c..eecd88bf3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lmdb", "author": "Kris Zyp", - "version": "2.9.3-beta.4-debug", + "version": "2.9.3-beta.5-debug", "description": "Simple, efficient, scalable, high-performance LMDB interface", "license": "MIT", "repository": { diff --git a/read.js b/read.js index 2df1ecc72..ba762e69a 100644 --- a/read.js +++ b/read.js @@ -94,6 +94,9 @@ export function addReadMethods(LMDBStore, { let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this)); txn.refCount = (txn.refCount || 0) + 1; outstandingReads++; + if (!txn.address) { + throw new Error('Invalid transaction, it has no address'); + } let address = recordReadInstruction(txn.address, this.db.dbi, id, this.writeKey, maxKeySize, ( rc, bufferId, offset, size ) => { if (rc && rc !== 1) callback(lmdbError(rc)); @@ -412,6 +415,9 @@ export function addReadMethods(LMDBStore, { if (txn.isDone) throw new Error('Can not iterate on range with transaction that is already' + ' done'); txnAddress = txn.address; + if (!txnAddress) { + throw new Error('Invalid transaction, it has no address'); + } cursor = null; } else { let writeTxn = env.writeTxn; From 039d18837abd82e2b083544af1f780fed480fdca Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Sat, 20 Jan 2024 20:01:41 -0700 Subject: [PATCH 05/12] Track transcation with timer --- read.js | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/read.js b/read.js index ba762e69a..13e3334da 100644 --- a/read.js +++ b/read.js @@ -159,6 +159,7 @@ export function addReadMethods(LMDBStore, { if (!buffer.isGlobal && !env.writeTxn) { let txn = options?.transaction || (readTxnRenewed ? readTxn : renewReadTxn(this)); buffer.txn = txn; + txn.refCount = (txn.refCount || 0) + 1; return data; } else { @@ -405,6 +406,7 @@ export function addReadMethods(LMDBStore, { (options.inclusiveEnd ? 0x8000 : 0) | (options.exclusiveStart ? 0x10000 : 0); let store = this; + let txn_handle; function resetCursor() { try { if (cursor) @@ -433,7 +435,7 @@ export function addReadMethods(LMDBStore, { cursor = new Cursor(db, txnAddress || 0); } cursorAddress = cursor.address; - txn.refCount = (txn.refCount || 0) + 1; // track transaction so we always use the same one + txn_handle = txn.use(2000); if (snapshot === false) { cursorRenewId = renewId; // use shared read transaction txn.renewingRefCount = (txn.renewingRefCount || 0) + 1; // need to know how many are renewing cursors @@ -494,10 +496,9 @@ export function addReadMethods(LMDBStore, { iterable.onDone() if (cursorRenewId) txn.renewingRefCount--; - if (--txn.refCount <= 0 && txn.notCurrent) { + txn_handle.done(); + if (txn.refCount <= 0 && txn.notCurrent) { cursor.close(); - txn.abort(); // this is no longer main read txn, abort it now that we are done - txn.isDone = true; } else { if (db.availableCursor || txn != readTxn) { cursor.close(); @@ -831,8 +832,39 @@ Txn.prototype.done = function() { } else if (this.refCount < 0) throw new Error('Can not finish a transaction more times than it was used'); } -Txn.prototype.use = function() { +Txn.prototype.use = function(timeout) { this.refCount = (this.refCount || 0) + 1; + if (timeout) { + let timed_out; + let handle; + (async () => { + let timer + await new Promise(async (resolve, reject) => { + timer = setTimeout(() => { + timed_out = true; + resolve(); + }, timeout).unref(); + handle = { + done: () => { + // clear our timeout timer + clearTimeout(timer); + this.done(); + resolve(); + } + } + }); + if (timed_out) { + try { + throw new Error('Transaction took too long'); + } catch(error) { + // this should print with a proper stack trace + console.error(error); + } + } + })(); + return handle; + } + return this; } From 1afdf49fc9d051a6426c001ccb87ea75959f0e56 Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Sat, 20 Jan 2024 22:07:51 -0700 Subject: [PATCH 06/12] Just use use and done and done't do timeouts --- read.js | 37 +++---------------------------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/read.js b/read.js index 13e3334da..d20a76f57 100644 --- a/read.js +++ b/read.js @@ -435,7 +435,7 @@ export function addReadMethods(LMDBStore, { cursor = new Cursor(db, txnAddress || 0); } cursorAddress = cursor.address; - txn_handle = txn.use(2000); + if (txn.use) txn.use(); // track transaction so we always use the same one if (snapshot === false) { cursorRenewId = renewId; // use shared read transaction txn.renewingRefCount = (txn.renewingRefCount || 0) + 1; // need to know how many are renewing cursors @@ -496,7 +496,7 @@ export function addReadMethods(LMDBStore, { iterable.onDone() if (cursorRenewId) txn.renewingRefCount--; - txn_handle.done(); + txn.done?.(); if (txn.refCount <= 0 && txn.notCurrent) { cursor.close(); } else { @@ -832,39 +832,8 @@ Txn.prototype.done = function() { } else if (this.refCount < 0) throw new Error('Can not finish a transaction more times than it was used'); } -Txn.prototype.use = function(timeout) { +Txn.prototype.use = function() { this.refCount = (this.refCount || 0) + 1; - if (timeout) { - let timed_out; - let handle; - (async () => { - let timer - await new Promise(async (resolve, reject) => { - timer = setTimeout(() => { - timed_out = true; - resolve(); - }, timeout).unref(); - handle = { - done: () => { - // clear our timeout timer - clearTimeout(timer); - this.done(); - resolve(); - } - } - }); - if (timed_out) { - try { - throw new Error('Transaction took too long'); - } catch(error) { - // this should print with a proper stack trace - console.error(error); - } - } - })(); - return handle; - } - return this; } From f4acb0e1226b63d789a129b7ae088e8fb5165473 Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Tue, 23 Jan 2024 09:21:08 -0700 Subject: [PATCH 07/12] Add support for asynchronous iteration with flatMap --- package.json | 2 +- util/RangeIterable.js | 31 ++++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index eecd88bf3..f9e08e805 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lmdb", "author": "Kris Zyp", - "version": "2.9.3-beta.5-debug", + "version": "2.10.0-beta.1", "description": "Simple, efficient, scalable, high-performance LMDB interface", "license": "MIT", "repository": { diff --git a/util/RangeIterable.js b/util/RangeIterable.js index ce2bf6fd4..ab1c121d2 100644 --- a/util/RangeIterable.js +++ b/util/RangeIterable.js @@ -173,21 +173,46 @@ export class RangeIterable { let isFirst = true; let currentSubIterator; return { - next() { + next(resolvedResult) { try { do { if (currentSubIterator) { - let result = currentSubIterator.next(); + let result; + if (resolvedResult) { + result = resolvedResult; + resolvedResult = undefined; + } else result = currentSubIterator.next(); + if (result.then) { + if (!async) throw new Error('Can not synchronously iterate with asynchronous values'); + return result.then((result) => this.next(result)); + } if (!result.done) { return result; } } - let result = iterator.next(); + let result = resolvedResult ?? iterator.next(); + if (result.then) { + if (!async) throw new Error('Can not synchronously iterate with asynchronous values'); + currentSubIterator = undefined; + return result.then((result) => this.next(result)); + } if (result.done) { if (mappedIterable.onDone) mappedIterable.onDone(); return result; } let value = callback(result.value); + if (value?.then) { + if (!async) throw new Error('Can not synchronously iterate with asynchronous values'); + return value.then((value) => { + if (Array.isArray(value) || value instanceof RangeIterable) { + currentSubIterator = value[Symbol.iterator](); + return this.next(); + } else { + currentSubIterator = null; + return { value }; + } + }) + } if (Array.isArray(value) || value instanceof RangeIterable) currentSubIterator = value[Symbol.iterator](); else { From c9cff935f66c2b8b610ecaa51b848bfbc2a4b43b Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Tue, 23 Jan 2024 09:45:09 -0700 Subject: [PATCH 08/12] Run mac and windows tests --- .github/workflows/prebuild.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prebuild.yml b/.github/workflows/prebuild.yml index 2ef25d04d..64d510ae3 100644 --- a/.github/workflows/prebuild.yml +++ b/.github/workflows/prebuild.yml @@ -2,7 +2,7 @@ name: Test and Prebuild on: [push] jobs: build-test-macos: - if: startsWith(github.ref, 'refs/tags/') + #if: startsWith(github.ref, 'refs/tags/') env: LMDB_DATA_V1: ${{ contains(github.ref, '-v1') }} runs-on: macos-11 @@ -35,7 +35,7 @@ jobs: with: files: prebuild-darwin.tar build-test-win32: - if: startsWith(github.ref, 'refs/tags/') + #if: startsWith(github.ref, 'refs/tags/') env: LMDB_DATA_V1: ${{ contains(github.ref, '-v1') }} runs-on: windows-latest From e3753ea36e3f740007c71021e17c70fabb706779 Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Tue, 23 Jan 2024 10:38:59 -0700 Subject: [PATCH 09/12] Handle refCount for non-read transactions --- read.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/read.js b/read.js index d20a76f57..19a84bca1 100644 --- a/read.js +++ b/read.js @@ -406,7 +406,6 @@ export function addReadMethods(LMDBStore, { (options.inclusiveEnd ? 0x8000 : 0) | (options.exclusiveStart ? 0x10000 : 0); let store = this; - let txn_handle; function resetCursor() { try { if (cursor) @@ -436,6 +435,7 @@ export function addReadMethods(LMDBStore, { } cursorAddress = cursor.address; if (txn.use) txn.use(); // track transaction so we always use the same one + else txn.refCount = (txn.refCount || 0) + 1; if (snapshot === false) { cursorRenewId = renewId; // use shared read transaction txn.renewingRefCount = (txn.renewingRefCount || 0) + 1; // need to know how many are renewing cursors @@ -496,7 +496,11 @@ export function addReadMethods(LMDBStore, { iterable.onDone() if (cursorRenewId) txn.renewingRefCount--; - txn.done?.(); + if (txn.done) txn.done(); + else if (--txn.refCount <= 0 && txn.notCurrent) { + txn.abort(); + txn.isDone = true; + } if (txn.refCount <= 0 && txn.notCurrent) { cursor.close(); } else { From 7e7d58415fe648deb9809a4d1d5d244e6ff49855 Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Tue, 30 Jan 2024 15:17:19 -0700 Subject: [PATCH 10/12] Ensure that iterables go through return step when map function returns done indicator --- util/RangeIterable.js | 57 +++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/util/RangeIterable.js b/util/RangeIterable.js index ab1c121d2..ea00aa8c7 100644 --- a/util/RangeIterable.js +++ b/util/RangeIterable.js @@ -34,7 +34,10 @@ export class RangeIterable { if (!async) { throw new Error('Can not synchronously iterate with asynchronous values'); } - return iteratorResult.then(iteratorResult => this.next(iteratorResult), onError); + return iteratorResult.then(iteratorResult => this.next(iteratorResult), (error) => { + this.return(); + throw error; + }); } } if (iteratorResult.done === true) { @@ -52,34 +55,39 @@ export class RangeIterable { this.next() : { value: result - }, onError); + }, (error) => { + this.return(); + throw error; + }); } } while(result === SKIP); if (result === DONE) { - if (iterable.onDone) iterable.onDone(); - return result; + return this.return(); } return { value: result }; } catch(error) { - onError(error); + this.return(); + throw error; } }, return() { - if (iterable.onDone) iterable.onDone(); - return iterator.return(); + if (!this.done) { + this.done = true; + if (iterable.onDone) iterable.onDone(); + return iterator.return(); + } }, throw() { - if (iterable.onDone) iterable.onDone(); - return iterator.throw(); + if (!this.done) { + this.done = true; + if (iterable.onDone) iterable.onDone(); + return iterator.throw(); + } } }; }; - function onError(error) { - if (iterable.onDone) iterable.onDone(); - throw error; - } return iterable; } [Symbol.asyncIterator]() { @@ -120,11 +128,15 @@ export class RangeIterable { if (!async) throw new Error('Can not synchronously iterate with asynchronous values'); result.then((result) => { if (result.done()) concatIterable.onDone(); - }, onError); + }, (error) => { + this.return(); + throw error; + }); } else if (result.done) concatIterable.onDone(); } } catch (error) { - onError(error); + this.return(); + throw error; } } else { if (concatIterable.onDone) concatIterable.onDone(); @@ -145,7 +157,8 @@ export class RangeIterable { if (result.done) return iteratorDone(result); return result; } catch (error) { - onError(error); + this.return(); + throw error; } }, return() { @@ -158,11 +171,6 @@ export class RangeIterable { } }; }; - function onError(error) { - if (iterable.onDone) iterable.onDone(); - throw error; - } - return concatIterable; } @@ -221,7 +229,8 @@ export class RangeIterable { } } while(true); } catch (error) { - onError(error); + this.return(); + throw error; } }, return() { @@ -238,10 +247,6 @@ export class RangeIterable { } }; }; - function onError(error) { - if (iterable.onDone) iterable.onDone(); - throw error; - } return mappedIterable; } From 4fab6c673caa6da84240803be4d9a38ba36e4869 Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Wed, 31 Jan 2024 13:29:35 -0700 Subject: [PATCH 11/12] Provide access to whether we are sync iterating or not --- util/RangeIterable.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/util/RangeIterable.js b/util/RangeIterable.js index ea00aa8c7..074cbee41 100644 --- a/util/RangeIterable.js +++ b/util/RangeIterable.js @@ -18,6 +18,7 @@ export class RangeIterable { let iterable = new RangeIterable(); iterable.iterate = (async) => { let iterator = source[async ? Symbol.asyncIterator : Symbol.iterator](); + if (!async) source.isSync = true; let i = 0; return { next(resolvedResult) { @@ -45,11 +46,9 @@ export class RangeIterable { if (iterable.onDone) iterable.onDone(); return iteratorResult; } - result = func(iteratorResult.value, i++); - if (result && result.then) { - if (!async) { - throw new Error('Can not synchronously iterate with asynchronous values'); - } + result = func.call(source, iteratorResult.value, i++); + if (result && result.then && async) { + // if async, wait for promise to resolve before returning iterator result return result.then(result => result === SKIP ? this.next() : From 2d3a87e85b184162fa59907d46aa9f14fcda8753 Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Wed, 31 Jan 2024 14:18:05 -0700 Subject: [PATCH 12/12] Improve use of return/throw methods --- src/txn.cpp | 3 --- util/RangeIterable.js | 49 ++++++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/txn.cpp b/src/txn.cpp index d3ed927ce..c0bb3c308 100644 --- a/src/txn.cpp +++ b/src/txn.cpp @@ -106,9 +106,6 @@ void TxnWrap::removeFromEnvWrap() { if (this->ew) { if (this->ew->currentWriteTxn == this) { this->ew->currentWriteTxn = this->parentTw; - if (this->parentTw) { - fprintf(stderr, "ending child transaction\n"); - } } else { auto it = std::find(ew->readTxns.begin(), ew->readTxns.end(), this); diff --git a/util/RangeIterable.js b/util/RangeIterable.js index 074cbee41..c20cd017d 100644 --- a/util/RangeIterable.js +++ b/util/RangeIterable.js @@ -3,6 +3,10 @@ const DONE = { value: null, done: true, } +const RETURN_DONE = { // we allow this one to be mutated + value: null, + done: true, +}; if (!Symbol.asyncIterator) { Symbol.asyncIterator = Symbol.for('Symbol.asyncIterator'); } @@ -33,11 +37,10 @@ export class RangeIterable { iteratorResult = iterator.next(); if (iteratorResult.then) { if (!async) { - throw new Error('Can not synchronously iterate with asynchronous values'); + this.throw(new Error('Can not synchronously iterate with asynchronous values')); } return iteratorResult.then(iteratorResult => this.next(iteratorResult), (error) => { - this.return(); - throw error; + this.throw(error); }); } } @@ -55,8 +58,7 @@ export class RangeIterable { { value: result }, (error) => { - this.return(); - throw error; + this.throw(error); }); } } while(result === SKIP); @@ -67,23 +69,21 @@ export class RangeIterable { value: result }; } catch(error) { - this.return(); - throw error; + this.throw(error); } }, - return() { + return(value) { if (!this.done) { + RETURN_DONE.value = value; this.done = true; if (iterable.onDone) iterable.onDone(); - return iterator.return(); + iterator.return(); } + return RETURN_DONE; }, - throw() { - if (!this.done) { - this.done = true; - if (iterable.onDone) iterable.onDone(); - return iterator.throw(); - } + throw(error) { + this.return(); + throw error; } }; }; @@ -134,8 +134,7 @@ export class RangeIterable { } else if (result.done) concatIterable.onDone(); } } catch (error) { - this.return(); - throw error; + this.throw(error); } } else { if (concatIterable.onDone) concatIterable.onDone(); @@ -161,12 +160,18 @@ export class RangeIterable { } }, return() { - if (concatIterable.onDone) concatIterable.onDone(); - return iterator.return(); + if (!this.done) { + RETURN_DONE.value = value; + this.done = true; + if (concatIterable.onDone) concatIterable.onDone(); + iterator.return(); + } + return RETURN_DONE; + }, - throw() { - if (concatIterable.onDone) concatIterable.onDone(); - return iterator.throw(); + throw(error) { + this.return(); + throw error; } }; };