Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async_hooks: fix AsyncLocalStorage in unhandledRejection cases #41202

Closed
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
69 changes: 41 additions & 28 deletions lib/internal/process/promises.js
Expand Up @@ -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');
Expand Down Expand Up @@ -220,41 +224,50 @@ 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;
}
Expand Down
33 changes: 23 additions & 10 deletions 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();
const callbackToken = {};
const awaitToken = {};

let i = 0;
process.setUncaughtExceptionCaptureCallback((err) => {
++i;
assert.strictEqual(err.message, 'err2');
assert.strictEqual(asyncLocalStorage.getStore().get('hello'), 'node');
});
const exceptionHandler = common.mustCall(
(err) => {
++i;
assert.strictEqual(err.message, 'err2');
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);
Expand Down