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

async_hooks: simplify fatalError #14051

wants to merge 5 commits into from

Conversation

AndreasMadsen
Copy link
Member

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

async_hooks

Simplify fatalError by implementing it like ClearFatalExceptionHandlers.

On my Mac I now get a SIGILL instead of SIGABRT. Very odd.

/cc @nodejs/async_hooks

Implement fatalError like ClearFatalExceptionHandlers.
@nodejs-github-bot nodejs-github-bot added the async_hooks Issues and PRs related to the async hooks subsystem. label Jul 3, 2017
@AndreasMadsen
Copy link
Member Author

Let's see how platform specific that SIGILL issue is.

CI: https://ci.nodejs.org/job/node-test-pull-request/8946/

@AndreasMadsen
Copy link
Member Author

Looks like the SIGILL issue isn't Mac specific. It is very odd.

@AndreasMadsen AndreasMadsen mentioned this pull request Jul 3, 2017
4 tasks
@@ -60,19 +60,11 @@ async_wrap.setupHooks({ init,
destroy: emitDestroyN });

// Used to fatally abort the process if a callback throws.
function fatalError(e) {
Copy link
Contributor

@refack refack Jul 3, 2017

Choose a reason for hiding this comment

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

Why is everyone trying to kill this 😨
I like it as a way to bail from a callback or a Promise.catch
Ref: #13839 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

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

Just because it is useful for Promise.catch doesn't mean that is useful here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably. I'll copy it to test/common/

Copy link
Member Author

Choose a reason for hiding this comment

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

Letting an error bubble with try {} finally {} have always been the standard way of handling throws.

Copy link
Contributor

Choose a reason for hiding this comment

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

Except what async_hooks is doing isn't standard. This is a guard to prevent an abort from C++ if it detects stack corruption that can likely result from the user try catching/Promise wrapping the call, and to get useful core dumps. It also doesn't follow how this is handled in C++ (https://github.com/nodejs/node/blob/v8.1.3/src/env-inl.h#L144-L160). I'm a big -1 on this.

Copy link
Contributor

Choose a reason for hiding this comment

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

By "exporting FatalException" do you mean expose it to JS?

Copy link
Contributor

@trevnorris trevnorris Jul 3, 2017

Choose a reason for hiding this comment

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

If anything I'd like to export the code in Environment::AsyncHooks::pop_ids() and use that as the uniform way to exit the process.

EDIT: Except it should show a JS stack instead of a C++ stack.

Copy link
Member Author

Choose a reason for hiding this comment

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

By "exporting FatalException" do you mean expose it to JS?

Yes. At the very least I would like a unified way of making a fatal exception. Currently, there are a couple of different implementations floating around in nodecore. It makes things like --abort-on-uncaught-exception difficult to test.

Copy link
Contributor

Choose a reason for hiding this comment

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

@AndreasMadsen

At the very least I would like a unified way of making a fatal exception.

Sounds good to me.

Copy link
Contributor

Choose a reason for hiding this comment

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

So area this is currently a WIP?

@AndreasMadsen
Copy link
Member Author

AndreasMadsen commented Jul 3, 2017

I have reduced the SIGILL failture to:

const assert = require('assert');
const { spawnSync } = require('child_process');

const child = spawnSync(process.execPath, [
  '--abort-on-uncaught-exception', '-e', 'throw new Error();'
]);
assert.strictEqual(child.signal, 'SIGABRT');
assert.strictEqual(child.code, null);

@AndreasMadsen
Copy link
Member Author

Blocked by #14060

}
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)

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.

}

pushAsyncIds(asyncId, triggerAsyncId);

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

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
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.

}
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.

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

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?

@refack
Copy link
Contributor

refack commented Jul 9, 2017

@trevnorris just for explicitness what's the meaning of the S and N suffixes? I'm assuming N is for Native... What's the S?

@AndreasMadsen
Copy link
Member Author

AndreasMadsen commented Jul 10, 2017

@refack As I tried to tell @BridgeAR, there is no point reviewing this as it is now. As we need to do something drastically different anyway.

What's the S?

I think it is for Script. I plan to make a rename PR in the near future.

@AndreasMadsen AndreasMadsen added the wip Issues and PRs that are still a work in progress. label Jul 10, 2017
@AndreasMadsen
Copy link
Member Author

Clearly, there is a lot of confusion so I will just close this PR.

@trevnorris
Copy link
Contributor

I think it is for Script. I plan to make a rename PR in the near future.

Yup, that's it. Thanks for the PR to make it more explicit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
async_hooks Issues and PRs related to the async hooks subsystem. wip Issues and PRs that are still a work in progress.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants