Skip to content

Commit 7b369d1

Browse files
AndreasMadsenaddaleax
authored andcommitted
async_hooks: fix default nextTick triggerAsyncId
In the case where triggerAsyncId is null it should default to the current executionAsyncId. This worked but as a side-effect the resource object was changed too. This fix also makes the null check more strict. EmitInitS is not a documented API, thus there is no reason to be flexible in its input. Ref: #13548 (comment) PR-URL: #14026 Reviewed-By: Refael Ackermann <[email protected]>
1 parent 86c06c0 commit 7b369d1

File tree

5 files changed

+73
-15
lines changed

5 files changed

+73
-15
lines changed

‎lib/async_hooks.js‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,7 @@ function emitInitS(asyncId, type, triggerAsyncId, resource){
322322

323323
// This can run after the early return check b/c running this function
324324
// manually means that the embedder must have used initTriggerId().
325-
if(!Number.isSafeInteger(triggerAsyncId)){
326-
if(triggerAsyncId!==undefined)
327-
resource=triggerAsyncId;
325+
if(triggerAsyncId===null){
328326
triggerAsyncId=initTriggerId();
329327
}
330328

‎lib/internal/process/next_tick.js‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function setupNextTick(){
6060
// The needed emit*() functions.
6161
const{ emitInit, emitBefore, emitAfter, emitDestroy }=async_hooks;
6262
// Grab the constants necessary for working with internal arrays.
63-
const{ kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr, kInitTriggerId}=
63+
const{ kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr }=
6464
async_wrap.constants;
6565
const{ async_id_symbol, trigger_id_symbol }=async_wrap;
6666
varnextTickQueue=newNextTickQueue();
@@ -302,6 +302,10 @@ function setupNextTick(){
302302
if(process._exiting)
303303
return;
304304

305+
if(triggerAsyncId===null){
306+
triggerAsyncId=async_hooks.initTriggerId();
307+
}
308+
305309
varargs;
306310
switch(arguments.length){
307311
case2: break;
@@ -320,8 +324,5 @@ function setupNextTick(){
320324
++tickInfo[kLength];
321325
if(async_hook_fields[kInit]>0)
322326
emitInit(asyncId,'TickObject',triggerAsyncId,obj);
323-
324-
// The call to initTriggerId() was skipped, so clear kInitTriggerId.
325-
async_uid_fields[kInitTriggerId]=0;
326327
}
327328
}

‎test/async-hooks/init-hooks.js‎

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ class ActivityCollector{
3434
this._logid=logid;
3535
this._logtype=logtype;
3636

37-
// register event handlers if provided
37+
// Register event handlers if provided
3838
this.oninit=typeofoninit==='function' ? oninit : noop;
3939
this.onbefore=typeofonbefore==='function' ? onbefore : noop;
4040
this.onafter=typeofonafter==='function' ? onafter : noop;
4141
this.ondestroy=typeofondestroy==='function' ? ondestroy : noop;
4242

43-
// create the hook with which we'll collect activity data
43+
// Create the hook with which we'll collect activity data
4444
this._asyncHook=async_hooks.createHook({
4545
init: this._init.bind(this),
4646
before: this._before.bind(this),
@@ -106,10 +106,15 @@ class ActivityCollector{
106106
'\nExpected "destroy" to be called after "after"');
107107
}
108108
}
109+
if(!a.handleIsObject){
110+
v('No resource object\n'+activityString(a)+
111+
'\nExpected "init" to be called with a resource object');
112+
}
109113
}
110114
if(violations.length){
111-
console.error(violations.join('\n'));
112-
assert.fail(violations.length,0,`Failed sanity checks: ${violations}`);
115+
console.error(violations.join('\n\n')+'\n');
116+
assert.fail(violations.length,0,
117+
`${violations.length} failed sanity checks`);
113118
}
114119
}
115120

@@ -143,11 +148,11 @@ class ActivityCollector{
143148
_getActivity(uid,hook){
144149
consth=this._activities.get(uid);
145150
if(!h){
146-
// if we allowed handles without init we ignore any further life time
151+
// If we allowed handles without init we ignore any further life time
147152
// events this makes sense for a few tests in which we enable some hooks
148153
// later
149154
if(this._allowNoInit){
150-
conststub={ uid,type: 'Unknown'};
155+
conststub={ uid,type: 'Unknown',handleIsObject: true};
151156
this._activities.set(uid,stub);
152157
returnstub;
153158
}else{
@@ -163,7 +168,14 @@ class ActivityCollector{
163168
}
164169

165170
_init(uid,type,triggerAsyncId,handle){
166-
constactivity={ uid, type, triggerAsyncId };
171+
constactivity={
172+
uid,
173+
type,
174+
triggerAsyncId,
175+
// In some cases (e.g. Timeout) the handle is a function, thus the usual
176+
// `typeof handle === 'object' && handle !== null` check can't be used.
177+
handleIsObject: handleinstanceofObject
178+
};
167179
this._stamp(activity,'init');
168180
this._activities.set(uid,activity);
169181
this._maybeLog(uid,type,'init');

‎test/async-hooks/test-emit-init.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ initHooks({
4646
})
4747
}).enable();
4848

49-
async_hooks.emitInit(expectedId,expectedType,expectedResource);
49+
async_hooks.emitInit(expectedId,expectedType,null,expectedResource);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
// Flags: --expose-internals
3+
constcommon=require('../common');
4+
5+
// This tests ensures that the triggerId of both the internal and external
6+
// nexTick function sets the triggerAsyncId correctly.
7+
8+
constassert=require('assert');
9+
constasync_hooks=require('async_hooks');
10+
constinitHooks=require('./init-hooks');
11+
const{ checkInvocations }=require('./hook-checks');
12+
constinternal=require('internal/process/next_tick');
13+
14+
consthooks=initHooks();
15+
hooks.enable();
16+
17+
constrootAsyncId=async_hooks.executionAsyncId();
18+
19+
// public
20+
process.nextTick(common.mustCall(function(){
21+
assert.strictEqual(async_hooks.triggerAsyncId(),rootAsyncId);
22+
}));
23+
24+
// internal default
25+
internal.nextTick(null,common.mustCall(function(){
26+
assert.strictEqual(async_hooks.triggerAsyncId(),rootAsyncId);
27+
}));
28+
29+
// internal
30+
internal.nextTick(rootAsyncId+1,common.mustCall(function(){
31+
assert.strictEqual(async_hooks.triggerAsyncId(),rootAsyncId+1);
32+
}));
33+
34+
process.on('exit',function(){
35+
hooks.sanityCheck();
36+
37+
constas=hooks.activitiesOfTypes('TickObject');
38+
checkInvocations(as[0],{
39+
init: 1,before: 1,after: 1,destroy: 1
40+
},'when process exits');
41+
checkInvocations(as[1],{
42+
init: 1,before: 1,after: 1,destroy: 1
43+
},'when process exits');
44+
checkInvocations(as[2],{
45+
init: 1,before: 1,after: 1,destroy: 1
46+
},'when process exits');
47+
});

0 commit comments

Comments
(0)