Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
5d75ba4
test: remove async_wrap tests
trevnorris Oct 31, 2016
53a85f1
stream_base,tls_wrap: notify on destruct
trevnorris Oct 31, 2016
757ac35
crypto: use named FunctionTemplate
trevnorris Sep 28, 2016
29e9d09
async_wrap: use more specific providers
trevnorris Nov 2, 2016
82d8606
async_wrap: use double, not int64_t, for uid
trevnorris Dec 13, 2016
4a427e3
async_wrap: add GetAsyncId() method
trevnorris Nov 2, 2016
f5d6056
src: add AsyncWrap::GetAsyncId() to all children
trevnorris Sep 28, 2016
1c9154c
async_wrap: only call SetupHooks() once
trevnorris Nov 2, 2016
4c809dd
async_hooks: introduce async_hooks.js
trevnorris Nov 2, 2016
3611f6a
async_hooks: check correct array
trevnorris Nov 17, 2016
d244d55
next_tick: add async_hooks support
trevnorris Nov 14, 2016
977718e
timers: initial timers support
trevnorris Nov 16, 2016
3e5ef64
fs: add initial async_hooks support
trevnorris Nov 17, 2016
955d390
net: add initial async_hooks net support
trevnorris Nov 16, 2016
da7c67c
lib: add triggerId support to various modules
trevnorris Nov 17, 2016
b3721ba
http: reset Agents on free pool
trevnorris Nov 17, 2016
78a2a7f
src: properly propagate triggerId in more places
trevnorris Nov 17, 2016
eab9e86
timers: manually set ids if no hooks exist
trevnorris Nov 18, 2016
4399547
fs: add AsyncReset to TSLWrap
trevnorris Nov 22, 2016
830428a
async_hooks: allow enable/disable while processing
trevnorris Dec 15, 2016
ad382c5
async_hooks: run after() in case of exception
trevnorris Dec 20, 2016
6e5af4a
streams: set initTriggerId before nextTick()
trevnorris Dec 20, 2016
32b5288
next_tick: set MicrotaskCallback to the void
trevnorris Dec 20, 2016
bd660a4
test: allow running with async_hook noops
trevnorris Dec 20, 2016
e8c27f8
async_hooks: move location of restoreTmpHooks()
trevnorris Dec 22, 2016
0ab9547
async_wrap: use v8::Eternal for provider strings
trevnorris Dec 27, 2016
352c89a
fs: use preallocated Float64Array
trevnorris Dec 29, 2016
e9ae576
PARTIAL remove all applicable TODOs
trevnorris Jan 3, 2017
015e665
async_hooks: verify stack integrity
trevnorris Jan 11, 2017
e2af82f
async_hooks: place destroy ids on native list
trevnorris Jan 17, 2017
1b7438f
test: add more async hook tests
trevnorris Jan 17, 2017
ca8d6db
timers: don't reset _asyncId/_triggerId
trevnorris Jan 17, 2017
ac2e137
async_hooks: return early if emitting the void
trevnorris Jan 17, 2017
da138cf
async_hooks: really always exit on exception
trevnorris Jan 18, 2017
25dd8af
async_wrap: don't allow exports to be overwritten
trevnorris Jan 18, 2017
2d98170
fix bug in array buffer size in fs
trevnorris Jan 20, 2017
7bc7d3c
PARTIAL implement new stack tracker
trevnorris Jan 20, 2017
edf67a7
timers: don't emit destroy() on same id twice
trevnorris Jan 26, 2017
90f0a63
test: make async hooks test better
trevnorris Jan 26, 2017
1ff2dfb
async_wrap: print msg on error instead of abort
trevnorris Feb 1, 2017
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
Prev Previous commit
Next Next commit
async_hooks: run after() in case of exception
In the case of an exception being caught by _fatalException(), make sure
to always run the after() callbacks to let the user know the callback
has completed.

Include test to make sure after() is triggered correctly, including
having the correct currentId.
  • Loading branch information
trevnorris committed Jan 27, 2017
commit ad382c5a8258f5e7fdcb05435771f2fb1cd88354
10 changes: 6 additions & 4 deletions lib/async_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ const { kInit, kBefore, kAfter, kDestroy, kActiveHooks, kAsyncUidCntr,
kCurrentId, kTriggerId, kInitTriggerId, kScopedTriggerId } =
async_wrap.constants;

// Expose the current_trigger_id_stack via the async_wrap binding for internal
// usage. Specifically so that _fatalException() can empty it if the exception
// is caught.
async_wrap.current_trigger_id_stack = current_trigger_id_stack;

// Setup the callbacks that node::AsyncWrap will call when there are hooks to
// process. They use the same functions as the JS embedder API.
async_wrap.setupHooks({ init,
Expand Down Expand Up @@ -294,6 +299,7 @@ function emitInitS(id, type, triggerId, handle) {
}
}

// Isn't null if hooks were added/removed while the hooks were running.
if (tmp_active_hooks_array !== null) {
restoreTmpHooks();
}
Expand Down Expand Up @@ -342,10 +348,6 @@ function emitAfterS(id) {
}

// Remove state after the call has completed.
// TODO(trevnorris): Clearing the native counters is easy, but also must
// track the length of the array manually so the native side can empty it
// in case there's an error. Or, export the array in such a way that
// fatalException can empty it as well.
// TODO(trevnorris): Add a check that kCurrentId === id to make sure the
// stack hasn't been corrupted.
if (current_trigger_id_stack.length > 0) {
Expand Down
13 changes: 13 additions & 0 deletions lib/internal/bootstrap_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@
}

function setupProcessFatal() {
const async_wrap = process.binding('async_wrap');
const async_uid_fields = async_wrap.async_uid_fields;
const { kCurrentId, kTriggerId } = async_wrap.constants;

process._fatalException = function(er) {
var caught;
Expand All @@ -306,6 +309,16 @@
// if we handled an error, then make sure any ticks get processed
} else {
NativeModule.require('timers').setImmediate(process._tickCallback);

// Emit the after() hooks now that the exception has been delt with.
NativeModule.require('async_hooks').emitAfter(
async_uid_fields[kCurrentId]);
async_uid_fields[kCurrentId] = 0;
async_uid_fields[kTriggerId] = 0;
async_wrap.current_trigger_id_stack.length = 0;
// No need to worry about running restoreTmpHooks() because that's
// only used while other hooks are running and if a hook throws then
// the application is forced to shut down.
}

return caught;
Expand Down
5 changes: 2 additions & 3 deletions src/async-wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -435,9 +435,8 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
return Local<Value>();
}

// TODO(trevnorris): It will be confusing for developers if there's a caught
// uncaught exception. Which leads to none of their after() callbacks being
// called.
// If the callback failed then the after() hooks will be called at the end
// of _fatalException().
if (async_hooks->fields()[AsyncHooks::kAfter] > 0) {
if (uid.IsEmpty())
uid = Number::New(env()->isolate(), get_id());
Expand Down
50 changes: 50 additions & 0 deletions test/parallel/test-async-wrap-after-uncaughtexception.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const thrown_ids = {};

process.on('exit', () => {
process.removeAllListeners('uncaughtException');
assert.strictEqual(Object.keys(thrown_ids).length, 0, 'ids remain');
});

process.on('uncaughtException', common.mustCall((err) => {
assert.strictEqual(err.message, thrown_ids[async_hooks.currentId()]);
}, 5));

async_hooks.createHook({
after(id) {
delete thrown_ids[id];
},
}).enable();

const simId = setImmediate(() => {
throw new Error('setImmediate');
})._asyncId;
thrown_ids[simId] = 'setImmediate';

const stId = setTimeout(() => {
throw new Error('setTimeout');
}, 10)._asyncId;
thrown_ids[stId] = 'setTimeout';

const sinId = setInterval(function() {
clearInterval(this);
throw new Error('setInterval');
}, 10)._asyncId;
thrown_ids[sinId] = 'setInterval';

const rbId = require('crypto').randomBytes(1, () => {
throw new Error('RANDOMBYTESREQUEST');
}).getAsyncId();
thrown_ids[rbId] = 'RANDOMBYTESREQUEST';

const tcpId = require('net').createServer(function(c) {
c.end();
setImmediate(() => this.close());
throw new Error('TCPWRAP');
}).listen(common.PORT)._handle.getAsyncId();
thrown_ids[tcpId] = 'TCPWRAP';
require('net').connect(common.PORT, () => {}).resume();