mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
async_hooks: add executionAsyncResource
Remove the need for the destroy hook in the basic APM case. Co-authored-by: Stephen Belanger <admin@stephenbelanger.com> PR-URL: https://github.com/nodejs/node/pull/30959 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
1c11ea4388
commit
9fdb6e6aaf
151
benchmark/async_hooks/async-resource-vs-destroy.js
Normal file
151
benchmark/async_hooks/async-resource-vs-destroy.js
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { promisify } = require('util');
|
||||||
|
const { readFile } = require('fs');
|
||||||
|
const sleep = promisify(setTimeout);
|
||||||
|
const read = promisify(readFile);
|
||||||
|
const common = require('../common.js');
|
||||||
|
const {
|
||||||
|
createHook,
|
||||||
|
executionAsyncResource,
|
||||||
|
executionAsyncId
|
||||||
|
} = require('async_hooks');
|
||||||
|
const { createServer } = require('http');
|
||||||
|
|
||||||
|
// Configuration for the http server
|
||||||
|
// there is no need for parameters in this test
|
||||||
|
const connections = 500;
|
||||||
|
const path = '/';
|
||||||
|
|
||||||
|
const bench = common.createBenchmark(main, {
|
||||||
|
type: ['async-resource', 'destroy'],
|
||||||
|
asyncMethod: ['callbacks', 'async'],
|
||||||
|
n: [1e6]
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildCurrentResource(getServe) {
|
||||||
|
const server = createServer(getServe(getCLS, setCLS));
|
||||||
|
const hook = createHook({ init });
|
||||||
|
const cls = Symbol('cls');
|
||||||
|
hook.enable();
|
||||||
|
|
||||||
|
return {
|
||||||
|
server,
|
||||||
|
close
|
||||||
|
};
|
||||||
|
|
||||||
|
function getCLS() {
|
||||||
|
const resource = executionAsyncResource();
|
||||||
|
if (resource === null || !resource[cls]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return resource[cls].state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCLS(state) {
|
||||||
|
const resource = executionAsyncResource();
|
||||||
|
if (resource === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!resource[cls]) {
|
||||||
|
resource[cls] = { state };
|
||||||
|
} else {
|
||||||
|
resource[cls].state = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(asyncId, type, triggerAsyncId, resource) {
|
||||||
|
var cr = executionAsyncResource();
|
||||||
|
if (cr !== null) {
|
||||||
|
resource[cls] = cr[cls];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
hook.disable();
|
||||||
|
server.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDestroy(getServe) {
|
||||||
|
const transactions = new Map();
|
||||||
|
const server = createServer(getServe(getCLS, setCLS));
|
||||||
|
const hook = createHook({ init, destroy });
|
||||||
|
hook.enable();
|
||||||
|
|
||||||
|
return {
|
||||||
|
server,
|
||||||
|
close
|
||||||
|
};
|
||||||
|
|
||||||
|
function getCLS() {
|
||||||
|
const asyncId = executionAsyncId();
|
||||||
|
return transactions.has(asyncId) ? transactions.get(asyncId) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCLS(value) {
|
||||||
|
const asyncId = executionAsyncId();
|
||||||
|
transactions.set(asyncId, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(asyncId, type, triggerAsyncId, resource) {
|
||||||
|
transactions.set(asyncId, getCLS());
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroy(asyncId) {
|
||||||
|
transactions.delete(asyncId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
hook.disable();
|
||||||
|
server.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServeAwait(getCLS, setCLS) {
|
||||||
|
return async function serve(req, res) {
|
||||||
|
setCLS(Math.random());
|
||||||
|
await sleep(10);
|
||||||
|
await read(__filename);
|
||||||
|
res.setHeader('content-type', 'application/json');
|
||||||
|
res.end(JSON.stringify({ cls: getCLS() }));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServeCallbacks(getCLS, setCLS) {
|
||||||
|
return function serve(req, res) {
|
||||||
|
setCLS(Math.random());
|
||||||
|
setTimeout(() => {
|
||||||
|
readFile(__filename, () => {
|
||||||
|
res.setHeader('content-type', 'application/json');
|
||||||
|
res.end(JSON.stringify({ cls: getCLS() }));
|
||||||
|
});
|
||||||
|
}, 10);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const types = {
|
||||||
|
'async-resource': buildCurrentResource,
|
||||||
|
'destroy': buildDestroy
|
||||||
|
};
|
||||||
|
|
||||||
|
const asyncMethods = {
|
||||||
|
'callbacks': getServeCallbacks,
|
||||||
|
'async': getServeAwait
|
||||||
|
};
|
||||||
|
|
||||||
|
function main({ type, asyncMethod }) {
|
||||||
|
const { server, close } = types[type](asyncMethods[asyncMethod]);
|
||||||
|
|
||||||
|
server
|
||||||
|
.listen(common.PORT)
|
||||||
|
.on('listening', () => {
|
||||||
|
|
||||||
|
bench.http({
|
||||||
|
path,
|
||||||
|
connections
|
||||||
|
}, () => {
|
||||||
|
close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -459,6 +459,62 @@ init for PROMISE with id 6, trigger id: 5 # the Promise returned by then()
|
||||||
after 6
|
after 6
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `async_hooks.executionAsyncResource()`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Returns: {Object} The resource representing the current execution.
|
||||||
|
Useful to store data within the resource.
|
||||||
|
|
||||||
|
Resource objects returned by `executionAsyncResource()` are most often internal
|
||||||
|
Node.js handle objects with undocumented APIs. Using any functions or properties
|
||||||
|
on the object is likely to crash your application and should be avoided.
|
||||||
|
|
||||||
|
Using `executionAsyncResource()` in the top-level execution context will
|
||||||
|
return an empty object as there is no handle or request object to use,
|
||||||
|
but having an object representing the top-level can be helpful.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { open } = require('fs');
|
||||||
|
const { executionAsyncId, executionAsyncResource } = require('async_hooks');
|
||||||
|
|
||||||
|
console.log(executionAsyncId(), executionAsyncResource()); // 1 {}
|
||||||
|
open(__filename, 'r', (err, fd) => {
|
||||||
|
console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This can be used to implement continuation local storage without the
|
||||||
|
use of a tracking `Map` to store the metadata:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { createServer } = require('http');
|
||||||
|
const {
|
||||||
|
executionAsyncId,
|
||||||
|
executionAsyncResource,
|
||||||
|
createHook
|
||||||
|
} = require('async_hooks');
|
||||||
|
const sym = Symbol('state'); // Private symbol to avoid pollution
|
||||||
|
|
||||||
|
createHook({
|
||||||
|
init(asyncId, type, triggerAsyncId, resource) {
|
||||||
|
const cr = executionAsyncResource();
|
||||||
|
if (cr) {
|
||||||
|
resource[sym] = cr[sym];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).enable();
|
||||||
|
|
||||||
|
const server = createServer(function(req, res) {
|
||||||
|
executionAsyncResource()[sym] = { state: req.url };
|
||||||
|
setTimeout(function() {
|
||||||
|
res.end(JSON.stringify(executionAsyncResource()[sym]));
|
||||||
|
}, 100);
|
||||||
|
}).listen(3000);
|
||||||
|
```
|
||||||
|
|
||||||
#### `async_hooks.executionAsyncId()`
|
#### `async_hooks.executionAsyncId()`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ const {
|
||||||
getHookArrays,
|
getHookArrays,
|
||||||
enableHooks,
|
enableHooks,
|
||||||
disableHooks,
|
disableHooks,
|
||||||
|
executionAsyncResource,
|
||||||
// Internal Embedder API
|
// Internal Embedder API
|
||||||
newAsyncId,
|
newAsyncId,
|
||||||
getDefaultTriggerAsyncId,
|
getDefaultTriggerAsyncId,
|
||||||
|
|
@ -176,7 +177,7 @@ class AsyncResource {
|
||||||
|
|
||||||
runInAsyncScope(fn, thisArg, ...args) {
|
runInAsyncScope(fn, thisArg, ...args) {
|
||||||
const asyncId = this[async_id_symbol];
|
const asyncId = this[async_id_symbol];
|
||||||
emitBefore(asyncId, this[trigger_async_id_symbol]);
|
emitBefore(asyncId, this[trigger_async_id_symbol], this);
|
||||||
|
|
||||||
const ret = thisArg === undefined ?
|
const ret = thisArg === undefined ?
|
||||||
fn(...args) :
|
fn(...args) :
|
||||||
|
|
@ -211,6 +212,7 @@ module.exports = {
|
||||||
createHook,
|
createHook,
|
||||||
executionAsyncId,
|
executionAsyncId,
|
||||||
triggerAsyncId,
|
triggerAsyncId,
|
||||||
|
executionAsyncResource,
|
||||||
// Embedder API
|
// Embedder API
|
||||||
AsyncResource,
|
AsyncResource,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,26 @@ const async_wrap = internalBinding('async_wrap');
|
||||||
* 3. executionAsyncId of the current resource.
|
* 3. executionAsyncId of the current resource.
|
||||||
*
|
*
|
||||||
* async_ids_stack is a Float64Array that contains part of the async ID
|
* async_ids_stack is a Float64Array that contains part of the async ID
|
||||||
* stack. Each pushAsyncIds() call adds two doubles to it, and each
|
* stack. Each pushAsyncContext() call adds two doubles to it, and each
|
||||||
* popAsyncIds() call removes two doubles from it.
|
* popAsyncContext() call removes two doubles from it.
|
||||||
* It has a fixed size, so if that is exceeded, calls to the native
|
* It has a fixed size, so if that is exceeded, calls to the native
|
||||||
* side are used instead in pushAsyncIds() and popAsyncIds().
|
* side are used instead in pushAsyncContext() and popAsyncContext().
|
||||||
*/
|
*/
|
||||||
const { async_hook_fields, async_id_fields, owner_symbol } = async_wrap;
|
const {
|
||||||
|
async_hook_fields,
|
||||||
|
async_id_fields,
|
||||||
|
execution_async_resources,
|
||||||
|
owner_symbol
|
||||||
|
} = async_wrap;
|
||||||
// Store the pair executionAsyncId and triggerAsyncId in a std::stack on
|
// Store the pair executionAsyncId and triggerAsyncId in a std::stack on
|
||||||
// Environment::AsyncHooks::async_ids_stack_ tracks the resource responsible for
|
// Environment::AsyncHooks::async_ids_stack_ tracks the resource responsible for
|
||||||
// the current execution stack. This is unwound as each resource exits. In the
|
// 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
|
// case of a fatal exception this stack is emptied after calling each hook's
|
||||||
// after() callback.
|
// after() callback.
|
||||||
const { pushAsyncIds: pushAsyncIds_, popAsyncIds: popAsyncIds_ } = async_wrap;
|
const {
|
||||||
|
pushAsyncContext: pushAsyncContext_,
|
||||||
|
popAsyncContext: popAsyncContext_
|
||||||
|
} = async_wrap;
|
||||||
// For performance reasons, only track Promises when a hook is enabled.
|
// For performance reasons, only track Promises when a hook is enabled.
|
||||||
const { enablePromiseHook, disablePromiseHook } = async_wrap;
|
const { enablePromiseHook, disablePromiseHook } = async_wrap;
|
||||||
// Properties in active_hooks are used to keep track of the set of hooks being
|
// Properties in active_hooks are used to keep track of the set of hooks being
|
||||||
|
|
@ -92,6 +100,15 @@ const emitDestroyNative = emitHookFactory(destroy_symbol, 'emitDestroyNative');
|
||||||
const emitPromiseResolveNative =
|
const emitPromiseResolveNative =
|
||||||
emitHookFactory(promise_resolve_symbol, 'emitPromiseResolveNative');
|
emitHookFactory(promise_resolve_symbol, 'emitPromiseResolveNative');
|
||||||
|
|
||||||
|
const topLevelResource = {};
|
||||||
|
|
||||||
|
function executionAsyncResource() {
|
||||||
|
const index = async_hook_fields[kStackLength] - 1;
|
||||||
|
if (index === -1) return topLevelResource;
|
||||||
|
const resource = execution_async_resources[index];
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
// Used to fatally abort the process if a callback throws.
|
// Used to fatally abort the process if a callback throws.
|
||||||
function fatalError(e) {
|
function fatalError(e) {
|
||||||
if (typeof e.stack === 'string') {
|
if (typeof e.stack === 'string') {
|
||||||
|
|
@ -330,8 +347,8 @@ function emitInitScript(asyncId, type, triggerAsyncId, resource) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function emitBeforeScript(asyncId, triggerAsyncId) {
|
function emitBeforeScript(asyncId, triggerAsyncId, resource) {
|
||||||
pushAsyncIds(asyncId, triggerAsyncId);
|
pushAsyncContext(asyncId, triggerAsyncId, resource);
|
||||||
|
|
||||||
if (async_hook_fields[kBefore] > 0)
|
if (async_hook_fields[kBefore] > 0)
|
||||||
emitBeforeNative(asyncId);
|
emitBeforeNative(asyncId);
|
||||||
|
|
@ -342,7 +359,7 @@ function emitAfterScript(asyncId) {
|
||||||
if (async_hook_fields[kAfter] > 0)
|
if (async_hook_fields[kAfter] > 0)
|
||||||
emitAfterNative(asyncId);
|
emitAfterNative(asyncId);
|
||||||
|
|
||||||
popAsyncIds(asyncId);
|
popAsyncContext(asyncId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -360,6 +377,7 @@ function clearAsyncIdStack() {
|
||||||
async_id_fields[kExecutionAsyncId] = 0;
|
async_id_fields[kExecutionAsyncId] = 0;
|
||||||
async_id_fields[kTriggerAsyncId] = 0;
|
async_id_fields[kTriggerAsyncId] = 0;
|
||||||
async_hook_fields[kStackLength] = 0;
|
async_hook_fields[kStackLength] = 0;
|
||||||
|
execution_async_resources.splice(0, execution_async_resources.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -369,12 +387,13 @@ function hasAsyncIdStack() {
|
||||||
|
|
||||||
|
|
||||||
// This is the equivalent of the native push_async_ids() call.
|
// This is the equivalent of the native push_async_ids() call.
|
||||||
function pushAsyncIds(asyncId, triggerAsyncId) {
|
function pushAsyncContext(asyncId, triggerAsyncId, resource) {
|
||||||
const offset = async_hook_fields[kStackLength];
|
const offset = async_hook_fields[kStackLength];
|
||||||
if (offset * 2 >= async_wrap.async_ids_stack.length)
|
if (offset * 2 >= async_wrap.async_ids_stack.length)
|
||||||
return pushAsyncIds_(asyncId, triggerAsyncId);
|
return pushAsyncContext_(asyncId, triggerAsyncId, resource);
|
||||||
async_wrap.async_ids_stack[offset * 2] = async_id_fields[kExecutionAsyncId];
|
async_wrap.async_ids_stack[offset * 2] = async_id_fields[kExecutionAsyncId];
|
||||||
async_wrap.async_ids_stack[offset * 2 + 1] = async_id_fields[kTriggerAsyncId];
|
async_wrap.async_ids_stack[offset * 2 + 1] = async_id_fields[kTriggerAsyncId];
|
||||||
|
execution_async_resources[offset] = resource;
|
||||||
async_hook_fields[kStackLength]++;
|
async_hook_fields[kStackLength]++;
|
||||||
async_id_fields[kExecutionAsyncId] = asyncId;
|
async_id_fields[kExecutionAsyncId] = asyncId;
|
||||||
async_id_fields[kTriggerAsyncId] = triggerAsyncId;
|
async_id_fields[kTriggerAsyncId] = triggerAsyncId;
|
||||||
|
|
@ -382,18 +401,19 @@ function pushAsyncIds(asyncId, triggerAsyncId) {
|
||||||
|
|
||||||
|
|
||||||
// This is the equivalent of the native pop_async_ids() call.
|
// This is the equivalent of the native pop_async_ids() call.
|
||||||
function popAsyncIds(asyncId) {
|
function popAsyncContext(asyncId) {
|
||||||
const stackLength = async_hook_fields[kStackLength];
|
const stackLength = async_hook_fields[kStackLength];
|
||||||
if (stackLength === 0) return false;
|
if (stackLength === 0) return false;
|
||||||
|
|
||||||
if (enabledHooksExist() && async_id_fields[kExecutionAsyncId] !== asyncId) {
|
if (enabledHooksExist() && async_id_fields[kExecutionAsyncId] !== asyncId) {
|
||||||
// Do the same thing as the native code (i.e. crash hard).
|
// Do the same thing as the native code (i.e. crash hard).
|
||||||
return popAsyncIds_(asyncId);
|
return popAsyncContext_(asyncId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const offset = stackLength - 1;
|
const offset = stackLength - 1;
|
||||||
async_id_fields[kExecutionAsyncId] = async_wrap.async_ids_stack[2 * offset];
|
async_id_fields[kExecutionAsyncId] = async_wrap.async_ids_stack[2 * offset];
|
||||||
async_id_fields[kTriggerAsyncId] = async_wrap.async_ids_stack[2 * offset + 1];
|
async_id_fields[kTriggerAsyncId] = async_wrap.async_ids_stack[2 * offset + 1];
|
||||||
|
execution_async_resources.pop();
|
||||||
async_hook_fields[kStackLength] = offset;
|
async_hook_fields[kStackLength] = offset;
|
||||||
return offset > 0;
|
return offset > 0;
|
||||||
}
|
}
|
||||||
|
|
@ -426,6 +446,7 @@ module.exports = {
|
||||||
clearDefaultTriggerAsyncId,
|
clearDefaultTriggerAsyncId,
|
||||||
clearAsyncIdStack,
|
clearAsyncIdStack,
|
||||||
hasAsyncIdStack,
|
hasAsyncIdStack,
|
||||||
|
executionAsyncResource,
|
||||||
// Internal Embedder API
|
// Internal Embedder API
|
||||||
newAsyncId,
|
newAsyncId,
|
||||||
getOrSetAsyncId,
|
getOrSetAsyncId,
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ function processTicksAndRejections() {
|
||||||
do {
|
do {
|
||||||
while (tock = queue.shift()) {
|
while (tock = queue.shift()) {
|
||||||
const asyncId = tock[async_id_symbol];
|
const asyncId = tock[async_id_symbol];
|
||||||
emitBefore(asyncId, tock[trigger_async_id_symbol]);
|
emitBefore(asyncId, tock[trigger_async_id_symbol], tock);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const callback = tock.callback;
|
const callback = tock.callback;
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ const {
|
||||||
emitInit,
|
emitInit,
|
||||||
emitBefore,
|
emitBefore,
|
||||||
emitAfter,
|
emitAfter,
|
||||||
emitDestroy
|
emitDestroy,
|
||||||
} = require('internal/async_hooks');
|
} = require('internal/async_hooks');
|
||||||
|
|
||||||
// Symbols for storing async id state.
|
// Symbols for storing async id state.
|
||||||
|
|
@ -448,7 +448,7 @@ function getTimerCallbacks(runNextTicks) {
|
||||||
prevImmediate = immediate;
|
prevImmediate = immediate;
|
||||||
|
|
||||||
const asyncId = immediate[async_id_symbol];
|
const asyncId = immediate[async_id_symbol];
|
||||||
emitBefore(asyncId, immediate[trigger_async_id_symbol]);
|
emitBefore(asyncId, immediate[trigger_async_id_symbol], immediate);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const argv = immediate._argv;
|
const argv = immediate._argv;
|
||||||
|
|
@ -537,7 +537,7 @@ function getTimerCallbacks(runNextTicks) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
emitBefore(asyncId, timer[trigger_async_id_symbol]);
|
emitBefore(asyncId, timer[trigger_async_id_symbol], timer);
|
||||||
|
|
||||||
let start;
|
let start;
|
||||||
if (timer._repeat)
|
if (timer._repeat)
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ CallbackScope::~CallbackScope() {
|
||||||
|
|
||||||
InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap, int flags)
|
InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap, int flags)
|
||||||
: InternalCallbackScope(async_wrap->env(),
|
: InternalCallbackScope(async_wrap->env(),
|
||||||
async_wrap->object(),
|
async_wrap->GetResource(),
|
||||||
{ async_wrap->get_async_id(),
|
{ async_wrap->get_async_id(),
|
||||||
async_wrap->get_trigger_async_id() },
|
async_wrap->get_trigger_async_id() },
|
||||||
flags) {}
|
flags) {}
|
||||||
|
|
@ -50,7 +50,6 @@ InternalCallbackScope::InternalCallbackScope(Environment* env,
|
||||||
object_(object),
|
object_(object),
|
||||||
skip_hooks_(flags & kSkipAsyncHooks),
|
skip_hooks_(flags & kSkipAsyncHooks),
|
||||||
skip_task_queues_(flags & kSkipTaskQueues) {
|
skip_task_queues_(flags & kSkipTaskQueues) {
|
||||||
CHECK_IMPLIES(!(flags & kAllowEmptyResource), !object.IsEmpty());
|
|
||||||
CHECK_NOT_NULL(env);
|
CHECK_NOT_NULL(env);
|
||||||
env->PushAsyncCallbackScope();
|
env->PushAsyncCallbackScope();
|
||||||
|
|
||||||
|
|
@ -69,8 +68,9 @@ InternalCallbackScope::InternalCallbackScope(Environment* env,
|
||||||
AsyncWrap::EmitBefore(env, asyncContext.async_id);
|
AsyncWrap::EmitBefore(env, asyncContext.async_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
env->async_hooks()->push_async_ids(async_context_.async_id,
|
env->async_hooks()->push_async_context(async_context_.async_id,
|
||||||
async_context_.trigger_async_id);
|
async_context_.trigger_async_id, object);
|
||||||
|
|
||||||
pushed_ids_ = true;
|
pushed_ids_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ void InternalCallbackScope::Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pushed_ids_)
|
if (pushed_ids_)
|
||||||
env_->async_hooks()->pop_async_id(async_context_.async_id);
|
env_->async_hooks()->pop_async_context(async_context_.async_id);
|
||||||
|
|
||||||
if (failed_) return;
|
if (failed_) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -260,8 +260,8 @@ static void PromiseHook(PromiseHookType type, Local<Promise> promise,
|
||||||
if (wrap == nullptr) return;
|
if (wrap == nullptr) return;
|
||||||
|
|
||||||
if (type == PromiseHookType::kBefore) {
|
if (type == PromiseHookType::kBefore) {
|
||||||
env->async_hooks()->push_async_ids(
|
env->async_hooks()->push_async_context(wrap->get_async_id(),
|
||||||
wrap->get_async_id(), wrap->get_trigger_async_id());
|
wrap->get_trigger_async_id(), wrap->object());
|
||||||
wrap->EmitTraceEventBefore();
|
wrap->EmitTraceEventBefore();
|
||||||
AsyncWrap::EmitBefore(wrap->env(), wrap->get_async_id());
|
AsyncWrap::EmitBefore(wrap->env(), wrap->get_async_id());
|
||||||
} else if (type == PromiseHookType::kAfter) {
|
} else if (type == PromiseHookType::kAfter) {
|
||||||
|
|
@ -273,7 +273,7 @@ static void PromiseHook(PromiseHookType type, Local<Promise> promise,
|
||||||
// Popping it off the stack can be skipped in that case, because it is
|
// Popping it off the stack can be skipped in that case, because it is
|
||||||
// known that it would correspond to exactly one call with
|
// known that it would correspond to exactly one call with
|
||||||
// PromiseHookType::kBefore that was not witnessed by the PromiseHook.
|
// PromiseHookType::kBefore that was not witnessed by the PromiseHook.
|
||||||
env->async_hooks()->pop_async_id(wrap->get_async_id());
|
env->async_hooks()->pop_async_context(wrap->get_async_id());
|
||||||
}
|
}
|
||||||
} else if (type == PromiseHookType::kResolve) {
|
} else if (type == PromiseHookType::kResolve) {
|
||||||
AsyncWrap::EmitPromiseResolve(wrap->env(), wrap->get_async_id());
|
AsyncWrap::EmitPromiseResolve(wrap->env(), wrap->get_async_id());
|
||||||
|
|
@ -382,7 +382,6 @@ static void RegisterDestroyHook(const FunctionCallbackInfo<Value>& args) {
|
||||||
p->target.SetWeak(p, AsyncWrap::WeakCallback, WeakCallbackType::kParameter);
|
p->target.SetWeak(p, AsyncWrap::WeakCallback, WeakCallbackType::kParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AsyncWrap::GetAsyncId(const FunctionCallbackInfo<Value>& args) {
|
void AsyncWrap::GetAsyncId(const FunctionCallbackInfo<Value>& args) {
|
||||||
AsyncWrap* wrap;
|
AsyncWrap* wrap;
|
||||||
args.GetReturnValue().Set(kInvalidAsyncId);
|
args.GetReturnValue().Set(kInvalidAsyncId);
|
||||||
|
|
@ -391,20 +390,20 @@ void AsyncWrap::GetAsyncId(const FunctionCallbackInfo<Value>& args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AsyncWrap::PushAsyncIds(const FunctionCallbackInfo<Value>& args) {
|
void AsyncWrap::PushAsyncContext(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
// No need for CHECK(IsNumber()) on args because if FromJust() doesn't fail
|
// No need for CHECK(IsNumber()) on args because if FromJust() doesn't fail
|
||||||
// then the checks in push_async_ids() and pop_async_id() will.
|
// then the checks in push_async_ids() and pop_async_id() will.
|
||||||
double async_id = args[0]->NumberValue(env->context()).FromJust();
|
double async_id = args[0]->NumberValue(env->context()).FromJust();
|
||||||
double trigger_async_id = args[1]->NumberValue(env->context()).FromJust();
|
double trigger_async_id = args[1]->NumberValue(env->context()).FromJust();
|
||||||
env->async_hooks()->push_async_ids(async_id, trigger_async_id);
|
env->async_hooks()->push_async_context(async_id, trigger_async_id, args[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AsyncWrap::PopAsyncIds(const FunctionCallbackInfo<Value>& args) {
|
void AsyncWrap::PopAsyncContext(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
double async_id = args[0]->NumberValue(env->context()).FromJust();
|
double async_id = args[0]->NumberValue(env->context()).FromJust();
|
||||||
args.GetReturnValue().Set(env->async_hooks()->pop_async_id(async_id));
|
args.GetReturnValue().Set(env->async_hooks()->pop_async_context(async_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -433,6 +432,7 @@ void AsyncWrap::EmitDestroy() {
|
||||||
AsyncWrap::EmitDestroy(env(), async_id_);
|
AsyncWrap::EmitDestroy(env(), async_id_);
|
||||||
// Ensure no double destroy is emitted via AsyncReset().
|
// Ensure no double destroy is emitted via AsyncReset().
|
||||||
async_id_ = kInvalidAsyncId;
|
async_id_ = kInvalidAsyncId;
|
||||||
|
resource_.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWrap::QueueDestroyAsyncId(const FunctionCallbackInfo<Value>& args) {
|
void AsyncWrap::QueueDestroyAsyncId(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
|
@ -464,8 +464,8 @@ void AsyncWrap::Initialize(Local<Object> target,
|
||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
|
|
||||||
env->SetMethod(target, "setupHooks", SetupHooks);
|
env->SetMethod(target, "setupHooks", SetupHooks);
|
||||||
env->SetMethod(target, "pushAsyncIds", PushAsyncIds);
|
env->SetMethod(target, "pushAsyncContext", PushAsyncContext);
|
||||||
env->SetMethod(target, "popAsyncIds", PopAsyncIds);
|
env->SetMethod(target, "popAsyncContext", PopAsyncContext);
|
||||||
env->SetMethod(target, "queueDestroyAsyncId", QueueDestroyAsyncId);
|
env->SetMethod(target, "queueDestroyAsyncId", QueueDestroyAsyncId);
|
||||||
env->SetMethod(target, "enablePromiseHook", EnablePromiseHook);
|
env->SetMethod(target, "enablePromiseHook", EnablePromiseHook);
|
||||||
env->SetMethod(target, "disablePromiseHook", DisablePromiseHook);
|
env->SetMethod(target, "disablePromiseHook", DisablePromiseHook);
|
||||||
|
|
@ -502,6 +502,10 @@ void AsyncWrap::Initialize(Local<Object> target,
|
||||||
"async_id_fields",
|
"async_id_fields",
|
||||||
env->async_hooks()->async_id_fields().GetJSArray());
|
env->async_hooks()->async_id_fields().GetJSArray());
|
||||||
|
|
||||||
|
FORCE_SET_TARGET_FIELD(target,
|
||||||
|
"execution_async_resources",
|
||||||
|
env->async_hooks()->execution_async_resources());
|
||||||
|
|
||||||
target->Set(context,
|
target->Set(context,
|
||||||
env->async_ids_stack_string(),
|
env->async_ids_stack_string(),
|
||||||
env->async_hooks()->async_ids_stack().GetJSArray()).Check();
|
env->async_hooks()->async_ids_stack().GetJSArray()).Check();
|
||||||
|
|
@ -670,6 +674,12 @@ void AsyncWrap::AsyncReset(Local<Object> resource, double execution_async_id,
|
||||||
: execution_async_id;
|
: execution_async_id;
|
||||||
trigger_async_id_ = env()->get_default_trigger_async_id();
|
trigger_async_id_ = env()->get_default_trigger_async_id();
|
||||||
|
|
||||||
|
if (resource != object()) {
|
||||||
|
// TODO(addaleax): Using a strong reference here makes it very easy to
|
||||||
|
// introduce memory leaks. Move away from using a strong reference.
|
||||||
|
resource_.Reset(env()->isolate(), resource);
|
||||||
|
}
|
||||||
|
|
||||||
switch (provider_type()) {
|
switch (provider_type()) {
|
||||||
#define V(PROVIDER) \
|
#define V(PROVIDER) \
|
||||||
case PROVIDER_ ## PROVIDER: \
|
case PROVIDER_ ## PROVIDER: \
|
||||||
|
|
@ -776,6 +786,14 @@ Local<Object> AsyncWrap::GetOwner(Environment* env, Local<Object> obj) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Local<Object> AsyncWrap::GetResource() {
|
||||||
|
if (resource_.IsEmpty()) {
|
||||||
|
return object();
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource_.Get(env()->isolate());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
NODE_MODULE_CONTEXT_AWARE_INTERNAL(async_wrap, node::AsyncWrap::Initialize)
|
NODE_MODULE_CONTEXT_AWARE_INTERNAL(async_wrap, node::AsyncWrap::Initialize)
|
||||||
|
|
|
||||||
|
|
@ -134,8 +134,8 @@ class AsyncWrap : public BaseObject {
|
||||||
void* priv);
|
void* priv);
|
||||||
|
|
||||||
static void GetAsyncId(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void GetAsyncId(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void PushAsyncIds(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void PushAsyncContext(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void PopAsyncIds(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void PopAsyncContext(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void AsyncReset(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void AsyncReset(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void GetProviderType(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void GetProviderType(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void QueueDestroyAsyncId(
|
static void QueueDestroyAsyncId(
|
||||||
|
|
@ -202,6 +202,7 @@ class AsyncWrap : public BaseObject {
|
||||||
v8::Local<v8::Object> obj);
|
v8::Local<v8::Object> obj);
|
||||||
|
|
||||||
bool IsDoneInitializing() const override;
|
bool IsDoneInitializing() const override;
|
||||||
|
v8::Local<v8::Object> GetResource();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class PromiseWrap;
|
friend class PromiseWrap;
|
||||||
|
|
@ -216,6 +217,7 @@ class AsyncWrap : public BaseObject {
|
||||||
// Because the values may be Reset(), cannot be made const.
|
// Because the values may be Reset(), cannot be made const.
|
||||||
double async_id_ = kInvalidAsyncId;
|
double async_id_ = kInvalidAsyncId;
|
||||||
double trigger_async_id_;
|
double trigger_async_id_;
|
||||||
|
v8::Global<v8::Object> resource_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ inline AsyncHooks::AsyncHooks()
|
||||||
: async_ids_stack_(env()->isolate(), 16 * 2),
|
: async_ids_stack_(env()->isolate(), 16 * 2),
|
||||||
fields_(env()->isolate(), kFieldsCount),
|
fields_(env()->isolate(), kFieldsCount),
|
||||||
async_id_fields_(env()->isolate(), kUidFieldsCount) {
|
async_id_fields_(env()->isolate(), kUidFieldsCount) {
|
||||||
v8::HandleScope handle_scope(env()->isolate());
|
clear_async_id_stack();
|
||||||
|
|
||||||
// Always perform async_hooks checks, not just when async_hooks is enabled.
|
// Always perform async_hooks checks, not just when async_hooks is enabled.
|
||||||
// TODO(AndreasMadsen): Consider removing this for LTS releases.
|
// TODO(AndreasMadsen): Consider removing this for LTS releases.
|
||||||
|
|
@ -113,6 +113,10 @@ inline AliasedFloat64Array& AsyncHooks::async_ids_stack() {
|
||||||
return async_ids_stack_;
|
return async_ids_stack_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline v8::Local<v8::Array> AsyncHooks::execution_async_resources() {
|
||||||
|
return PersistentToLocal::Strong(execution_async_resources_);
|
||||||
|
}
|
||||||
|
|
||||||
inline v8::Local<v8::String> AsyncHooks::provider_string(int idx) {
|
inline v8::Local<v8::String> AsyncHooks::provider_string(int idx) {
|
||||||
return providers_[idx].Get(env()->isolate());
|
return providers_[idx].Get(env()->isolate());
|
||||||
}
|
}
|
||||||
|
|
@ -125,9 +129,12 @@ inline Environment* AsyncHooks::env() {
|
||||||
return Environment::ForAsyncHooks(this);
|
return Environment::ForAsyncHooks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember to keep this code aligned with pushAsyncIds() in JS.
|
// Remember to keep this code aligned with pushAsyncContext() in JS.
|
||||||
inline void AsyncHooks::push_async_ids(double async_id,
|
inline void AsyncHooks::push_async_context(double async_id,
|
||||||
double trigger_async_id) {
|
double trigger_async_id,
|
||||||
|
v8::Local<v8::Value> resource) {
|
||||||
|
v8::HandleScope handle_scope(env()->isolate());
|
||||||
|
|
||||||
// Since async_hooks is experimental, do only perform the check
|
// Since async_hooks is experimental, do only perform the check
|
||||||
// when async_hooks is enabled.
|
// when async_hooks is enabled.
|
||||||
if (fields_[kCheck] > 0) {
|
if (fields_[kCheck] > 0) {
|
||||||
|
|
@ -143,10 +150,13 @@ inline void AsyncHooks::push_async_ids(double async_id,
|
||||||
fields_[kStackLength] += 1;
|
fields_[kStackLength] += 1;
|
||||||
async_id_fields_[kExecutionAsyncId] = async_id;
|
async_id_fields_[kExecutionAsyncId] = async_id;
|
||||||
async_id_fields_[kTriggerAsyncId] = trigger_async_id;
|
async_id_fields_[kTriggerAsyncId] = trigger_async_id;
|
||||||
|
|
||||||
|
auto resources = execution_async_resources();
|
||||||
|
USE(resources->Set(env()->context(), offset, resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember to keep this code aligned with popAsyncIds() in JS.
|
// Remember to keep this code aligned with popAsyncContext() in JS.
|
||||||
inline bool AsyncHooks::pop_async_id(double async_id) {
|
inline bool AsyncHooks::pop_async_context(double async_id) {
|
||||||
// In case of an exception then this may have already been reset, if the
|
// In case of an exception then this may have already been reset, if the
|
||||||
// stack was multiple MakeCallback()'s deep.
|
// stack was multiple MakeCallback()'s deep.
|
||||||
if (fields_[kStackLength] == 0) return false;
|
if (fields_[kStackLength] == 0) return false;
|
||||||
|
|
@ -175,11 +185,18 @@ inline bool AsyncHooks::pop_async_id(double async_id) {
|
||||||
async_id_fields_[kTriggerAsyncId] = async_ids_stack_[2 * offset + 1];
|
async_id_fields_[kTriggerAsyncId] = async_ids_stack_[2 * offset + 1];
|
||||||
fields_[kStackLength] = offset;
|
fields_[kStackLength] = offset;
|
||||||
|
|
||||||
|
auto resources = execution_async_resources();
|
||||||
|
USE(resources->Delete(env()->context(), offset));
|
||||||
|
|
||||||
return fields_[kStackLength] > 0;
|
return fields_[kStackLength] > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep in sync with clearAsyncIdStack in lib/internal/async_hooks.js.
|
// Keep in sync with clearAsyncIdStack in lib/internal/async_hooks.js.
|
||||||
inline void AsyncHooks::clear_async_id_stack() {
|
inline void AsyncHooks::clear_async_id_stack() {
|
||||||
|
auto isolate = env()->isolate();
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
execution_async_resources_.Reset(isolate, v8::Array::New(isolate));
|
||||||
|
|
||||||
async_id_fields_[kExecutionAsyncId] = 0;
|
async_id_fields_[kExecutionAsyncId] = 0;
|
||||||
async_id_fields_[kTriggerAsyncId] = 0;
|
async_id_fields_[kTriggerAsyncId] = 0;
|
||||||
fields_[kStackLength] = 0;
|
fields_[kStackLength] = 0;
|
||||||
|
|
@ -206,7 +223,6 @@ inline AsyncHooks::DefaultTriggerAsyncIdScope ::~DefaultTriggerAsyncIdScope() {
|
||||||
old_default_trigger_async_id_;
|
old_default_trigger_async_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Environment* Environment::ForAsyncHooks(AsyncHooks* hooks) {
|
Environment* Environment::ForAsyncHooks(AsyncHooks* hooks) {
|
||||||
return ContainerOf(&Environment::async_hooks_, hooks);
|
return ContainerOf(&Environment::async_hooks_, hooks);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -655,14 +655,16 @@ class AsyncHooks : public MemoryRetainer {
|
||||||
inline AliasedUint32Array& fields();
|
inline AliasedUint32Array& fields();
|
||||||
inline AliasedFloat64Array& async_id_fields();
|
inline AliasedFloat64Array& async_id_fields();
|
||||||
inline AliasedFloat64Array& async_ids_stack();
|
inline AliasedFloat64Array& async_ids_stack();
|
||||||
|
inline v8::Local<v8::Array> execution_async_resources();
|
||||||
|
|
||||||
inline v8::Local<v8::String> provider_string(int idx);
|
inline v8::Local<v8::String> provider_string(int idx);
|
||||||
|
|
||||||
inline void no_force_checks();
|
inline void no_force_checks();
|
||||||
inline Environment* env();
|
inline Environment* env();
|
||||||
|
|
||||||
inline void push_async_ids(double async_id, double trigger_async_id);
|
inline void push_async_context(double async_id, double trigger_async_id,
|
||||||
inline bool pop_async_id(double async_id);
|
v8::Local<v8::Value> execution_async_resource_);
|
||||||
|
inline bool pop_async_context(double async_id);
|
||||||
inline void clear_async_id_stack(); // Used in fatal exceptions.
|
inline void clear_async_id_stack(); // Used in fatal exceptions.
|
||||||
|
|
||||||
AsyncHooks(const AsyncHooks&) = delete;
|
AsyncHooks(const AsyncHooks&) = delete;
|
||||||
|
|
@ -707,6 +709,8 @@ class AsyncHooks : public MemoryRetainer {
|
||||||
AliasedFloat64Array async_id_fields_;
|
AliasedFloat64Array async_id_fields_;
|
||||||
|
|
||||||
void grow_async_ids_stack();
|
void grow_async_ids_stack();
|
||||||
|
|
||||||
|
v8::Global<v8::Array> execution_async_resources_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImmediateInfo : public MemoryRetainer {
|
class ImmediateInfo : public MemoryRetainer {
|
||||||
|
|
|
||||||
|
|
@ -208,15 +208,13 @@ class InternalCallbackScope {
|
||||||
public:
|
public:
|
||||||
enum Flags {
|
enum Flags {
|
||||||
kNoFlags = 0,
|
kNoFlags = 0,
|
||||||
// Tell the constructor whether its `object` parameter may be empty or not.
|
|
||||||
kAllowEmptyResource = 1,
|
|
||||||
// Indicates whether 'before' and 'after' hooks should be skipped.
|
// Indicates whether 'before' and 'after' hooks should be skipped.
|
||||||
kSkipAsyncHooks = 2,
|
kSkipAsyncHooks = 1,
|
||||||
// Indicates whether nextTick and microtask queues should be skipped.
|
// Indicates whether nextTick and microtask queues should be skipped.
|
||||||
// This should only be used when there is no call into JS in this scope.
|
// This should only be used when there is no call into JS in this scope.
|
||||||
// (The HTTP parser also uses it for some weird backwards
|
// (The HTTP parser also uses it for some weird backwards
|
||||||
// compatibility issues, but it shouldn't.)
|
// compatibility issues, but it shouldn't.)
|
||||||
kSkipTaskQueues = 4
|
kSkipTaskQueues = 2
|
||||||
};
|
};
|
||||||
InternalCallbackScope(Environment* env,
|
InternalCallbackScope(Environment* env,
|
||||||
v8::Local<v8::Object> object,
|
v8::Local<v8::Object> object,
|
||||||
|
|
|
||||||
|
|
@ -121,9 +121,8 @@ int NodeMainInstance::Run() {
|
||||||
{
|
{
|
||||||
InternalCallbackScope callback_scope(
|
InternalCallbackScope callback_scope(
|
||||||
env.get(),
|
env.get(),
|
||||||
Local<Object>(),
|
Object::New(isolate_),
|
||||||
{ 1, 0 },
|
{ 1, 0 },
|
||||||
InternalCallbackScope::kAllowEmptyResource |
|
|
||||||
InternalCallbackScope::kSkipAsyncHooks);
|
InternalCallbackScope::kSkipAsyncHooks);
|
||||||
LoadEnvironment(env.get());
|
LoadEnvironment(env.get());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::Local;
|
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
using v8::Platform;
|
using v8::Platform;
|
||||||
using v8::Task;
|
using v8::Task;
|
||||||
|
|
@ -388,8 +387,9 @@ void PerIsolatePlatformData::RunForegroundTask(std::unique_ptr<Task> task) {
|
||||||
DebugSealHandleScope scope(isolate);
|
DebugSealHandleScope scope(isolate);
|
||||||
Environment* env = Environment::GetCurrent(isolate);
|
Environment* env = Environment::GetCurrent(isolate);
|
||||||
if (env != nullptr) {
|
if (env != nullptr) {
|
||||||
InternalCallbackScope cb_scope(env, Local<Object>(), { 0, 0 },
|
v8::HandleScope scope(isolate);
|
||||||
InternalCallbackScope::kAllowEmptyResource);
|
InternalCallbackScope cb_scope(env, Object::New(isolate), { 0, 0 },
|
||||||
|
InternalCallbackScope::kNoFlags);
|
||||||
task->Run();
|
task->Run();
|
||||||
} else {
|
} else {
|
||||||
task->Run();
|
task->Run();
|
||||||
|
|
|
||||||
|
|
@ -326,9 +326,8 @@ void Worker::Run() {
|
||||||
HandleScope handle_scope(isolate_);
|
HandleScope handle_scope(isolate_);
|
||||||
InternalCallbackScope callback_scope(
|
InternalCallbackScope callback_scope(
|
||||||
env_.get(),
|
env_.get(),
|
||||||
Local<Object>(),
|
Object::New(isolate_),
|
||||||
{ 1, 0 },
|
{ 1, 0 },
|
||||||
InternalCallbackScope::kAllowEmptyResource |
|
|
||||||
InternalCallbackScope::kSkipAsyncHooks);
|
InternalCallbackScope::kSkipAsyncHooks);
|
||||||
|
|
||||||
if (!env_->RunBootstrapping().IsEmpty()) {
|
if (!env_->RunBootstrapping().IsEmpty()) {
|
||||||
|
|
|
||||||
30
test/async-hooks/test-async-exec-resource-http.js
Normal file
30
test/async-hooks/test-async-exec-resource-http.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const {
|
||||||
|
executionAsyncResource,
|
||||||
|
executionAsyncId,
|
||||||
|
createHook,
|
||||||
|
} = require('async_hooks');
|
||||||
|
const http = require('http');
|
||||||
|
|
||||||
|
const hooked = {};
|
||||||
|
createHook({
|
||||||
|
init(asyncId, type, triggerAsyncId, resource) {
|
||||||
|
hooked[asyncId] = resource;
|
||||||
|
}
|
||||||
|
}).enable();
|
||||||
|
|
||||||
|
const server = http.createServer((req, res) => {
|
||||||
|
res.end('ok');
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(0, () => {
|
||||||
|
assert.strictEqual(executionAsyncResource(), hooked[executionAsyncId()]);
|
||||||
|
|
||||||
|
http.get({ port: server.address().port }, () => {
|
||||||
|
assert.strictEqual(executionAsyncResource(), hooked[executionAsyncId()]);
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -15,6 +15,8 @@ runBenchmark('async_hooks',
|
||||||
'asyncHooks=all',
|
'asyncHooks=all',
|
||||||
'connections=50',
|
'connections=50',
|
||||||
'method=trackingDisabled',
|
'method=trackingDisabled',
|
||||||
'n=10'
|
'n=10',
|
||||||
|
'type=async-resource',
|
||||||
|
'asyncMethod=async'
|
||||||
],
|
],
|
||||||
{});
|
{});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const sleep = require('util').promisify(setTimeout);
|
||||||
|
const assert = require('assert');
|
||||||
|
const { executionAsyncResource, createHook } = require('async_hooks');
|
||||||
|
const { createServer, get } = require('http');
|
||||||
|
const sym = Symbol('cls');
|
||||||
|
|
||||||
|
// Tests continuation local storage with the currentResource API
|
||||||
|
// through an async function
|
||||||
|
|
||||||
|
assert.ok(executionAsyncResource());
|
||||||
|
|
||||||
|
createHook({
|
||||||
|
init(asyncId, type, triggerAsyncId, resource) {
|
||||||
|
const cr = executionAsyncResource();
|
||||||
|
resource[sym] = cr[sym];
|
||||||
|
}
|
||||||
|
}).enable();
|
||||||
|
|
||||||
|
async function handler(req, res) {
|
||||||
|
executionAsyncResource()[sym] = { state: req.url };
|
||||||
|
await sleep(10);
|
||||||
|
const { state } = executionAsyncResource()[sym];
|
||||||
|
res.setHeader('content-type', 'application/json');
|
||||||
|
res.end(JSON.stringify({ state }));
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = createServer(function(req, res) {
|
||||||
|
handler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
function test(n) {
|
||||||
|
get(`http://localhost:${server.address().port}/${n}`, common.mustCall(function(res) {
|
||||||
|
res.setEncoding('utf8');
|
||||||
|
|
||||||
|
let body = '';
|
||||||
|
res.on('data', function(chunk) {
|
||||||
|
body += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', common.mustCall(function() {
|
||||||
|
assert.deepStrictEqual(JSON.parse(body), { state: `/${n}` });
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(function() {
|
||||||
|
server.unref();
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
test(i);
|
||||||
|
}
|
||||||
|
}));
|
||||||
49
test/parallel/test-async-hooks-execution-async-resource.js
Normal file
49
test/parallel/test-async-hooks-execution-async-resource.js
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const { executionAsyncResource, createHook } = require('async_hooks');
|
||||||
|
const { createServer, get } = require('http');
|
||||||
|
const sym = Symbol('cls');
|
||||||
|
|
||||||
|
// Tests continuation local storage with the executionAsyncResource API
|
||||||
|
|
||||||
|
assert.ok(executionAsyncResource());
|
||||||
|
|
||||||
|
createHook({
|
||||||
|
init(asyncId, type, triggerAsyncId, resource) {
|
||||||
|
const cr = executionAsyncResource();
|
||||||
|
resource[sym] = cr[sym];
|
||||||
|
}
|
||||||
|
}).enable();
|
||||||
|
|
||||||
|
const server = createServer(function(req, res) {
|
||||||
|
executionAsyncResource()[sym] = { state: req.url };
|
||||||
|
setTimeout(function() {
|
||||||
|
const { state } = executionAsyncResource()[sym];
|
||||||
|
res.setHeader('content-type', 'application/json');
|
||||||
|
res.end(JSON.stringify({ state }));
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
function test(n) {
|
||||||
|
get(`http://localhost:${server.address().port}/${n}`, common.mustCall(function(res) {
|
||||||
|
res.setEncoding('utf8');
|
||||||
|
|
||||||
|
let body = '';
|
||||||
|
res.on('data', function(chunk) {
|
||||||
|
body += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', common.mustCall(function() {
|
||||||
|
assert.deepStrictEqual(JSON.parse(body), { state: `/${n}` });
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(function() {
|
||||||
|
server.unref();
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
test(i);
|
||||||
|
}
|
||||||
|
}));
|
||||||
Loading…
Reference in New Issue
Block a user