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: https://github.com/nodejs/node/issues/13548#issuecomment-310985270 PR-URL: https://github.com/nodejs/node/pull/14026 Reviewed-By: Refael Ackermann <refack@gmail.com>
This commit is contained in:
parent
aa8655a0da
commit
0fd4c73e5c
@ -322,9 +322,7 @@ function emitInitS(asyncId, type, triggerAsyncId, resource) {
|
|||||||
|
|
||||||
// This can run after the early return check b/c running this function
|
// This can run after the early return check b/c running this function
|
||||||
// manually means that the embedder must have used initTriggerId().
|
// manually means that the embedder must have used initTriggerId().
|
||||||
if (!Number.isSafeInteger(triggerAsyncId)) {
|
if (triggerAsyncId === null) {
|
||||||
if (triggerAsyncId !== undefined)
|
|
||||||
resource = triggerAsyncId;
|
|
||||||
triggerAsyncId = initTriggerId();
|
triggerAsyncId = initTriggerId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ function setupNextTick() {
|
|||||||
// The needed emit*() functions.
|
// The needed emit*() functions.
|
||||||
const { emitInit, emitBefore, emitAfter, emitDestroy } = async_hooks;
|
const { emitInit, emitBefore, emitAfter, emitDestroy } = async_hooks;
|
||||||
// Grab the constants necessary for working with internal arrays.
|
// Grab the constants necessary for working with internal arrays.
|
||||||
const { kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr, kInitTriggerId } =
|
const { kInit, kBefore, kAfter, kDestroy, kAsyncUidCntr } =
|
||||||
async_wrap.constants;
|
async_wrap.constants;
|
||||||
const { async_id_symbol, trigger_id_symbol } = async_wrap;
|
const { async_id_symbol, trigger_id_symbol } = async_wrap;
|
||||||
var nextTickQueue = new NextTickQueue();
|
var nextTickQueue = new NextTickQueue();
|
||||||
@ -302,6 +302,10 @@ function setupNextTick() {
|
|||||||
if (process._exiting)
|
if (process._exiting)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (triggerAsyncId === null) {
|
||||||
|
triggerAsyncId = async_hooks.initTriggerId();
|
||||||
|
}
|
||||||
|
|
||||||
var args;
|
var args;
|
||||||
switch (arguments.length) {
|
switch (arguments.length) {
|
||||||
case 2: break;
|
case 2: break;
|
||||||
@ -320,8 +324,5 @@ function setupNextTick() {
|
|||||||
++tickInfo[kLength];
|
++tickInfo[kLength];
|
||||||
if (async_hook_fields[kInit] > 0)
|
if (async_hook_fields[kInit] > 0)
|
||||||
emitInit(asyncId, 'TickObject', triggerAsyncId, obj);
|
emitInit(asyncId, 'TickObject', triggerAsyncId, obj);
|
||||||
|
|
||||||
// The call to initTriggerId() was skipped, so clear kInitTriggerId.
|
|
||||||
async_uid_fields[kInitTriggerId] = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,13 +34,13 @@ class ActivityCollector {
|
|||||||
this._logid = logid;
|
this._logid = logid;
|
||||||
this._logtype = logtype;
|
this._logtype = logtype;
|
||||||
|
|
||||||
// register event handlers if provided
|
// Register event handlers if provided
|
||||||
this.oninit = typeof oninit === 'function' ? oninit : noop;
|
this.oninit = typeof oninit === 'function' ? oninit : noop;
|
||||||
this.onbefore = typeof onbefore === 'function' ? onbefore : noop;
|
this.onbefore = typeof onbefore === 'function' ? onbefore : noop;
|
||||||
this.onafter = typeof onafter === 'function' ? onafter : noop;
|
this.onafter = typeof onafter === 'function' ? onafter : noop;
|
||||||
this.ondestroy = typeof ondestroy === 'function' ? ondestroy : noop;
|
this.ondestroy = typeof ondestroy === 'function' ? ondestroy : noop;
|
||||||
|
|
||||||
// create the hook with which we'll collect activity data
|
// Create the hook with which we'll collect activity data
|
||||||
this._asyncHook = async_hooks.createHook({
|
this._asyncHook = async_hooks.createHook({
|
||||||
init: this._init.bind(this),
|
init: this._init.bind(this),
|
||||||
before: this._before.bind(this),
|
before: this._before.bind(this),
|
||||||
@ -106,10 +106,15 @@ class ActivityCollector {
|
|||||||
'\nExpected "destroy" to be called after "after"');
|
'\nExpected "destroy" to be called after "after"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!a.handleIsObject) {
|
||||||
|
v('No resource object\n' + activityString(a) +
|
||||||
|
'\nExpected "init" to be called with a resource object');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (violations.length) {
|
if (violations.length) {
|
||||||
console.error(violations.join('\n'));
|
console.error(violations.join('\n\n') + '\n');
|
||||||
assert.fail(violations.length, 0, `Failed sanity checks: ${violations}`);
|
assert.fail(violations.length, 0,
|
||||||
|
`${violations.length} failed sanity checks`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,11 +148,11 @@ class ActivityCollector {
|
|||||||
_getActivity(uid, hook) {
|
_getActivity(uid, hook) {
|
||||||
const h = this._activities.get(uid);
|
const h = this._activities.get(uid);
|
||||||
if (!h) {
|
if (!h) {
|
||||||
// if we allowed handles without init we ignore any further life time
|
// If we allowed handles without init we ignore any further life time
|
||||||
// events this makes sense for a few tests in which we enable some hooks
|
// events this makes sense for a few tests in which we enable some hooks
|
||||||
// later
|
// later
|
||||||
if (this._allowNoInit) {
|
if (this._allowNoInit) {
|
||||||
const stub = { uid, type: 'Unknown' };
|
const stub = { uid, type: 'Unknown', handleIsObject: true };
|
||||||
this._activities.set(uid, stub);
|
this._activities.set(uid, stub);
|
||||||
return stub;
|
return stub;
|
||||||
} else {
|
} else {
|
||||||
@ -163,7 +168,14 @@ class ActivityCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_init(uid, type, triggerAsyncId, handle) {
|
_init(uid, type, triggerAsyncId, handle) {
|
||||||
const activity = { uid, type, triggerAsyncId };
|
const activity = {
|
||||||
|
uid,
|
||||||
|
type,
|
||||||
|
triggerAsyncId,
|
||||||
|
// In some cases (e.g. Timeout) the handle is a function, thus the usual
|
||||||
|
// `typeof handle === 'object' && handle !== null` check can't be used.
|
||||||
|
handleIsObject: handle instanceof Object
|
||||||
|
};
|
||||||
this._stamp(activity, 'init');
|
this._stamp(activity, 'init');
|
||||||
this._activities.set(uid, activity);
|
this._activities.set(uid, activity);
|
||||||
this._maybeLog(uid, type, 'init');
|
this._maybeLog(uid, type, 'init');
|
||||||
|
@ -46,4 +46,4 @@ initHooks({
|
|||||||
})
|
})
|
||||||
}).enable();
|
}).enable();
|
||||||
|
|
||||||
async_hooks.emitInit(expectedId, expectedType, expectedResource);
|
async_hooks.emitInit(expectedId, expectedType, null, expectedResource);
|
||||||
|
47
test/async-hooks/test-internal-nexttick-default-trigger.js
Normal file
47
test/async-hooks/test-internal-nexttick-default-trigger.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
'use strict';
|
||||||
|
// Flags: --expose-internals
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
// This tests ensures that the triggerId of both the internal and external
|
||||||
|
// nexTick function sets the triggerAsyncId correctly.
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const async_hooks = require('async_hooks');
|
||||||
|
const initHooks = require('./init-hooks');
|
||||||
|
const { checkInvocations } = require('./hook-checks');
|
||||||
|
const internal = require('internal/process/next_tick');
|
||||||
|
|
||||||
|
const hooks = initHooks();
|
||||||
|
hooks.enable();
|
||||||
|
|
||||||
|
const rootAsyncId = async_hooks.executionAsyncId();
|
||||||
|
|
||||||
|
// public
|
||||||
|
process.nextTick(common.mustCall(function() {
|
||||||
|
assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// internal default
|
||||||
|
internal.nextTick(null, common.mustCall(function() {
|
||||||
|
assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// internal
|
||||||
|
internal.nextTick(rootAsyncId + 1, common.mustCall(function() {
|
||||||
|
assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId + 1);
|
||||||
|
}));
|
||||||
|
|
||||||
|
process.on('exit', function() {
|
||||||
|
hooks.sanityCheck();
|
||||||
|
|
||||||
|
const as = hooks.activitiesOfTypes('TickObject');
|
||||||
|
checkInvocations(as[0], {
|
||||||
|
init: 1, before: 1, after: 1, destroy: 1
|
||||||
|
}, 'when process exits');
|
||||||
|
checkInvocations(as[1], {
|
||||||
|
init: 1, before: 1, after: 1, destroy: 1
|
||||||
|
}, 'when process exits');
|
||||||
|
checkInvocations(as[2], {
|
||||||
|
init: 1, before: 1, after: 1, destroy: 1
|
||||||
|
}, 'when process exits');
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user