Skip to content

Commit 244ada3

Browse files
committedAug 30, 2017
async_hooks: emitAfter correctly on fatalException
Previously calling emitAfter() from _fatalException would skipt the first asyncId. Instead use the size() of the std::stack to determine how many times to loop and call emitAfter(). PR-URL: #14914 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
1 parent 5e443d7 commit 244ada3

File tree

6 files changed

+56
-3
lines changed

6 files changed

+56
-3
lines changed
 

‎lib/internal/bootstrap_node.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@
383383
// Arrays containing hook flags and ids for async_hook calls.
384384
const { async_hook_fields, async_uid_fields } = async_wrap;
385385
// Internal functions needed to manipulate the stack.
386-
const { clearIdStack, popAsyncIds } = async_wrap;
386+
const { clearIdStack, asyncIdStackSize } = async_wrap;
387387
const { kAfter, kCurrentAsyncId, kInitTriggerId } = async_wrap.constants;
388388

389389
process._fatalException = function(er) {
@@ -420,8 +420,7 @@
420420
do {
421421
NativeModule.require('async_hooks').emitAfter(
422422
async_uid_fields[kCurrentAsyncId]);
423-
// popAsyncIds() returns true if there are more ids on the stack.
424-
} while (popAsyncIds(async_uid_fields[kCurrentAsyncId]));
423+
} while (asyncIdStackSize() > 0);
425424
// Or completely empty the id stack.
426425
} else {
427426
clearIdStack();

‎src/async-wrap.cc

+8
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,13 @@ void AsyncWrap::PopAsyncIds(const FunctionCallbackInfo<Value>& args) {
450450
}
451451

452452

453+
void AsyncWrap::AsyncIdStackSize(const FunctionCallbackInfo<Value>& args) {
454+
Environment* env = Environment::GetCurrent(args);
455+
args.GetReturnValue().Set(
456+
static_cast<double>(env->async_hooks()->stack_size()));
457+
}
458+
459+
453460
void AsyncWrap::ClearIdStack(const FunctionCallbackInfo<Value>& args) {
454461
Environment* env = Environment::GetCurrent(args);
455462
env->async_hooks()->clear_id_stack();
@@ -486,6 +493,7 @@ void AsyncWrap::Initialize(Local<Object> target,
486493
env->SetMethod(target, "setupHooks", SetupHooks);
487494
env->SetMethod(target, "pushAsyncIds", PushAsyncIds);
488495
env->SetMethod(target, "popAsyncIds", PopAsyncIds);
496+
env->SetMethod(target, "asyncIdStackSize", AsyncIdStackSize);
489497
env->SetMethod(target, "clearIdStack", ClearIdStack);
490498
env->SetMethod(target, "addIdToDestroyList", QueueDestroyId);
491499
env->SetMethod(target, "enablePromiseHook", EnablePromiseHook);

‎src/async-wrap.h

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ class AsyncWrap : public BaseObject {
110110
static void GetAsyncId(const v8::FunctionCallbackInfo<v8::Value>& args);
111111
static void PushAsyncIds(const v8::FunctionCallbackInfo<v8::Value>& args);
112112
static void PopAsyncIds(const v8::FunctionCallbackInfo<v8::Value>& args);
113+
static void AsyncIdStackSize(const v8::FunctionCallbackInfo<v8::Value>& args);
113114
static void ClearIdStack(const v8::FunctionCallbackInfo<v8::Value>& args);
114115
static void AsyncReset(const v8::FunctionCallbackInfo<v8::Value>& args);
115116
static void QueueDestroyId(const v8::FunctionCallbackInfo<v8::Value>& args);

‎src/env-inl.h

+4
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ inline bool Environment::AsyncHooks::pop_ids(double async_id) {
166166
return !ids_stack_.empty();
167167
}
168168

169+
inline size_t Environment::AsyncHooks::stack_size() {
170+
return ids_stack_.size();
171+
}
172+
169173
inline void Environment::AsyncHooks::clear_id_stack() {
170174
while (!ids_stack_.empty())
171175
ids_stack_.pop();

‎src/env.h

+1
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ class Environment {
400400

401401
inline void push_ids(double async_id, double trigger_id);
402402
inline bool pop_ids(double async_id);
403+
inline size_t stack_size();
403404
inline void clear_id_stack(); // Used in fatal exceptions.
404405

405406
// Used to propagate the trigger_id to the constructor of any newly created
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const async_hooks = require('async_hooks');
6+
7+
const id_obj = {};
8+
let collect = true;
9+
10+
const hook = async_hooks.createHook({
11+
before(id) { if (collect) id_obj[id] = true; },
12+
after(id) { delete id_obj[id]; },
13+
}).enable();
14+
15+
process.once('uncaughtException', common.mustCall((er) => {
16+
assert.strictEqual(er.message, 'bye');
17+
collect = false;
18+
}));
19+
20+
setImmediate(common.mustCall(() => {
21+
process.nextTick(common.mustCall(() => {
22+
assert.strictEqual(Object.keys(id_obj).length, 0);
23+
hook.disable();
24+
}));
25+
26+
// Create a stack of async ids that will need to be emitted in the case of
27+
// an uncaught exception.
28+
const ar1 = new async_hooks.AsyncResource('Mine');
29+
ar1.emitBefore();
30+
31+
const ar2 = new async_hooks.AsyncResource('Mine');
32+
ar2.emitBefore();
33+
34+
throw new Error('bye');
35+
36+
// TODO(trevnorris): This test shows that the after() hooks are always called
37+
// correctly, but it doesn't solve where the emitDestroy() is missed because
38+
// of the uncaught exception. Simple solution is to always call emitDestroy()
39+
// before the emitAfter(), but how to codify this?
40+
}));

0 commit comments

Comments
 (0)
Please sign in to comment.