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: simplify fatalError #14051

Closed
wants to merge 5 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 42 additions & 46 deletions lib/async_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,14 @@ function emitInitS(asyncId, type, triggerAsyncId, resource) {
if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < 0)
throw new RangeError('triggerAsyncId must be an unsigned integer');

init(asyncId, type, triggerAsyncId, resource);
// Remove prepear for a fatal exception in case an error occurred
var didThrow = true;
try {
init(asyncId, type, triggerAsyncId, resource);
didThrow = false;
} finally {
if (didThrow) clearFatalExceptionHandlers();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's correct to move this line from the N variant to the S variant?

}

// Isn't null if hooks were added/removed while the hooks were running.
if (tmp_active_hooks_array !== null) {
Expand All @@ -337,17 +344,10 @@ function emitInitS(asyncId, type, triggerAsyncId, resource) {

function emitBeforeN(asyncId) {
processing_hook = true;
var didThrow = true;
// Use a single try/catch for all hook to avoid setting up one per iteration.
try {
for (var i = 0; i < active_hooks_array.length; i++) {
if (typeof active_hooks_array[i][before_symbol] === 'function') {
active_hooks_array[i][before_symbol](asyncId);
}
for (var i = 0; i < active_hooks_array.length; i++) {
if (typeof active_hooks_array[i][before_symbol] === 'function') {
active_hooks_array[i][before_symbol](asyncId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allowing an exception to bubble then be handled by a Promise or similar means that it's possible that not all hook callbacks execute, but users that have those unexecuted callbacks won't know that it hasn't been called. Consider the following:

const map = new Map();
createHook({
  before(id) { map.set(id, /* some object */) },
  after(id) { map.delete(id) },
}).enable();

You've just introduced a vector for a memory leak.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW If you're touching this you could use for...of (supposed to be tiny bit faster on TF&I)
Ref: #13419 (comment)

}
didThrow = false;
} finally {
if (didThrow) clearFatalExceptionHandlers();
}
processing_hook = false;

Expand Down Expand Up @@ -376,25 +376,26 @@ function emitBeforeS(asyncId, triggerAsyncId = asyncId) {

if (async_hook_fields[kBefore] === 0)
return;
emitBeforeN(asyncId);

// Remove prepear for a fatal exception in case an error occurred
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/prepear/prepare

var didThrow = true;
try {
emitBeforeN(asyncId);
didThrow = false;
} finally {
if (didThrow) clearFatalExceptionHandlers();
}
}


// Called from native. The asyncId stack handling is taken care of there before
// this is called.
function emitAfterN(asyncId) {
processing_hook = true;
var didThrow = true;
// Use a single try/catch for all hook to avoid setting up one per iteration.
try {
for (var i = 0; i < active_hooks_array.length; i++) {
if (typeof active_hooks_array[i][after_symbol] === 'function') {
active_hooks_array[i][after_symbol](asyncId);
}
for (var i = 0; i < active_hooks_array.length; i++) {
if (typeof active_hooks_array[i][after_symbol] === 'function') {
active_hooks_array[i][after_symbol](asyncId);
}
didThrow = false;
} catch (e) {
if (didThrow) clearFatalExceptionHandlers();
}
processing_hook = false;

Expand All @@ -408,8 +409,16 @@ function emitAfterN(asyncId) {
// kIdStackIndex. But what happens if the user doesn't have both before and
// after callbacks.
function emitAfterS(asyncId) {
if (async_hook_fields[kAfter] > 0)
emitAfterN(asyncId);
if (async_hook_fields[kAfter] > 0) {
// Remove prepear for a fatal exception in case an error occurred
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/prepear/prepare

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndreasMadsen is the comment a TODO? Otherwise I'm not sure I understand what It means.

var didThrow = true;
try {
emitAfterN(asyncId);
didThrow = false;
} finally {
if (didThrow) clearFatalExceptionHandlers();
}
}

popAsyncIds(asyncId);
}
Expand All @@ -426,17 +435,10 @@ function emitDestroyS(asyncId) {

function emitDestroyN(asyncId) {
processing_hook = true;
var didThrow = true;
// Use a single try/catch for all hook to avoid setting up one per iteration.
try {
for (var i = 0; i < active_hooks_array.length; i++) {
if (typeof active_hooks_array[i][destroy_symbol] === 'function') {
active_hooks_array[i][destroy_symbol](asyncId);
}
for (var i = 0; i < active_hooks_array.length; i++) {
if (typeof active_hooks_array[i][destroy_symbol] === 'function') {
active_hooks_array[i][destroy_symbol](asyncId);
}
didThrow = false;
} finally {
if (didThrow) clearFatalExceptionHandlers();
}
processing_hook = false;

Expand All @@ -461,19 +463,13 @@ function emitDestroyN(asyncId) {
// exceptions.
function init(asyncId, type, triggerAsyncId, resource) {
processing_hook = true;
var didThrow = true;
// Use a single try/catch for all hook to avoid setting up one per iteration.
try {
for (var i = 0; i < active_hooks_array.length; i++) {
if (typeof active_hooks_array[i][init_symbol] === 'function') {
active_hooks_array[i][init_symbol](
asyncId, type, triggerAsyncId,
resource
);
}
for (var i = 0; i < active_hooks_array.length; i++) {
if (typeof active_hooks_array[i][init_symbol] === 'function') {
active_hooks_array[i][init_symbol](
asyncId, type, triggerAsyncId,
resource
);
}
} finally {
if (didThrow) clearFatalExceptionHandlers();
}
processing_hook = false;
}
Expand Down