mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
async_hooks: deprecate undocumented API
PR-URL: https://github.com/nodejs/node/pull/16972 Refs: https://github.com/nodejs/node/issues/14328 Refs: https://github.com/nodejs/node/issues/15572 Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
parent
97ba69f915
commit
07a4fa3dbe
@ -780,6 +780,24 @@ On the other hand, `node-inspect` may be installed locally through a package
|
||||
manager, as it is published on the npm registry under the same name. No source
|
||||
code modification is necessary if that is done.
|
||||
|
||||
<a id="DEP0085"></a>
|
||||
### DEP0085: AsyncHooks Sensitive API
|
||||
|
||||
Type: Runtime
|
||||
|
||||
The AsyncHooks Sensitive API was never documented and had various of minor
|
||||
issues, see https://github.com/nodejs/node/issues/15572. Use the `AsyncResource`
|
||||
API instead.
|
||||
|
||||
|
||||
<a id="DEP0086"></a>
|
||||
### DEP0086: Remove runInAsyncIdScope
|
||||
|
||||
Type: Runtime
|
||||
|
||||
`runInAsyncIdScope` doesn't emit the `before` or `after` event and can thus
|
||||
cause a lot of issues. See https://github.com/nodejs/node/issues/14328 for more
|
||||
details.
|
||||
|
||||
[`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size
|
||||
[`Buffer.from(array)`]: buffer.html#buffer_class_method_buffer_from_array
|
||||
|
@ -1,118 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
const async_wrap = process.binding('async_wrap');
|
||||
const errors = require('internal/errors');
|
||||
/* async_hook_fields is a Uint32Array wrapping the uint32_t array of
|
||||
* Environment::AsyncHooks::fields_[]. Each index tracks the number of active
|
||||
* hooks for each type.
|
||||
*
|
||||
* async_id_fields is a Float64Array wrapping the double array of
|
||||
* Environment::AsyncHooks::async_id_fields_[]. Each index contains the ids for
|
||||
* the various asynchronous states of the application. These are:
|
||||
* kExecutionAsyncId: The async_id assigned to the resource responsible for the
|
||||
* current execution stack.
|
||||
* kTriggerAsyncId: The trigger_async_id of the resource responsible for
|
||||
* the current execution stack.
|
||||
* kAsyncIdCounter: Incremental counter tracking the next assigned async_id.
|
||||
* kInitTriggerAsyncId: Written immediately before a resource's constructor
|
||||
* that sets the value of the init()'s triggerAsyncId. The order of
|
||||
* retrieving the triggerAsyncId value is passing directly to the
|
||||
* constructor -> value set in kInitTriggerAsyncId -> executionAsyncId of
|
||||
* the current resource.
|
||||
*/
|
||||
const { async_hook_fields, async_id_fields } = async_wrap;
|
||||
// Store the pair executionAsyncId and triggerAsyncId in a std::stack on
|
||||
// Environment::AsyncHooks::ids_stack_ tracks the resource responsible for the
|
||||
// current execution stack. This is unwound as each resource exits. In the case
|
||||
// of a fatal exception this stack is emptied after calling each hook's after()
|
||||
// callback.
|
||||
const internalUtil = require('internal/util');
|
||||
const async_wrap = process.binding('async_wrap');
|
||||
const internal_async_hooks = require('internal/async_hooks');
|
||||
|
||||
// Get functions
|
||||
// Only used to support a deprecated API. pushAsyncIds, popAsyncIds should
|
||||
// never be directly in this manner.
|
||||
const { pushAsyncIds, popAsyncIds } = async_wrap;
|
||||
// For performance reasons, only track Proimses when a hook is enabled.
|
||||
const { enablePromiseHook, disablePromiseHook } = async_wrap;
|
||||
// For userland AsyncResources, make sure to emit a destroy event when the
|
||||
// resource gets gced.
|
||||
const { registerDestroyHook } = async_wrap;
|
||||
// Properties in active_hooks are used to keep track of the set of hooks being
|
||||
// executed in case another hook is enabled/disabled. The new set of hooks is
|
||||
// then restored once the active set of hooks is finished executing.
|
||||
const active_hooks = {
|
||||
// Array of all AsyncHooks that will be iterated whenever an async event
|
||||
// fires. Using var instead of (preferably const) in order to assign
|
||||
// active_hooks.tmp_array if a hook is enabled/disabled during hook
|
||||
// execution.
|
||||
array: [],
|
||||
// Use a counter to track nested calls of async hook callbacks and make sure
|
||||
// the active_hooks.array isn't altered mid execution.
|
||||
call_depth: 0,
|
||||
// Use to temporarily store and updated active_hooks.array if the user
|
||||
// enables or disables a hook while hooks are being processed. If a hook is
|
||||
// enabled() or disabled() during hook execution then the current set of
|
||||
// active hooks is duplicated and set equal to active_hooks.tmp_array. Any
|
||||
// subsequent changes are on the duplicated array. When all hooks have
|
||||
// completed executing active_hooks.tmp_array is assigned to
|
||||
// active_hooks.array.
|
||||
tmp_array: null,
|
||||
// Keep track of the field counts held in active_hooks.tmp_array. Because the
|
||||
// async_hook_fields can't be reassigned, store each uint32 in an array that
|
||||
// is written back to async_hook_fields when active_hooks.array is restored.
|
||||
tmp_fields: null
|
||||
};
|
||||
const {
|
||||
// Private API
|
||||
getHookArrays,
|
||||
enableHooks,
|
||||
disableHooks,
|
||||
// Sensitive Embedder API
|
||||
newUid,
|
||||
initTriggerId,
|
||||
setInitTriggerId,
|
||||
emitInit,
|
||||
emitBefore,
|
||||
emitAfter,
|
||||
emitDestroy,
|
||||
} = internal_async_hooks;
|
||||
|
||||
// Get fields
|
||||
const { async_id_fields } = async_wrap;
|
||||
|
||||
// Each constant tracks how many callbacks there are for any given step of
|
||||
// async execution. These are tracked so if the user didn't include callbacks
|
||||
// for a given step, that step can bail out early.
|
||||
const { kInit, kBefore, kAfter, kDestroy, kTotals, kPromiseResolve,
|
||||
kCheck, kExecutionAsyncId, kTriggerAsyncId, kAsyncIdCounter,
|
||||
kInitTriggerAsyncId } = async_wrap.constants;
|
||||
// Get symbols
|
||||
const {
|
||||
init_symbol, before_symbol, after_symbol, destroy_symbol,
|
||||
promise_resolve_symbol
|
||||
} = internal_async_hooks.symbols;
|
||||
|
||||
// Symbols used to store the respective ids on both AsyncResource instances and
|
||||
// internal resources. They will also be assigned to arbitrary objects passed
|
||||
// in by the user that take place of internally constructed objects.
|
||||
const { async_id_symbol, trigger_async_id_symbol } = async_wrap;
|
||||
|
||||
// Used in AsyncHook and AsyncResource.
|
||||
const init_symbol = Symbol('init');
|
||||
const before_symbol = Symbol('before');
|
||||
const after_symbol = Symbol('after');
|
||||
const destroy_symbol = Symbol('destroy');
|
||||
const promise_resolve_symbol = Symbol('promiseResolve');
|
||||
const emitBeforeNative = emitHookFactory(before_symbol, 'emitBeforeNative');
|
||||
const emitAfterNative = emitHookFactory(after_symbol, 'emitAfterNative');
|
||||
const emitDestroyNative = emitHookFactory(destroy_symbol, 'emitDestroyNative');
|
||||
const emitPromiseResolveNative =
|
||||
emitHookFactory(promise_resolve_symbol, 'emitPromiseResolveNative');
|
||||
// Get constants
|
||||
const {
|
||||
kInit, kBefore, kAfter, kDestroy, kTotals, kPromiseResolve,
|
||||
kExecutionAsyncId, kTriggerAsyncId
|
||||
} = async_wrap.constants;
|
||||
|
||||
// TODO(refack): move to node-config.cc
|
||||
const abort_regex = /^--abort[_-]on[_-]uncaught[_-]exception$/;
|
||||
|
||||
// Setup the callbacks that node::AsyncWrap will call when there are hooks to
|
||||
// process. They use the same functions as the JS embedder API. These callbacks
|
||||
// are setup immediately to prevent async_wrap.setupHooks() from being hijacked
|
||||
// and the cost of doing so is negligible.
|
||||
async_wrap.setupHooks({ init: emitInitNative,
|
||||
before: emitBeforeNative,
|
||||
after: emitAfterNative,
|
||||
destroy: emitDestroyNative,
|
||||
promise_resolve: emitPromiseResolveNative });
|
||||
|
||||
// Used to fatally abort the process if a callback throws.
|
||||
function fatalError(e) {
|
||||
if (typeof e.stack === 'string') {
|
||||
process._rawDebug(e.stack);
|
||||
} else {
|
||||
const o = { message: e };
|
||||
Error.captureStackTrace(o, fatalError);
|
||||
process._rawDebug(o.stack);
|
||||
}
|
||||
if (process.execArgv.some((e) => abort_regex.test(e))) {
|
||||
process.abort();
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
// Public API //
|
||||
// Listener API //
|
||||
|
||||
class AsyncHook {
|
||||
constructor({ init, before, after, destroy, promiseResolve }) {
|
||||
@ -160,8 +92,7 @@ class AsyncHook {
|
||||
hooks_array.push(this);
|
||||
|
||||
if (prev_kTotals === 0 && hook_fields[kTotals] > 0) {
|
||||
enablePromiseHook();
|
||||
hook_fields[kCheck] += 1;
|
||||
enableHooks();
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -186,8 +117,7 @@ class AsyncHook {
|
||||
hooks_array.splice(index, 1);
|
||||
|
||||
if (prev_kTotals > 0 && hook_fields[kTotals] === 0) {
|
||||
disablePromiseHook();
|
||||
hook_fields[kCheck] -= 1;
|
||||
disableHooks();
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -195,47 +125,6 @@ class AsyncHook {
|
||||
}
|
||||
|
||||
|
||||
function getHookArrays() {
|
||||
if (active_hooks.call_depth === 0)
|
||||
return [active_hooks.array, async_hook_fields];
|
||||
// If this hook is being enabled while in the middle of processing the array
|
||||
// of currently active hooks then duplicate the current set of active hooks
|
||||
// and store this there. This shouldn't fire until the next time hooks are
|
||||
// processed.
|
||||
if (active_hooks.tmp_array === null)
|
||||
storeActiveHooks();
|
||||
return [active_hooks.tmp_array, active_hooks.tmp_fields];
|
||||
}
|
||||
|
||||
|
||||
function storeActiveHooks() {
|
||||
active_hooks.tmp_array = active_hooks.array.slice();
|
||||
// Don't want to make the assumption that kInit to kDestroy are indexes 0 to
|
||||
// 4. So do this the long way.
|
||||
active_hooks.tmp_fields = [];
|
||||
active_hooks.tmp_fields[kInit] = async_hook_fields[kInit];
|
||||
active_hooks.tmp_fields[kBefore] = async_hook_fields[kBefore];
|
||||
active_hooks.tmp_fields[kAfter] = async_hook_fields[kAfter];
|
||||
active_hooks.tmp_fields[kDestroy] = async_hook_fields[kDestroy];
|
||||
active_hooks.tmp_fields[kPromiseResolve] = async_hook_fields[kPromiseResolve];
|
||||
}
|
||||
|
||||
|
||||
// Then restore the correct hooks array in case any hooks were added/removed
|
||||
// during hook callback execution.
|
||||
function restoreActiveHooks() {
|
||||
active_hooks.array = active_hooks.tmp_array;
|
||||
async_hook_fields[kInit] = active_hooks.tmp_fields[kInit];
|
||||
async_hook_fields[kBefore] = active_hooks.tmp_fields[kBefore];
|
||||
async_hook_fields[kAfter] = active_hooks.tmp_fields[kAfter];
|
||||
async_hook_fields[kDestroy] = active_hooks.tmp_fields[kDestroy];
|
||||
async_hook_fields[kPromiseResolve] = active_hooks.tmp_fields[kPromiseResolve];
|
||||
|
||||
active_hooks.tmp_array = null;
|
||||
active_hooks.tmp_fields = null;
|
||||
}
|
||||
|
||||
|
||||
function createHook(fns) {
|
||||
return new AsyncHook(fns);
|
||||
}
|
||||
@ -250,15 +139,6 @@ function triggerAsyncId() {
|
||||
return async_id_fields[kTriggerAsyncId];
|
||||
}
|
||||
|
||||
function validateAsyncId(asyncId, type) {
|
||||
// Skip validation when async_hooks is disabled
|
||||
if (async_hook_fields[kCheck] <= 0) return;
|
||||
|
||||
if (!Number.isSafeInteger(asyncId) || asyncId < -1) {
|
||||
fatalError(new errors.RangeError('ERR_INVALID_ASYNC_ID', type, asyncId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Embedder API //
|
||||
|
||||
@ -284,12 +164,12 @@ class AsyncResource {
|
||||
triggerAsyncId);
|
||||
}
|
||||
|
||||
this[async_id_symbol] = ++async_id_fields[kAsyncIdCounter];
|
||||
this[async_id_symbol] = newUid();
|
||||
this[trigger_async_id_symbol] = triggerAsyncId;
|
||||
// this prop name (destroyed) has to be synchronized with C++
|
||||
this[destroyedSymbol] = { destroyed: false };
|
||||
|
||||
emitInitScript(
|
||||
emitInit(
|
||||
this[async_id_symbol], type, this[trigger_async_id_symbol], this
|
||||
);
|
||||
|
||||
@ -299,18 +179,18 @@ class AsyncResource {
|
||||
}
|
||||
|
||||
emitBefore() {
|
||||
emitBeforeScript(this[async_id_symbol], this[trigger_async_id_symbol]);
|
||||
emitBefore(this[async_id_symbol], this[trigger_async_id_symbol]);
|
||||
return this;
|
||||
}
|
||||
|
||||
emitAfter() {
|
||||
emitAfterScript(this[async_id_symbol]);
|
||||
emitAfter(this[async_id_symbol]);
|
||||
return this;
|
||||
}
|
||||
|
||||
emitDestroy() {
|
||||
this[destroyedSymbol].destroyed = true;
|
||||
emitDestroyScript(this[async_id_symbol]);
|
||||
emitDestroy(this[async_id_symbol]);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -336,168 +216,6 @@ function runInAsyncIdScope(asyncId, cb) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sensitive Embedder API //
|
||||
|
||||
// Increment the internal id counter and return the value. Important that the
|
||||
// counter increment first. Since it's done the same way in
|
||||
// Environment::new_async_uid()
|
||||
function newUid() {
|
||||
return ++async_id_fields[kAsyncIdCounter];
|
||||
}
|
||||
|
||||
|
||||
// Return the triggerAsyncId meant for the constructor calling it. It's up to
|
||||
// the user to safeguard this call and make sure it's zero'd out when the
|
||||
// constructor is complete.
|
||||
function initTriggerId() {
|
||||
var triggerAsyncId = async_id_fields[kInitTriggerAsyncId];
|
||||
// Reset value after it's been called so the next constructor doesn't
|
||||
// inherit it by accident.
|
||||
async_id_fields[kInitTriggerAsyncId] = 0;
|
||||
if (triggerAsyncId <= 0)
|
||||
triggerAsyncId = async_id_fields[kExecutionAsyncId];
|
||||
return triggerAsyncId;
|
||||
}
|
||||
|
||||
|
||||
function setInitTriggerId(triggerAsyncId) {
|
||||
// CHECK(Number.isSafeInteger(triggerAsyncId))
|
||||
// CHECK(triggerAsyncId > 0)
|
||||
async_id_fields[kInitTriggerAsyncId] = triggerAsyncId;
|
||||
}
|
||||
|
||||
|
||||
function emitInitScript(asyncId, type, triggerAsyncId, resource) {
|
||||
validateAsyncId(asyncId, 'asyncId');
|
||||
if (triggerAsyncId !== null)
|
||||
validateAsyncId(triggerAsyncId, 'triggerAsyncId');
|
||||
if (async_hook_fields[kCheck] > 0 &&
|
||||
(typeof type !== 'string' || type.length <= 0)) {
|
||||
throw new errors.TypeError('ERR_ASYNC_TYPE', type);
|
||||
}
|
||||
|
||||
// Short circuit all checks for the common case. Which is that no hooks have
|
||||
// been set. Do this to remove performance impact for embedders (and core).
|
||||
if (async_hook_fields[kInit] === 0)
|
||||
return;
|
||||
|
||||
// This can run after the early return check b/c running this function
|
||||
// manually means that the embedder must have used initTriggerId().
|
||||
if (triggerAsyncId === null) {
|
||||
triggerAsyncId = initTriggerId();
|
||||
} else {
|
||||
// If a triggerAsyncId was passed, any kInitTriggerAsyncId still must be
|
||||
// null'd.
|
||||
async_id_fields[kInitTriggerAsyncId] = 0;
|
||||
}
|
||||
|
||||
emitInitNative(asyncId, type, triggerAsyncId, resource);
|
||||
}
|
||||
|
||||
function emitHookFactory(symbol, name) {
|
||||
// Called from native. The asyncId stack handling is taken care of there
|
||||
// before this is called.
|
||||
// eslint-disable-next-line func-style
|
||||
const fn = function(asyncId) {
|
||||
active_hooks.call_depth += 1;
|
||||
// Use a single try/catch for all hook to avoid setting up one per
|
||||
// iteration.
|
||||
try {
|
||||
for (var i = 0; i < active_hooks.array.length; i++) {
|
||||
if (typeof active_hooks.array[i][symbol] === 'function') {
|
||||
active_hooks.array[i][symbol](asyncId);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
fatalError(e);
|
||||
} finally {
|
||||
active_hooks.call_depth -= 1;
|
||||
}
|
||||
|
||||
// Hooks can only be restored if there have been no recursive hook calls.
|
||||
// Also the active hooks do not need to be restored if enable()/disable()
|
||||
// weren't called during hook execution, in which case
|
||||
// active_hooks.tmp_array will be null.
|
||||
if (active_hooks.call_depth === 0 && active_hooks.tmp_array !== null) {
|
||||
restoreActiveHooks();
|
||||
}
|
||||
};
|
||||
|
||||
// Set the name property of the anonymous function as it looks good in the
|
||||
// stack trace.
|
||||
Object.defineProperty(fn, 'name', {
|
||||
value: name
|
||||
});
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
function emitBeforeScript(asyncId, triggerAsyncId) {
|
||||
// Validate the ids. An id of -1 means it was never set and is visible on the
|
||||
// call graph. An id < -1 should never happen in any circumstance. Throw
|
||||
// on user calls because async state should still be recoverable.
|
||||
validateAsyncId(asyncId, 'asyncId');
|
||||
validateAsyncId(triggerAsyncId, 'triggerAsyncId');
|
||||
|
||||
pushAsyncIds(asyncId, triggerAsyncId);
|
||||
|
||||
if (async_hook_fields[kBefore] > 0)
|
||||
emitBeforeNative(asyncId);
|
||||
}
|
||||
|
||||
|
||||
function emitAfterScript(asyncId) {
|
||||
validateAsyncId(asyncId, 'asyncId');
|
||||
|
||||
if (async_hook_fields[kAfter] > 0)
|
||||
emitAfterNative(asyncId);
|
||||
|
||||
popAsyncIds(asyncId);
|
||||
}
|
||||
|
||||
|
||||
function emitDestroyScript(asyncId) {
|
||||
validateAsyncId(asyncId, 'asyncId');
|
||||
|
||||
// Return early if there are no destroy callbacks, or invalid asyncId.
|
||||
if (async_hook_fields[kDestroy] === 0 || asyncId <= 0)
|
||||
return;
|
||||
async_wrap.queueDestroyAsyncId(asyncId);
|
||||
}
|
||||
|
||||
|
||||
// Used by C++ to call all init() callbacks. Because some state can be setup
|
||||
// from C++ there's no need to perform all the same operations as in
|
||||
// emitInitScript.
|
||||
function emitInitNative(asyncId, type, triggerAsyncId, resource) {
|
||||
active_hooks.call_depth += 1;
|
||||
// Use a single try/catch for all hook to avoid setting up one per iteration.
|
||||
try {
|
||||
for (var i = 0; i < active_hooks.array.length; i++) {
|
||||
if (typeof active_hooks.array[i][init_symbol] === 'function') {
|
||||
active_hooks.array[i][init_symbol](
|
||||
asyncId, type, triggerAsyncId,
|
||||
resource
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
fatalError(e);
|
||||
} finally {
|
||||
active_hooks.call_depth -= 1;
|
||||
}
|
||||
|
||||
// Hooks can only be restored if there have been no recursive hook calls.
|
||||
// Also the active hooks do not need to be restored if enable()/disable()
|
||||
// weren't called during hook execution, in which case active_hooks.tmp_array
|
||||
// will be null.
|
||||
if (active_hooks.call_depth === 0 && active_hooks.tmp_array !== null) {
|
||||
restoreActiveHooks();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Placing all exports down here because the exported classes won't export
|
||||
// otherwise.
|
||||
module.exports = {
|
||||
@ -507,13 +225,62 @@ module.exports = {
|
||||
triggerAsyncId,
|
||||
// Embedder API
|
||||
AsyncResource,
|
||||
runInAsyncIdScope,
|
||||
// Sensitive Embedder API
|
||||
newUid,
|
||||
initTriggerId,
|
||||
setInitTriggerId,
|
||||
emitInit: emitInitScript,
|
||||
emitBefore: emitBeforeScript,
|
||||
emitAfter: emitAfterScript,
|
||||
emitDestroy: emitDestroyScript,
|
||||
};
|
||||
|
||||
// Deprecated API //
|
||||
|
||||
Object.defineProperty(module.exports, 'runInAsyncIdScope', {
|
||||
get: internalUtil.deprecate(function() {
|
||||
return runInAsyncIdScope;
|
||||
}, 'async_hooks.runInAsyncIdScope is deprecated. ' +
|
||||
'Create an AsyncResource instead.', 'DEP0086')
|
||||
});
|
||||
|
||||
Object.defineProperty(module.exports, 'newUid', {
|
||||
get: internalUtil.deprecate(function() {
|
||||
return newUid;
|
||||
}, 'async_hooks.newUid is deprecated. ' +
|
||||
'Use AsyncResource instead.', 'DEP0085')
|
||||
});
|
||||
|
||||
Object.defineProperty(module.exports, 'initTriggerId', {
|
||||
get: internalUtil.deprecate(function() {
|
||||
return initTriggerId;
|
||||
}, 'async_hooks.initTriggerId is deprecated. ' +
|
||||
'Use the AsyncResource default instead.', 'DEP0085')
|
||||
});
|
||||
|
||||
Object.defineProperty(module.exports, 'setInitTriggerId', {
|
||||
get: internalUtil.deprecate(function() {
|
||||
return setInitTriggerId;
|
||||
}, 'async_hooks.setInitTriggerId is deprecated. ' +
|
||||
'Use the triggerAsyncId parameter in AsyncResource instead.', 'DEP0085')
|
||||
});
|
||||
|
||||
Object.defineProperty(module.exports, 'emitInit', {
|
||||
get: internalUtil.deprecate(function() {
|
||||
return emitInit;
|
||||
}, 'async_hooks.emitInit is deprecated. ' +
|
||||
'Use AsyncResource constructor instead.', 'DEP0085')
|
||||
});
|
||||
|
||||
Object.defineProperty(module.exports, 'emitBefore', {
|
||||
get: internalUtil.deprecate(function() {
|
||||
return emitBefore;
|
||||
}, 'async_hooks.emitBefore is deprecated. ' +
|
||||
'Use AsyncResource.emitBefore instead.', 'DEP0085')
|
||||
});
|
||||
|
||||
Object.defineProperty(module.exports, 'emitAfter', {
|
||||
get: internalUtil.deprecate(function() {
|
||||
return emitAfter;
|
||||
}, 'async_hooks.emitAfter is deprecated. ' +
|
||||
'Use AsyncResource.emitAfter instead.', 'DEP0085')
|
||||
});
|
||||
|
||||
Object.defineProperty(module.exports, 'emitDestroy', {
|
||||
get: internalUtil.deprecate(function() {
|
||||
return emitDestroy;
|
||||
}, 'async_hooks.emitDestroy is deprecated. ' +
|
||||
'Use AsyncResource.emitDestroy instead.', 'DEP0085')
|
||||
});
|
||||
|
@ -28,7 +28,7 @@ const dns = require('dns');
|
||||
const util = require('util');
|
||||
const { isUint8Array } = require('internal/util/types');
|
||||
const EventEmitter = require('events');
|
||||
const { setInitTriggerId } = require('async_hooks');
|
||||
const { setInitTriggerId } = require('internal/async_hooks');
|
||||
const { UV_UDP_REUSEADDR } = process.binding('constants').os;
|
||||
const { async_id_symbol } = process.binding('async_wrap');
|
||||
const { nextTick } = require('internal/process/next_tick');
|
||||
|
349
lib/internal/async_hooks.js
Normal file
349
lib/internal/async_hooks.js
Normal file
@ -0,0 +1,349 @@
|
||||
'use strict';
|
||||
|
||||
const errors = require('internal/errors');
|
||||
const async_wrap = process.binding('async_wrap');
|
||||
/* async_hook_fields is a Uint32Array wrapping the uint32_t array of
|
||||
* Environment::AsyncHooks::fields_[]. Each index tracks the number of active
|
||||
* hooks for each type.
|
||||
*
|
||||
* async_id_fields is a Float64Array wrapping the double array of
|
||||
* Environment::AsyncHooks::async_id_fields_[]. Each index contains the ids for
|
||||
* the various asynchronous states of the application. These are:
|
||||
* kExecutionAsyncId: The async_id assigned to the resource responsible for the
|
||||
* current execution stack.
|
||||
* kTriggerAsyncId: The trigger_async_id of the resource responsible for
|
||||
* the current execution stack.
|
||||
* kAsyncIdCounter: Incremental counter tracking the next assigned async_id.
|
||||
* kInitTriggerAsyncId: Written immediately before a resource's constructor
|
||||
* that sets the value of the init()'s triggerAsyncId. The order of
|
||||
* retrieving the triggerAsyncId value is passing directly to the
|
||||
* constructor -> value set in kInitTriggerAsyncId -> executionAsyncId of
|
||||
* the current resource.
|
||||
*/
|
||||
const { async_hook_fields, async_id_fields } = async_wrap;
|
||||
// Store the pair executionAsyncId and triggerAsyncId in a std::stack on
|
||||
// Environment::AsyncHooks::ids_stack_ tracks the resource responsible for the
|
||||
// current execution stack. This is unwound as each resource exits. In the case
|
||||
// of a fatal exception this stack is emptied after calling each hook's after()
|
||||
// callback.
|
||||
const { pushAsyncIds, popAsyncIds } = async_wrap;
|
||||
// For performance reasons, only track Proimses when a hook is enabled.
|
||||
const { enablePromiseHook, disablePromiseHook } = async_wrap;
|
||||
// Properties in active_hooks are used to keep track of the set of hooks being
|
||||
// executed in case another hook is enabled/disabled. The new set of hooks is
|
||||
// then restored once the active set of hooks is finished executing.
|
||||
const active_hooks = {
|
||||
// Array of all AsyncHooks that will be iterated whenever an async event
|
||||
// fires. Using var instead of (preferably const) in order to assign
|
||||
// active_hooks.tmp_array if a hook is enabled/disabled during hook
|
||||
// execution.
|
||||
array: [],
|
||||
// Use a counter to track nested calls of async hook callbacks and make sure
|
||||
// the active_hooks.array isn't altered mid execution.
|
||||
call_depth: 0,
|
||||
// Use to temporarily store and updated active_hooks.array if the user
|
||||
// enables or disables a hook while hooks are being processed. If a hook is
|
||||
// enabled() or disabled() during hook execution then the current set of
|
||||
// active hooks is duplicated and set equal to active_hooks.tmp_array. Any
|
||||
// subsequent changes are on the duplicated array. When all hooks have
|
||||
// completed executing active_hooks.tmp_array is assigned to
|
||||
// active_hooks.array.
|
||||
tmp_array: null,
|
||||
// Keep track of the field counts held in active_hooks.tmp_array. Because the
|
||||
// async_hook_fields can't be reassigned, store each uint32 in an array that
|
||||
// is written back to async_hook_fields when active_hooks.array is restored.
|
||||
tmp_fields: null
|
||||
};
|
||||
|
||||
|
||||
// Each constant tracks how many callbacks there are for any given step of
|
||||
// async execution. These are tracked so if the user didn't include callbacks
|
||||
// for a given step, that step can bail out early.
|
||||
const { kInit, kBefore, kAfter, kDestroy, kPromiseResolve,
|
||||
kCheck, kExecutionAsyncId, kAsyncIdCounter,
|
||||
kInitTriggerAsyncId } = async_wrap.constants;
|
||||
|
||||
// Used in AsyncHook and AsyncResource.
|
||||
const init_symbol = Symbol('init');
|
||||
const before_symbol = Symbol('before');
|
||||
const after_symbol = Symbol('after');
|
||||
const destroy_symbol = Symbol('destroy');
|
||||
const promise_resolve_symbol = Symbol('promiseResolve');
|
||||
const emitBeforeNative = emitHookFactory(before_symbol, 'emitBeforeNative');
|
||||
const emitAfterNative = emitHookFactory(after_symbol, 'emitAfterNative');
|
||||
const emitDestroyNative = emitHookFactory(destroy_symbol, 'emitDestroyNative');
|
||||
const emitPromiseResolveNative =
|
||||
emitHookFactory(promise_resolve_symbol, 'emitPromiseResolveNative');
|
||||
|
||||
// TODO(refack): move to node-config.cc
|
||||
const abort_regex = /^--abort[_-]on[_-]uncaught[_-]exception$/;
|
||||
|
||||
// Setup the callbacks that node::AsyncWrap will call when there are hooks to
|
||||
// process. They use the same functions as the JS embedder API. These callbacks
|
||||
// are setup immediately to prevent async_wrap.setupHooks() from being hijacked
|
||||
// and the cost of doing so is negligible.
|
||||
async_wrap.setupHooks({ init: emitInitNative,
|
||||
before: emitBeforeNative,
|
||||
after: emitAfterNative,
|
||||
destroy: emitDestroyNative,
|
||||
promise_resolve: emitPromiseResolveNative });
|
||||
|
||||
// Used to fatally abort the process if a callback throws.
|
||||
function fatalError(e) {
|
||||
if (typeof e.stack === 'string') {
|
||||
process._rawDebug(e.stack);
|
||||
} else {
|
||||
const o = { message: e };
|
||||
Error.captureStackTrace(o, fatalError);
|
||||
process._rawDebug(o.stack);
|
||||
}
|
||||
if (process.execArgv.some((e) => abort_regex.test(e))) {
|
||||
process.abort();
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
function validateAsyncId(asyncId, type) {
|
||||
// Skip validation when async_hooks is disabled
|
||||
if (async_hook_fields[kCheck] <= 0) return;
|
||||
|
||||
if (!Number.isSafeInteger(asyncId) || asyncId < -1) {
|
||||
fatalError(new errors.RangeError('ERR_INVALID_ASYNC_ID', type, asyncId));
|
||||
}
|
||||
}
|
||||
|
||||
// Emit From Native //
|
||||
|
||||
// Used by C++ to call all init() callbacks. Because some state can be setup
|
||||
// from C++ there's no need to perform all the same operations as in
|
||||
// emitInitScript.
|
||||
function emitInitNative(asyncId, type, triggerAsyncId, resource) {
|
||||
active_hooks.call_depth += 1;
|
||||
// Use a single try/catch for all hook to avoid setting up one per iteration.
|
||||
try {
|
||||
for (var i = 0; i < active_hooks.array.length; i++) {
|
||||
if (typeof active_hooks.array[i][init_symbol] === 'function') {
|
||||
active_hooks.array[i][init_symbol](
|
||||
asyncId, type, triggerAsyncId,
|
||||
resource
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
fatalError(e);
|
||||
} finally {
|
||||
active_hooks.call_depth -= 1;
|
||||
}
|
||||
|
||||
// Hooks can only be restored if there have been no recursive hook calls.
|
||||
// Also the active hooks do not need to be restored if enable()/disable()
|
||||
// weren't called during hook execution, in which case active_hooks.tmp_array
|
||||
// will be null.
|
||||
if (active_hooks.call_depth === 0 && active_hooks.tmp_array !== null) {
|
||||
restoreActiveHooks();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function emitHookFactory(symbol, name) {
|
||||
// Called from native. The asyncId stack handling is taken care of there
|
||||
// before this is called.
|
||||
// eslint-disable-next-line func-style
|
||||
const fn = function(asyncId) {
|
||||
active_hooks.call_depth += 1;
|
||||
// Use a single try/catch for all hook to avoid setting up one per
|
||||
// iteration.
|
||||
try {
|
||||
for (var i = 0; i < active_hooks.array.length; i++) {
|
||||
if (typeof active_hooks.array[i][symbol] === 'function') {
|
||||
active_hooks.array[i][symbol](asyncId);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
fatalError(e);
|
||||
} finally {
|
||||
active_hooks.call_depth -= 1;
|
||||
}
|
||||
|
||||
// Hooks can only be restored if there have been no recursive hook calls.
|
||||
// Also the active hooks do not need to be restored if enable()/disable()
|
||||
// weren't called during hook execution, in which case
|
||||
// active_hooks.tmp_array will be null.
|
||||
if (active_hooks.call_depth === 0 && active_hooks.tmp_array !== null) {
|
||||
restoreActiveHooks();
|
||||
}
|
||||
};
|
||||
|
||||
// Set the name property of the anonymous function as it looks good in the
|
||||
// stack trace.
|
||||
Object.defineProperty(fn, 'name', {
|
||||
value: name
|
||||
});
|
||||
return fn;
|
||||
}
|
||||
|
||||
// Manage Active Hooks //
|
||||
|
||||
function getHookArrays() {
|
||||
if (active_hooks.call_depth === 0)
|
||||
return [active_hooks.array, async_hook_fields];
|
||||
// If this hook is being enabled while in the middle of processing the array
|
||||
// of currently active hooks then duplicate the current set of active hooks
|
||||
// and store this there. This shouldn't fire until the next time hooks are
|
||||
// processed.
|
||||
if (active_hooks.tmp_array === null)
|
||||
storeActiveHooks();
|
||||
return [active_hooks.tmp_array, active_hooks.tmp_fields];
|
||||
}
|
||||
|
||||
|
||||
function storeActiveHooks() {
|
||||
active_hooks.tmp_array = active_hooks.array.slice();
|
||||
// Don't want to make the assumption that kInit to kDestroy are indexes 0 to
|
||||
// 4. So do this the long way.
|
||||
active_hooks.tmp_fields = [];
|
||||
active_hooks.tmp_fields[kInit] = async_hook_fields[kInit];
|
||||
active_hooks.tmp_fields[kBefore] = async_hook_fields[kBefore];
|
||||
active_hooks.tmp_fields[kAfter] = async_hook_fields[kAfter];
|
||||
active_hooks.tmp_fields[kDestroy] = async_hook_fields[kDestroy];
|
||||
active_hooks.tmp_fields[kPromiseResolve] = async_hook_fields[kPromiseResolve];
|
||||
}
|
||||
|
||||
|
||||
// Then restore the correct hooks array in case any hooks were added/removed
|
||||
// during hook callback execution.
|
||||
function restoreActiveHooks() {
|
||||
active_hooks.array = active_hooks.tmp_array;
|
||||
async_hook_fields[kInit] = active_hooks.tmp_fields[kInit];
|
||||
async_hook_fields[kBefore] = active_hooks.tmp_fields[kBefore];
|
||||
async_hook_fields[kAfter] = active_hooks.tmp_fields[kAfter];
|
||||
async_hook_fields[kDestroy] = active_hooks.tmp_fields[kDestroy];
|
||||
async_hook_fields[kPromiseResolve] = active_hooks.tmp_fields[kPromiseResolve];
|
||||
|
||||
active_hooks.tmp_array = null;
|
||||
active_hooks.tmp_fields = null;
|
||||
}
|
||||
|
||||
|
||||
function enableHooks() {
|
||||
enablePromiseHook();
|
||||
async_hook_fields[kCheck] += 1;
|
||||
}
|
||||
|
||||
function disableHooks() {
|
||||
disablePromiseHook();
|
||||
async_hook_fields[kCheck] -= 1;
|
||||
}
|
||||
|
||||
// Sensitive Embedder API //
|
||||
|
||||
// Increment the internal id counter and return the value. Important that the
|
||||
// counter increment first. Since it's done the same way in
|
||||
// Environment::new_async_uid()
|
||||
function newUid() {
|
||||
return ++async_id_fields[kAsyncIdCounter];
|
||||
}
|
||||
|
||||
|
||||
// Return the triggerAsyncId meant for the constructor calling it. It's up to
|
||||
// the user to safeguard this call and make sure it's zero'd out when the
|
||||
// constructor is complete.
|
||||
function initTriggerId() {
|
||||
var triggerAsyncId = async_id_fields[kInitTriggerAsyncId];
|
||||
// Reset value after it's been called so the next constructor doesn't
|
||||
// inherit it by accident.
|
||||
async_id_fields[kInitTriggerAsyncId] = 0;
|
||||
if (triggerAsyncId <= 0)
|
||||
triggerAsyncId = async_id_fields[kExecutionAsyncId];
|
||||
return triggerAsyncId;
|
||||
}
|
||||
|
||||
|
||||
function setInitTriggerId(triggerAsyncId) {
|
||||
// CHECK(Number.isSafeInteger(triggerAsyncId))
|
||||
// CHECK(triggerAsyncId > 0)
|
||||
async_id_fields[kInitTriggerAsyncId] = triggerAsyncId;
|
||||
}
|
||||
|
||||
|
||||
function emitInitScript(asyncId, type, triggerAsyncId, resource) {
|
||||
validateAsyncId(asyncId, 'asyncId');
|
||||
if (triggerAsyncId !== null)
|
||||
validateAsyncId(triggerAsyncId, 'triggerAsyncId');
|
||||
if (async_hook_fields[kCheck] > 0 &&
|
||||
(typeof type !== 'string' || type.length <= 0)) {
|
||||
throw new errors.TypeError('ERR_ASYNC_TYPE', type);
|
||||
}
|
||||
|
||||
// Short circuit all checks for the common case. Which is that no hooks have
|
||||
// been set. Do this to remove performance impact for embedders (and core).
|
||||
if (async_hook_fields[kInit] === 0)
|
||||
return;
|
||||
|
||||
// This can run after the early return check b/c running this function
|
||||
// manually means that the embedder must have used initTriggerId().
|
||||
if (triggerAsyncId === null) {
|
||||
triggerAsyncId = initTriggerId();
|
||||
} else {
|
||||
// If a triggerAsyncId was passed, any kInitTriggerAsyncId still must be
|
||||
// null'd.
|
||||
async_id_fields[kInitTriggerAsyncId] = 0;
|
||||
}
|
||||
|
||||
emitInitNative(asyncId, type, triggerAsyncId, resource);
|
||||
}
|
||||
|
||||
|
||||
function emitBeforeScript(asyncId, triggerAsyncId) {
|
||||
// Validate the ids. An id of -1 means it was never set and is visible on the
|
||||
// call graph. An id < -1 should never happen in any circumstance. Throw
|
||||
// on user calls because async state should still be recoverable.
|
||||
validateAsyncId(asyncId, 'asyncId');
|
||||
validateAsyncId(triggerAsyncId, 'triggerAsyncId');
|
||||
|
||||
pushAsyncIds(asyncId, triggerAsyncId);
|
||||
|
||||
if (async_hook_fields[kBefore] > 0)
|
||||
emitBeforeNative(asyncId);
|
||||
}
|
||||
|
||||
|
||||
function emitAfterScript(asyncId) {
|
||||
validateAsyncId(asyncId, 'asyncId');
|
||||
|
||||
if (async_hook_fields[kAfter] > 0)
|
||||
emitAfterNative(asyncId);
|
||||
|
||||
popAsyncIds(asyncId);
|
||||
}
|
||||
|
||||
|
||||
function emitDestroyScript(asyncId) {
|
||||
validateAsyncId(asyncId, 'asyncId');
|
||||
|
||||
// Return early if there are no destroy callbacks, or invalid asyncId.
|
||||
if (async_hook_fields[kDestroy] === 0 || asyncId <= 0)
|
||||
return;
|
||||
async_wrap.queueDestroyAsyncId(asyncId);
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
// Private API
|
||||
getHookArrays,
|
||||
symbols: {
|
||||
init_symbol, before_symbol, after_symbol, destroy_symbol,
|
||||
promise_resolve_symbol
|
||||
},
|
||||
enableHooks,
|
||||
disableHooks,
|
||||
// Sensitive Embedder API
|
||||
newUid,
|
||||
initTriggerId,
|
||||
setInitTriggerId,
|
||||
emitInit: emitInitScript,
|
||||
emitBefore: emitBeforeScript,
|
||||
emitAfter: emitAfterScript,
|
||||
emitDestroy: emitDestroyScript,
|
||||
};
|
2
lib/internal/bootstrap_node.js
vendored
2
lib/internal/bootstrap_node.js
vendored
@ -401,7 +401,7 @@
|
||||
// Emit the after() hooks now that the exception has been handled.
|
||||
if (async_hook_fields[kAfter] > 0) {
|
||||
do {
|
||||
NativeModule.require('async_hooks').emitAfter(
|
||||
NativeModule.require('internal/async_hooks').emitAfter(
|
||||
async_id_fields[kExecutionAsyncId]);
|
||||
} while (asyncIdStackSize() > 0);
|
||||
// Or completely empty the id stack.
|
||||
|
@ -48,7 +48,7 @@ class NextTickQueue {
|
||||
|
||||
function setupNextTick() {
|
||||
const async_wrap = process.binding('async_wrap');
|
||||
const async_hooks = require('async_hooks');
|
||||
const async_hooks = require('internal/async_hooks');
|
||||
const promises = require('internal/process/promises');
|
||||
const errors = require('internal/errors');
|
||||
const emitPendingUnhandledRejections = promises.setup(scheduleMicrotasks);
|
||||
|
@ -43,7 +43,7 @@ const { TCPConnectWrap } = process.binding('tcp_wrap');
|
||||
const { PipeConnectWrap } = process.binding('pipe_wrap');
|
||||
const { ShutdownWrap, WriteWrap } = process.binding('stream_wrap');
|
||||
const { async_id_symbol } = process.binding('async_wrap');
|
||||
const { newUid, setInitTriggerId } = require('async_hooks');
|
||||
const { newUid, setInitTriggerId } = require('internal/async_hooks');
|
||||
const { nextTick } = require('internal/process/next_tick');
|
||||
const errors = require('internal/errors');
|
||||
const dns = require('dns');
|
||||
|
@ -40,7 +40,7 @@ const {
|
||||
emitBefore,
|
||||
emitAfter,
|
||||
emitDestroy
|
||||
} = require('async_hooks');
|
||||
} = require('internal/async_hooks');
|
||||
// Grab the constants necessary for working with internal arrays.
|
||||
const { kInit, kDestroy, kAsyncIdCounter } = async_wrap.constants;
|
||||
// Symbols for storing async id state.
|
||||
|
1
node.gyp
1
node.gyp
@ -76,6 +76,7 @@
|
||||
'lib/v8.js',
|
||||
'lib/vm.js',
|
||||
'lib/zlib.js',
|
||||
'lib/internal/async_hooks.js',
|
||||
'lib/internal/buffer.js',
|
||||
'lib/internal/child_process.js',
|
||||
'lib/internal/cluster/child.js',
|
||||
|
@ -11,35 +11,22 @@ switch (arg) {
|
||||
initHooks({
|
||||
oninit: common.mustCall(() => { throw new Error(arg); })
|
||||
}).enable();
|
||||
async_hooks.emitInit(
|
||||
async_hooks.newUid(),
|
||||
`${arg}_type`,
|
||||
async_hooks.executionAsyncId()
|
||||
);
|
||||
new async_hooks.AsyncResource(`${arg}_type`);
|
||||
return;
|
||||
|
||||
case 'test_callback':
|
||||
initHooks({
|
||||
onbefore: common.mustCall(() => { throw new Error(arg); })
|
||||
}).enable();
|
||||
const newAsyncId = async_hooks.newUid();
|
||||
async_hooks.emitInit(
|
||||
newAsyncId,
|
||||
`${arg}_type`,
|
||||
async_hooks.executionAsyncId()
|
||||
);
|
||||
async_hooks.emitBefore(newAsyncId, async_hooks.executionAsyncId());
|
||||
const resource = new async_hooks.AsyncResource(`${arg}_type`);
|
||||
resource.emitBefore();
|
||||
return;
|
||||
|
||||
case 'test_callback_abort':
|
||||
initHooks({
|
||||
oninit: common.mustCall(() => { throw new Error(arg); })
|
||||
}).enable();
|
||||
async_hooks.emitInit(
|
||||
async_hooks.newUid(),
|
||||
`${arg}_type`,
|
||||
async_hooks.executionAsyncId()
|
||||
);
|
||||
new async_hooks.AsyncResource(`${arg}_type`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
'use strict';
|
||||
// Flags: --expose-internals
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const spawnSync = require('child_process').spawnSync;
|
||||
const async_hooks = require('async_hooks');
|
||||
const async_hooks = require('internal/async_hooks');
|
||||
const initHooks = require('./init-hooks');
|
||||
|
||||
switch (process.argv[2]) {
|
||||
@ -17,13 +18,17 @@ switch (process.argv[2]) {
|
||||
assert.ok(!process.argv[2]);
|
||||
|
||||
|
||||
const c1 = spawnSync(process.execPath, [__filename, 'test_invalid_async_id']);
|
||||
const c1 = spawnSync(process.execPath, [
|
||||
'--expose-internals', __filename, 'test_invalid_async_id'
|
||||
]);
|
||||
assert.strictEqual(
|
||||
c1.stderr.toString().split(/[\r\n]+/g)[0],
|
||||
'RangeError [ERR_INVALID_ASYNC_ID]: Invalid asyncId value: -2');
|
||||
assert.strictEqual(c1.status, 1);
|
||||
|
||||
const c2 = spawnSync(process.execPath, [__filename, 'test_invalid_trigger_id']);
|
||||
const c2 = spawnSync(process.execPath, [
|
||||
'--expose-internals', __filename, 'test_invalid_trigger_id'
|
||||
]);
|
||||
assert.strictEqual(
|
||||
c2.stderr.toString().split(/[\r\n]+/g)[0],
|
||||
'RangeError [ERR_INVALID_ASYNC_ID]: Invalid triggerAsyncId value: -2');
|
||||
|
@ -1,9 +1,10 @@
|
||||
'use strict';
|
||||
// Flags: --expose-internals
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const spawnSync = require('child_process').spawnSync;
|
||||
const async_hooks = require('async_hooks');
|
||||
const async_hooks = require('internal/async_hooks');
|
||||
const initHooks = require('./init-hooks');
|
||||
|
||||
const expectedId = async_hooks.newUid();
|
||||
@ -36,20 +37,24 @@ switch (process.argv[2]) {
|
||||
assert.ok(!process.argv[2]);
|
||||
|
||||
|
||||
const c1 = spawnSync(process.execPath, [__filename, 'test_invalid_async_id']);
|
||||
const c1 = spawnSync(process.execPath, [
|
||||
'--expose-internals', __filename, 'test_invalid_async_id'
|
||||
]);
|
||||
assert.strictEqual(
|
||||
c1.stderr.toString().split(/[\r\n]+/g)[0],
|
||||
'RangeError [ERR_INVALID_ASYNC_ID]: Invalid asyncId value: undefined');
|
||||
assert.strictEqual(c1.status, 1);
|
||||
|
||||
const c2 = spawnSync(process.execPath, [__filename, 'test_invalid_trigger_id']);
|
||||
const c2 = spawnSync(process.execPath, [
|
||||
'--expose-internals', __filename, 'test_invalid_trigger_id'
|
||||
]);
|
||||
assert.strictEqual(
|
||||
c2.stderr.toString().split(/[\r\n]+/g)[0],
|
||||
'RangeError [ERR_INVALID_ASYNC_ID]: Invalid triggerAsyncId value: undefined');
|
||||
assert.strictEqual(c2.status, 1);
|
||||
|
||||
const c3 = spawnSync(process.execPath, [
|
||||
__filename, 'test_invalid_trigger_id_negative'
|
||||
'--expose-internals', __filename, 'test_invalid_trigger_id_negative'
|
||||
]);
|
||||
assert.strictEqual(
|
||||
c3.stderr.toString().split(/[\r\n]+/g)[0],
|
||||
|
@ -4,7 +4,7 @@ const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const async_hooks = require('async_hooks');
|
||||
|
||||
const asyncId = async_hooks.newUid();
|
||||
const asyncId = new async_hooks.AsyncResource('test').asyncId();
|
||||
|
||||
assert.notStrictEqual(async_hooks.executionAsyncId(), asyncId);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
'use strict';
|
||||
// Flags: --expose-internals
|
||||
|
||||
// Make sure http.request() can catch immediate errors in
|
||||
// net.createConnection().
|
||||
@ -9,7 +10,7 @@ const net = require('net');
|
||||
const http = require('http');
|
||||
const uv = process.binding('uv');
|
||||
const { async_id_symbol } = process.binding('async_wrap');
|
||||
const { newUid } = require('async_hooks');
|
||||
const { newUid } = require('internal/async_hooks');
|
||||
|
||||
const agent = new http.Agent();
|
||||
agent.createConnection = common.mustCall((cfg) => {
|
||||
|
Loading…
Reference in New Issue
Block a user