async_hooks: fix AsyncLocalStorage in unhandledRejection cases · nodejs/node@b688f20 · GitHub
Skip to content

Commit b688f20

Browse files
bmeckdanielleadams
authored andcommitted
async_hooks: fix AsyncLocalStorage in unhandledRejection cases
PR-URL: #41202 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
1 parent 2e133d5 commit b688f20

3 files changed

Lines changed: 187 additions & 64 deletions

File tree

lib/internal/async_hooks.js

Lines changed: 10 additions & 1 deletion

lib/internal/process/promises.js

Lines changed: 68 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ const {
2727
const {
2828
pushAsyncContext,
2929
popAsyncContext,
30+
symbols: {
31+
async_id_symbol: kAsyncIdSymbol,
32+
trigger_async_id_symbol: kTriggerAsyncIdSymbol
33+
}
3034
} = require('internal/async_hooks');
31-
const async_hooks = require('async_hooks');
3235
const { isErrorStackTraceLimitWritable } = require('internal/errors');
3336

3437
// *Must* match Environment::TickInfo::Fields in src/env.h.
@@ -123,20 +126,11 @@ function resolveError(type, promise, reason) {
123126
}
124127

125128
function unhandledRejection(promise, reason) {
126-
const asyncId = async_hooks.executionAsyncId();
127-
const triggerAsyncId = async_hooks.triggerAsyncId();
128-
const resource = promise;
129-
130129
const emit = (reason, promise, promiseInfo) => {
131-
try {
132-
pushAsyncContext(asyncId, triggerAsyncId, resource);
133-
if (promiseInfo.domain) {
134-
return promiseInfo.domain.emit('error', reason);
135-
}
136-
return process.emit('unhandledRejection', reason, promise);
137-
} finally {
138-
popAsyncContext(asyncId);
130+
if (promiseInfo.domain) {
131+
return promiseInfo.domain.emit('error', reason);
139132
}
133+
return process.emit('unhandledRejection', reason, promise);
140134
};
141135

142136
maybeUnhandledPromises.set(promise, {
@@ -220,40 +214,73 @@ function processPromiseRejections() {
220214
promiseInfo.warned = true;
221215
const { reason, uid, emit } = promiseInfo;
222216

223-
switch (unhandledRejectionsMode) {
224-
case kStrictUnhandledRejections: {
225-
const err = reason instanceof Error ?
226-
reason : generateUnhandledRejectionError(reason);
227-
triggerUncaughtException(err, true /* fromPromise */);
228-
const handled = emit(reason, promise, promiseInfo);
229-
if (!handled) emitUnhandledRejectionWarning(uid, reason);
230-
break;
231-
}
232-
case kIgnoreUnhandledRejections: {
233-
emit(reason, promise, promiseInfo);
234-
break;
235-
}
236-
case kAlwaysWarnUnhandledRejections: {
237-
emit(reason, promise, promiseInfo);
238-
emitUnhandledRejectionWarning(uid, reason);
239-
break;
240-
}
241-
case kThrowUnhandledRejections: {
242-
const handled = emit(reason, promise, promiseInfo);
243-
if (!handled) {
217+
let needPop = true;
218+
const {
219+
[kAsyncIdSymbol]: promiseAsyncId,
220+
[kTriggerAsyncIdSymbol]: promiseTriggerAsyncId,
221+
} = promise;
222+
// We need to check if async_hooks are enabled
223+
// don't use enabledHooksExist as a Promise could
224+
// come from a vm.* context and not have an async id
225+
if (typeof promiseAsyncId !== 'undefined') {
226+
pushAsyncContext(
227+
promiseAsyncId,
228+
promiseTriggerAsyncId,
229+
promise
230+
);
231+
}
232+
try {
233+
switch (unhandledRejectionsMode) {
234+
case kStrictUnhandledRejections: {
244235
const err = reason instanceof Error ?
245236
reason : generateUnhandledRejectionError(reason);
237+
// This destroys the async stack, don't clear it after
246238
triggerUncaughtException(err, true /* fromPromise */);
239+
if (typeof promiseAsyncId !== 'undefined') {
240+
pushAsyncContext(
241+
promise[kAsyncIdSymbol],
242+
promise[kTriggerAsyncIdSymbol],
243+
promise
244+
);
245+
}
246+
const handled = emit(reason, promise, promiseInfo);
247+
if (!handled) emitUnhandledRejectionWarning(uid, reason);
248+
break;
247249
}
248-
break;
249-
}
250-
case kWarnWithErrorCodeUnhandledRejections: {
251-
const handled = emit(reason, promise, promiseInfo);
252-
if (!handled) {
250+
case kIgnoreUnhandledRejections: {
251+
emit(reason, promise, promiseInfo);
252+
break;
253+
}
254+
case kAlwaysWarnUnhandledRejections: {
255+
emit(reason, promise, promiseInfo);
253256
emitUnhandledRejectionWarning(uid, reason);
254-
process.exitCode = 1;
257+
break;
258+
}
259+
case kThrowUnhandledRejections: {
260+
const handled = emit(reason, promise, promiseInfo);
261+
if (!handled) {
262+
const err = reason instanceof Error ?
263+
reason : generateUnhandledRejectionError(reason);
264+
// This destroys the async stack, don't clear it after
265+
triggerUncaughtException(err, true /* fromPromise */);
266+
needPop = false;
267+
}
268+
break;
269+
}
270+
case kWarnWithErrorCodeUnhandledRejections: {
271+
const handled = emit(reason, promise, promiseInfo);
272+
if (!handled) {
273+
emitUnhandledRejectionWarning(uid, reason);
274+
process.exitCode = 1;
275+
}
276+
break;
277+
}
278+
}
279+
} finally {
280+
if (needPop) {
281+
if (typeof promiseAsyncId !== 'undefined') {
282+
popAsyncContext(promiseAsyncId);
255283
}
256-
break;
257284
}
258285
}
259286
maybeScheduledTicksOrMicrotasks = true;
Lines changed: 109 additions & 22 deletions

0 commit comments

Comments
 (0)