From 92442d4b518dd07d620d806f1cc885ab3321ed9c Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Thu, 16 Dec 2021 12:43:17 -0600 Subject: [PATCH 1/8] async_hooks: fix AsyncLocalStorage in unhandledRejection cases --- lib/internal/process/promises.js | 65 +++++++++++-------- .../test-async-local-storage-errors.js | 31 ++++++--- 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index a6c65b0be4d84d..64aab7d4064ede 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -27,6 +27,10 @@ const { const { pushAsyncContext, popAsyncContext, + symbols: { + async_id_symbol: kAsyncIdSymbol, + trigger_async_id_symbol: kTriggerAsyncIdSymbol + } } = require('internal/async_hooks'); const async_hooks = require('async_hooks'); const { isErrorStackTraceLimitWritable } = require('internal/errors'); @@ -220,41 +224,46 @@ function processPromiseRejections() { promiseInfo.warned = true; const { reason, uid, emit } = promiseInfo; - switch (unhandledRejectionsMode) { - case kStrictUnhandledRejections: { - const err = reason instanceof Error ? - reason : generateUnhandledRejectionError(reason); - triggerUncaughtException(err, true /* fromPromise */); - const handled = emit(reason, promise, promiseInfo); - if (!handled) emitUnhandledRejectionWarning(uid, reason); - break; - } - case kIgnoreUnhandledRejections: { - emit(reason, promise, promiseInfo); - break; - } - case kAlwaysWarnUnhandledRejections: { - emit(reason, promise, promiseInfo); - emitUnhandledRejectionWarning(uid, reason); - break; - } - case kThrowUnhandledRejections: { - const handled = emit(reason, promise, promiseInfo); - if (!handled) { + try { + pushAsyncContext(promise[kAsyncIdSymbol], promise[kTriggerAsyncIdSymbol], promise); + switch (unhandledRejectionsMode) { + case kStrictUnhandledRejections: { const err = reason instanceof Error ? reason : generateUnhandledRejectionError(reason); triggerUncaughtException(err, true /* fromPromise */); + const handled = emit(reason, promise, promiseInfo); + if (!handled) emitUnhandledRejectionWarning(uid, reason); + break; } - break; - } - case kWarnWithErrorCodeUnhandledRejections: { - const handled = emit(reason, promise, promiseInfo); - if (!handled) { + case kIgnoreUnhandledRejections: { + emit(reason, promise, promiseInfo); + break; + } + case kAlwaysWarnUnhandledRejections: { + emit(reason, promise, promiseInfo); emitUnhandledRejectionWarning(uid, reason); - process.exitCode = 1; + break; + } + case kThrowUnhandledRejections: { + const handled = emit(reason, promise, promiseInfo); + if (!handled) { + const err = reason instanceof Error ? + reason : generateUnhandledRejectionError(reason); + triggerUncaughtException(err, true /* fromPromise */); + } + break; + } + case kWarnWithErrorCodeUnhandledRejections: { + const handled = emit(reason, promise, promiseInfo); + if (!handled) { + emitUnhandledRejectionWarning(uid, reason); + process.exitCode = 1; + } + break; } - break; } + } finally { + popAsyncContext(promise[kAsyncIdSymbol]); } maybeScheduledTicksOrMicrotasks = true; } diff --git a/test/async-hooks/test-async-local-storage-errors.js b/test/async-hooks/test-async-local-storage-errors.js index 0dd5754e02cbd9..1b7c44966b5262 100644 --- a/test/async-hooks/test-async-local-storage-errors.js +++ b/test/async-hooks/test-async-local-storage-errors.js @@ -1,22 +1,35 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const { AsyncLocalStorage } = require('async_hooks'); -// case 2 using *AndReturn calls (dual behaviors) const asyncLocalStorage = new AsyncLocalStorage(); +let callbackToken = {}; +let awaitToken = {}; let i = 0; -process.setUncaughtExceptionCaptureCallback((err) => { - ++i; +const exceptionHandler = common.mustCall( + (err) => { + ++i; assert.strictEqual(err.message, 'err2'); - assert.strictEqual(asyncLocalStorage.getStore().get('hello'), 'node'); -}); + assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); + }, 1); +process.setUncaughtExceptionCaptureCallback(exceptionHandler); + +const rejectionHandler = common.mustCall((err) => { + assert.strictEqual(err.message, 'err3'); + assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); +}, 1); +process.on('unhandledRejection', rejectionHandler); + +async function awaitTest() { + await null; + throw new Error('err3'); +} +asyncLocalStorage.run(awaitToken, awaitTest); try { - asyncLocalStorage.run(new Map(), () => { - const store = asyncLocalStorage.getStore(); - store.set('hello', 'node'); + asyncLocalStorage.run(callbackToken, () => { setTimeout(() => { process.nextTick(() => { assert.strictEqual(i, 1); From 6e250d80293a996f9d9b2c92e1de5254fdfa72f5 Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Thu, 16 Dec 2021 13:45:23 -0600 Subject: [PATCH 2/8] fixup: linter --- lib/internal/process/promises.js | 6 +++++- test/async-hooks/test-async-local-storage-errors.js | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index 64aab7d4064ede..321063efcbd132 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -225,7 +225,11 @@ function processPromiseRejections() { const { reason, uid, emit } = promiseInfo; try { - pushAsyncContext(promise[kAsyncIdSymbol], promise[kTriggerAsyncIdSymbol], promise); + pushAsyncContext( + promise[kAsyncIdSymbol], + promise[kTriggerAsyncIdSymbol], + promise + ); switch (unhandledRejectionsMode) { case kStrictUnhandledRejections: { const err = reason instanceof Error ? diff --git a/test/async-hooks/test-async-local-storage-errors.js b/test/async-hooks/test-async-local-storage-errors.js index 1b7c44966b5262..4a7c74bf96720f 100644 --- a/test/async-hooks/test-async-local-storage-errors.js +++ b/test/async-hooks/test-async-local-storage-errors.js @@ -4,15 +4,15 @@ const assert = require('assert'); const { AsyncLocalStorage } = require('async_hooks'); const asyncLocalStorage = new AsyncLocalStorage(); -let callbackToken = {}; -let awaitToken = {}; +const callbackToken = {}; +const awaitToken = {}; let i = 0; const exceptionHandler = common.mustCall( (err) => { ++i; - assert.strictEqual(err.message, 'err2'); - assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); + assert.strictEqual(err.message, 'err2'); + assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); }, 1); process.setUncaughtExceptionCaptureCallback(exceptionHandler); From ecbd8aa6a219edc687c1a542c368f2945fd34c9c Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Fri, 17 Dec 2021 09:42:28 -0600 Subject: [PATCH 3/8] fixup: more complete test, blocked --- test/async-hooks/test-async-local-storage-errors.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/async-hooks/test-async-local-storage-errors.js b/test/async-hooks/test-async-local-storage-errors.js index 4a7c74bf96720f..fb8da6db1cc415 100644 --- a/test/async-hooks/test-async-local-storage-errors.js +++ b/test/async-hooks/test-async-local-storage-errors.js @@ -22,6 +22,18 @@ const rejectionHandler = common.mustCall((err) => { }, 1); process.on('unhandledRejection', rejectionHandler); +const exceptionMonitor = common.mustCall((err, origin) => { + if (err.message === 'err2') { + assert.strictEqual(origin, 'uncaughtException'); + assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); + } + if (err.message === 'err3') { + assert.strictEqual(origin, 'unhandledRejection'); + assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); + } +}, 2); +process.on('uncaughtExceptionMonitor', exceptionMonitor); + async function awaitTest() { await null; throw new Error('err3'); From 7afcf6c2f7ddc5562c9e9f61cf6b7c575366b8cc Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Fri, 17 Dec 2021 13:55:35 -0600 Subject: [PATCH 4/8] fixup: more robust test --- lib/internal/process/promises.js | 90 +++++++++---------- .../test-async-local-storage-errors.js | 25 ++++-- 2 files changed, 59 insertions(+), 56 deletions(-) diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index 321063efcbd132..490c07c8c13d59 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -32,7 +32,6 @@ const { trigger_async_id_symbol: kTriggerAsyncIdSymbol } } = require('internal/async_hooks'); -const async_hooks = require('async_hooks'); const { isErrorStackTraceLimitWritable } = require('internal/errors'); // *Must* match Environment::TickInfo::Fields in src/env.h. @@ -127,20 +126,11 @@ function resolveError(type, promise, reason) { } function unhandledRejection(promise, reason) { - const asyncId = async_hooks.executionAsyncId(); - const triggerAsyncId = async_hooks.triggerAsyncId(); - const resource = promise; - const emit = (reason, promise, promiseInfo) => { - try { - pushAsyncContext(asyncId, triggerAsyncId, resource); - if (promiseInfo.domain) { - return promiseInfo.domain.emit('error', reason); - } - return process.emit('unhandledRejection', reason, promise); - } finally { - popAsyncContext(asyncId); + if (promiseInfo.domain) { + return promiseInfo.domain.emit('error', reason); } + return process.emit('unhandledRejection', reason, promise); }; maybeUnhandledPromises.set(promise, { @@ -224,50 +214,50 @@ function processPromiseRejections() { promiseInfo.warned = true; const { reason, uid, emit } = promiseInfo; - try { - pushAsyncContext( - promise[kAsyncIdSymbol], - promise[kTriggerAsyncIdSymbol], - promise - ); - switch (unhandledRejectionsMode) { - case kStrictUnhandledRejections: { + pushAsyncContext( + promise[kAsyncIdSymbol], + promise[kTriggerAsyncIdSymbol], + promise + ); + switch (unhandledRejectionsMode) { + case kStrictUnhandledRejections: { + const err = reason instanceof Error ? + reason : generateUnhandledRejectionError(reason); + // This destroys the async stack, don't clear it after + triggerUncaughtException(err, true /* fromPromise */); + const handled = emit(reason, promise, promiseInfo); + if (!handled) emitUnhandledRejectionWarning(uid, reason); + break; + } + case kIgnoreUnhandledRejections: { + emit(reason, promise, promiseInfo); + break; + } + case kAlwaysWarnUnhandledRejections: { + emit(reason, promise, promiseInfo); + emitUnhandledRejectionWarning(uid, reason); + popAsyncContext(promise[kAsyncIdSymbol]); + break; + } + case kThrowUnhandledRejections: { + const handled = emit(reason, promise, promiseInfo); + if (!handled) { const err = reason instanceof Error ? reason : generateUnhandledRejectionError(reason); + // This destroys the async stack, don't clear it after triggerUncaughtException(err, true /* fromPromise */); - const handled = emit(reason, promise, promiseInfo); - if (!handled) emitUnhandledRejectionWarning(uid, reason); - break; } - case kIgnoreUnhandledRejections: { - emit(reason, promise, promiseInfo); - break; - } - case kAlwaysWarnUnhandledRejections: { - emit(reason, promise, promiseInfo); + break; + } + case kWarnWithErrorCodeUnhandledRejections: { + const handled = emit(reason, promise, promiseInfo); + if (!handled) { emitUnhandledRejectionWarning(uid, reason); - break; - } - case kThrowUnhandledRejections: { - const handled = emit(reason, promise, promiseInfo); - if (!handled) { - const err = reason instanceof Error ? - reason : generateUnhandledRejectionError(reason); - triggerUncaughtException(err, true /* fromPromise */); - } - break; - } - case kWarnWithErrorCodeUnhandledRejections: { - const handled = emit(reason, promise, promiseInfo); - if (!handled) { - emitUnhandledRejectionWarning(uid, reason); - process.exitCode = 1; - } - break; + popAsyncContext(promise[kAsyncIdSymbol]); + process.exitCode = 1; } + break; } - } finally { - popAsyncContext(promise[kAsyncIdSymbol]); } maybeScheduledTicksOrMicrotasks = true; } diff --git a/test/async-hooks/test-async-local-storage-errors.js b/test/async-hooks/test-async-local-storage-errors.js index fb8da6db1cc415..b98f1df264ab7b 100644 --- a/test/async-hooks/test-async-local-storage-errors.js +++ b/test/async-hooks/test-async-local-storage-errors.js @@ -8,17 +8,30 @@ const callbackToken = {}; const awaitToken = {}; let i = 0; -const exceptionHandler = common.mustCall( - (err) => { - ++i; - assert.strictEqual(err.message, 'err2'); - assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); - }, 1); +let underlyingExceptionHandler = common.mustCall(function(err) { + ++i; + assert.strictEqual(err.message, 'err2'); + assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); + + // re-entrant check + Promise.reject(new Error('err4')); + underlyingExceptionHandler = common.mustCall( + function(err) { + assert.strictEqual(err.message, 'err4'); + assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); + }, 1); +}, 1); + +const exceptionHandler = common.mustCall(function(...args) { + return underlyingExceptionHandler.call(this, ...args); +}, 2); + process.setUncaughtExceptionCaptureCallback(exceptionHandler); const rejectionHandler = common.mustCall((err) => { assert.strictEqual(err.message, 'err3'); assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); + process.off('unhandledRejection', rejectionHandler); }, 1); process.on('unhandledRejection', rejectionHandler); From 4935ad70b03cd6a176e99b5f172cfff4d0605fce Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Mon, 20 Dec 2021 12:31:22 -0600 Subject: [PATCH 5/8] fixup: make test work and handle when hooks are disabled --- lib/internal/async_hooks.js | 9 +- lib/internal/process/promises.js | 98 +++++++++----- .../test-async-local-storage-errors.js | 128 ++++++++++++------ .../test-async-wrap-pop-id-during-load.js | 5 +- 4 files changed, 160 insertions(+), 80 deletions(-) diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 17cdabbd281ad1..400fd6f48f8c4a 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -443,7 +443,14 @@ function clearDefaultTriggerAsyncId() { async_id_fields[kDefaultTriggerAsyncId] = -1; } - +/** + * @template {Array} T + * @template {unknown} R + * @param {number} triggerAsyncId + * @param { (...T: args) => R } block + * @param {T} args + * @returns + */ function defaultTriggerAsyncIdScope(triggerAsyncId, block, ...args) { if (triggerAsyncId === undefined) return block.apply(null, args); diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index 490c07c8c13d59..76099b1032a466 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -214,49 +214,73 @@ function processPromiseRejections() { promiseInfo.warned = true; const { reason, uid, emit } = promiseInfo; - pushAsyncContext( - promise[kAsyncIdSymbol], - promise[kTriggerAsyncIdSymbol], - promise - ); - switch (unhandledRejectionsMode) { - case kStrictUnhandledRejections: { - const err = reason instanceof Error ? - reason : generateUnhandledRejectionError(reason); - // This destroys the async stack, don't clear it after - triggerUncaughtException(err, true /* fromPromise */); - const handled = emit(reason, promise, promiseInfo); - if (!handled) emitUnhandledRejectionWarning(uid, reason); - break; - } - case kIgnoreUnhandledRejections: { - emit(reason, promise, promiseInfo); - break; - } - case kAlwaysWarnUnhandledRejections: { - emit(reason, promise, promiseInfo); - emitUnhandledRejectionWarning(uid, reason); - popAsyncContext(promise[kAsyncIdSymbol]); - break; - } - case kThrowUnhandledRejections: { - const handled = emit(reason, promise, promiseInfo); - if (!handled) { + let needPop = true; + const { + [kAsyncIdSymbol]: promiseAsyncId, + [kTriggerAsyncIdSymbol]: promiseTriggerAsyncId, + } = promise; + // We need to check if async_hooks are enabled + // don't use enabledHooksExist as a Promise could + // come from a vm.* context and not have an async id + if (typeof promiseAsyncId !== 'undefined') { + pushAsyncContext( + promiseAsyncId, + promiseTriggerAsyncId, + promise + ); + } + try { + switch (unhandledRejectionsMode) { + case kStrictUnhandledRejections: { const err = reason instanceof Error ? reason : generateUnhandledRejectionError(reason); - // This destroys the async stack, don't clear it after + // This destroys the async stack, don't clear it after triggerUncaughtException(err, true /* fromPromise */); + if (typeof promiseAsyncId !== 'undefined') { + pushAsyncContext( + promise[kAsyncIdSymbol], + promise[kTriggerAsyncIdSymbol], + promise + ); + } + const handled = emit(reason, promise, promiseInfo); + if (!handled) emitUnhandledRejectionWarning(uid, reason); + break; } - break; - } - case kWarnWithErrorCodeUnhandledRejections: { - const handled = emit(reason, promise, promiseInfo); - if (!handled) { + case kIgnoreUnhandledRejections: { + emit(reason, promise, promiseInfo); + break; + } + case kAlwaysWarnUnhandledRejections: { + emit(reason, promise, promiseInfo); emitUnhandledRejectionWarning(uid, reason); - popAsyncContext(promise[kAsyncIdSymbol]); - process.exitCode = 1; + break; + } + case kThrowUnhandledRejections: { + const handled = emit(reason, promise, promiseInfo); + if (!handled) { + const err = reason instanceof Error ? + reason : generateUnhandledRejectionError(reason); + // This destroys the async stack, don't clear it after + triggerUncaughtException(err, true /* fromPromise */); + needPop = false; + } + break; + } + case kWarnWithErrorCodeUnhandledRejections: { + const handled = emit(reason, promise, promiseInfo); + if (!handled) { + emitUnhandledRejectionWarning(uid, reason); + process.exitCode = 1; + } + break; + } + } + } finally { + if (needPop) { + if (typeof promiseAsyncId !== 'undefined') { + popAsyncContext(promiseAsyncId); } - break; } } maybeScheduledTicksOrMicrotasks = true; diff --git a/test/async-hooks/test-async-local-storage-errors.js b/test/async-hooks/test-async-local-storage-errors.js index b98f1df264ab7b..8fb945b0784d9b 100644 --- a/test/async-hooks/test-async-local-storage-errors.js +++ b/test/async-hooks/test-async-local-storage-errors.js @@ -2,68 +2,114 @@ const common = require('../common'); const assert = require('assert'); const { AsyncLocalStorage } = require('async_hooks'); +const vm = require('vm'); + +// err1 is emitted sync as a control - no events +// err2 is emitted after a timeout - uncaughtExceptionMonitor + uncaughtException +// err3 is emitted after some awaits - unhandledRejection +// err4 is emitted during handling err3 - uncaughtExceptionMonitor +// err5 is emitted after err4 from a VM lacking hooks - unhandledRejection + uncaughtException const asyncLocalStorage = new AsyncLocalStorage(); -const callbackToken = {}; -const awaitToken = {}; +const callbackToken = {callbackToken:true}; +const awaitToken = {awaitToken:true}; let i = 0; -let underlyingExceptionHandler = common.mustCall(function(err) { - ++i; - assert.strictEqual(err.message, 'err2'); - assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); - - // re-entrant check - Promise.reject(new Error('err4')); - underlyingExceptionHandler = common.mustCall( - function(err) { - assert.strictEqual(err.message, 'err4'); - assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); - }, 1); -}, 1); +// redefining the uncaughtExceptionHandler is a bit odd, so we just do this so we can track total invocations +let underlyingExceptionHandler; const exceptionHandler = common.mustCall(function(...args) { return underlyingExceptionHandler.call(this, ...args); }, 2); - process.setUncaughtExceptionCaptureCallback(exceptionHandler); -const rejectionHandler = common.mustCall((err) => { - assert.strictEqual(err.message, 'err3'); - assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); - process.off('unhandledRejection', rejectionHandler); -}, 1); -process.on('unhandledRejection', rejectionHandler); - const exceptionMonitor = common.mustCall((err, origin) => { if (err.message === 'err2') { assert.strictEqual(origin, 'uncaughtException'); assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); - } - if (err.message === 'err3') { + } else if (err.message === 'err4') { assert.strictEqual(origin, 'unhandledRejection'); assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); + } else { + assert.fail('unknown error ' + err) } }, 2); process.on('uncaughtExceptionMonitor', exceptionMonitor); -async function awaitTest() { - await null; - throw new Error('err3'); +function fireErr1() { + underlyingExceptionHandler = common.mustCall(function(err) { + ++i; + assert.strictEqual(err.message, 'err2'); + assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); + }, 1); + try { + asyncLocalStorage.run(callbackToken, () => { + setTimeout(fireErr2, 0); + throw new Error('err1'); + }); + } catch (e) { + assert.strictEqual(e.message, 'err1'); + assert.strictEqual(asyncLocalStorage.getStore(), undefined); + } } -asyncLocalStorage.run(awaitToken, awaitTest); -try { - asyncLocalStorage.run(callbackToken, () => { - setTimeout(() => { - process.nextTick(() => { - assert.strictEqual(i, 1); - }); - throw new Error('err2'); - }, 0); - throw new Error('err1'); +function fireErr2() { + process.nextTick(() => { + assert.strictEqual(i, 1); + fireErr3(); }); -} catch (e) { - assert.strictEqual(e.message, 'err1'); - assert.strictEqual(asyncLocalStorage.getStore(), undefined); + throw new Error('err2'); } + +function fireErr3() { + assert.strictEqual(asyncLocalStorage.getStore(), callbackToken); + const rejectionHandler3 = common.mustCall((err) => { + assert.strictEqual(err.message, 'err3'); + assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); + process.off('unhandledRejection', rejectionHandler3); + + fireErr4(); + }, 1); + process.on('unhandledRejection', rejectionHandler3); + async function awaitTest() { + await null; + throw new Error('err3'); + } + asyncLocalStorage.run(awaitToken, awaitTest); +} + +let uncaughtExceptionHandler4 = common.mustCall( + function(err) { + assert.strictEqual(err.message, 'err4'); + assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); + fireErr5(); + }, 1); +function fireErr4() { + assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); + underlyingExceptionHandler = uncaughtExceptionHandler4; + // re-entrant check + Promise.reject(new Error('err4')); +} + +function fireErr5() { + assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); + underlyingExceptionHandler = () => {}; + const rejectionHandler5 = common.mustCall((err) => { + assert.strictEqual(err.message, 'err5'); + assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); + process.off('unhandledRejection', rejectionHandler5); + }, 1); + process.on('unhandledRejection', rejectionHandler5); + const makeOrphan = vm.compileFunction(`(${String(() => { + async function main() { + await null; + Promise.resolve().then(() => { + throw new Error("err5") + }) + } + main(); + })})()`); + makeOrphan(); +} + +fireErr1(); \ No newline at end of file diff --git a/test/parallel/test-async-wrap-pop-id-during-load.js b/test/parallel/test-async-wrap-pop-id-during-load.js index bd53c4d7893d49..3a6b72f2d318d2 100644 --- a/test/parallel/test-async-wrap-pop-id-during-load.js +++ b/test/parallel/test-async-wrap-pop-id-during-load.js @@ -18,8 +18,11 @@ const ret = spawnSync( ['--unhandled-rejections=none', '--stack_size=150', __filename, 'async'], { maxBuffer: Infinity } ); +const stdout = ret.stdout.toString('utf8', 0, 2048); assert.strictEqual(ret.status, 0, - `EXIT CODE: ${ret.status}, STDERR:\n${ret.stderr}`); + `EXIT CODE: ${ret.status}, STDERR:\n${ret.stderr}, STDOUT:\n${stdout}`); const stderr = ret.stderr.toString('utf8', 0, 2048); assert.doesNotMatch(stderr, /async.*hook/i); assert.ok(stderr.includes('Maximum call stack size exceeded'), stderr); + +console.error(stdout, stderr); From 6339fba541ec24221ff800f225ec139332d7f734 Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Mon, 20 Dec 2021 15:27:18 -0600 Subject: [PATCH 6/8] fixup: lint --- .../test-async-local-storage-errors.js | 25 +++++++++++-------- .../test-async-wrap-pop-id-during-load.js | 2 -- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/test/async-hooks/test-async-local-storage-errors.js b/test/async-hooks/test-async-local-storage-errors.js index 8fb945b0784d9b..f10a953bc9709d 100644 --- a/test/async-hooks/test-async-local-storage-errors.js +++ b/test/async-hooks/test-async-local-storage-errors.js @@ -5,18 +5,21 @@ const { AsyncLocalStorage } = require('async_hooks'); const vm = require('vm'); // err1 is emitted sync as a control - no events -// err2 is emitted after a timeout - uncaughtExceptionMonitor + uncaughtException +// err2 is emitted after a timeout - uncaughtExceptionMonitor +// + uncaughtException // err3 is emitted after some awaits - unhandledRejection // err4 is emitted during handling err3 - uncaughtExceptionMonitor -// err5 is emitted after err4 from a VM lacking hooks - unhandledRejection + uncaughtException +// err5 is emitted after err4 from a VM lacking hooks - unhandledRejection +// + uncaughtException const asyncLocalStorage = new AsyncLocalStorage(); -const callbackToken = {callbackToken:true}; -const awaitToken = {awaitToken:true}; +const callbackToken = { callbackToken: true }; +const awaitToken = { awaitToken: true }; let i = 0; -// redefining the uncaughtExceptionHandler is a bit odd, so we just do this so we can track total invocations +// Redefining the uncaughtExceptionHandler is a bit odd, so we just do this +// so we can track total invocations let underlyingExceptionHandler; const exceptionHandler = common.mustCall(function(...args) { return underlyingExceptionHandler.call(this, ...args); @@ -31,7 +34,7 @@ const exceptionMonitor = common.mustCall((err, origin) => { assert.strictEqual(origin, 'unhandledRejection'); assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); } else { - assert.fail('unknown error ' + err) + assert.fail('unknown error ' + err); } }, 2); process.on('uncaughtExceptionMonitor', exceptionMonitor); @@ -67,7 +70,7 @@ function fireErr3() { assert.strictEqual(err.message, 'err3'); assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); process.off('unhandledRejection', rejectionHandler3); - + fireErr4(); }, 1); process.on('unhandledRejection', rejectionHandler3); @@ -78,7 +81,7 @@ function fireErr3() { asyncLocalStorage.run(awaitToken, awaitTest); } -let uncaughtExceptionHandler4 = common.mustCall( +const uncaughtExceptionHandler4 = common.mustCall( function(err) { assert.strictEqual(err.message, 'err4'); assert.strictEqual(asyncLocalStorage.getStore(), awaitToken); @@ -104,12 +107,12 @@ function fireErr5() { async function main() { await null; Promise.resolve().then(() => { - throw new Error("err5") - }) + throw new Error('err5'); + }); } main(); })})()`); makeOrphan(); } -fireErr1(); \ No newline at end of file +fireErr1(); diff --git a/test/parallel/test-async-wrap-pop-id-during-load.js b/test/parallel/test-async-wrap-pop-id-during-load.js index 3a6b72f2d318d2..f8f20298b8163b 100644 --- a/test/parallel/test-async-wrap-pop-id-during-load.js +++ b/test/parallel/test-async-wrap-pop-id-during-load.js @@ -24,5 +24,3 @@ assert.strictEqual(ret.status, 0, const stderr = ret.stderr.toString('utf8', 0, 2048); assert.doesNotMatch(stderr, /async.*hook/i); assert.ok(stderr.includes('Maximum call stack size exceeded'), stderr); - -console.error(stdout, stderr); From 2154a4245f416b730b761de4be6a5b1a3d9da6eb Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Mon, 20 Dec 2021 15:28:32 -0600 Subject: [PATCH 7/8] fixup: lint --- test/parallel/test-async-wrap-pop-id-during-load.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/parallel/test-async-wrap-pop-id-during-load.js b/test/parallel/test-async-wrap-pop-id-during-load.js index f8f20298b8163b..bd53c4d7893d49 100644 --- a/test/parallel/test-async-wrap-pop-id-during-load.js +++ b/test/parallel/test-async-wrap-pop-id-during-load.js @@ -18,9 +18,8 @@ const ret = spawnSync( ['--unhandled-rejections=none', '--stack_size=150', __filename, 'async'], { maxBuffer: Infinity } ); -const stdout = ret.stdout.toString('utf8', 0, 2048); assert.strictEqual(ret.status, 0, - `EXIT CODE: ${ret.status}, STDERR:\n${ret.stderr}, STDOUT:\n${stdout}`); + `EXIT CODE: ${ret.status}, STDERR:\n${ret.stderr}`); const stderr = ret.stderr.toString('utf8', 0, 2048); assert.doesNotMatch(stderr, /async.*hook/i); assert.ok(stderr.includes('Maximum call stack size exceeded'), stderr); From 0f5d6d0e6cec57e7a086026a5cfcd7a90c6c4357 Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Mon, 20 Dec 2021 15:51:26 -0600 Subject: [PATCH 8/8] fixup: lint --- lib/internal/async_hooks.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 400fd6f48f8c4a..f15fe5cc99b5c4 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -444,12 +444,14 @@ function clearDefaultTriggerAsyncId() { } /** + * Sets a default top level trigger ID to be used + * * @template {Array} T * @template {unknown} R * @param {number} triggerAsyncId * @param { (...T: args) => R } block * @param {T} args - * @returns + * @returns {R} */ function defaultTriggerAsyncIdScope(triggerAsyncId, block, ...args) { if (triggerAsyncId === undefined)