-
-
Notifications
You must be signed in to change notification settings - Fork 33.4k
async_hooks: use typed array stack as fast path #17780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,14 +19,20 @@ const async_wrap = process.binding('async_wrap'); | |
* retrieving the triggerAsyncId value is passing directly to the | ||
* constructor -> value set in kDefaultTriggerAsyncId -> executionAsyncId of | ||
* the current resource. | ||
* | ||
* async_ids_fast_stack is a Float64Array that contains part of the async ID | ||
* stack. Each pushAsyncIds() call adds two doubles to it, and each | ||
* popAsyncIds() call removes two doubles from it. | ||
* It has a fixed size, so if that is exceeded, calls to the native | ||
* side are used instead in pushAsyncIds() and popAsyncIds(). | ||
*/ | ||
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; | ||
const { pushAsyncIds: pushAsyncIds_, popAsyncIds: 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 | ||
|
@@ -60,8 +66,8 @@ const active_hooks = { | |
// 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, | ||
kDefaultTriggerAsyncId } = async_wrap.constants; | ||
kCheck, kExecutionAsyncId, kAsyncIdCounter, kTriggerAsyncId, | ||
kDefaultTriggerAsyncId, kStackLength } = async_wrap.constants; | ||
|
||
// Used in AsyncHook and AsyncResource. | ||
const init_symbol = Symbol('init'); | ||
|
@@ -332,6 +338,38 @@ function emitDestroyScript(asyncId) { | |
} | ||
|
||
|
||
// This is the equivalent of the native push_async_ids() call. | ||
function pushAsyncIds(asyncId, triggerAsyncId) { | ||
const offset = async_hook_fields[kStackLength]; | ||
if (offset * 2 >= async_wrap.async_ids_stack.length) | ||
return pushAsyncIds_(asyncId, triggerAsyncId); | ||
async_wrap.async_ids_stack[offset * 2] = async_id_fields[kExecutionAsyncId]; | ||
async_wrap.async_ids_stack[offset * 2 + 1] = async_id_fields[kTriggerAsyncId]; | ||
async_hook_fields[kStackLength]++; | ||
async_id_fields[kExecutionAsyncId] = asyncId; | ||
async_id_fields[kTriggerAsyncId] = triggerAsyncId; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd take a slightly different approach here. Growing the stack can be done in JS: allocate a new typed array, copy over the elements, then call into C++ to update the AliasedBuffer's reference. That probably also obviates the need to overload If you wrote it this way because of how PromiseHook() calls There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bnoordhuis It’s not just |
||
|
||
|
||
// This is the equivalent of the native pop_async_ids() call. | ||
function popAsyncIds(asyncId) { | ||
if (async_hook_fields[kStackLength] === 0) return false; | ||
const stackLength = async_hook_fields[kStackLength]; | ||
|
||
if (async_hook_fields[kCheck] > 0 && | ||
async_id_fields[kExecutionAsyncId] !== asyncId) { | ||
// Do the same thing as the native code (i.e. crash hard). | ||
return popAsyncIds_(asyncId); | ||
} | ||
|
||
const offset = stackLength - 1; | ||
async_id_fields[kExecutionAsyncId] = async_wrap.async_ids_stack[2 * offset]; | ||
async_id_fields[kTriggerAsyncId] = async_wrap.async_ids_stack[2 * offset + 1]; | ||
async_hook_fields[kStackLength] = offset; | ||
return offset > 0; | ||
} | ||
|
||
|
||
module.exports = { | ||
// Private API | ||
getHookArrays, | ||
|
Uh oh!
There was an error while loading. Please reload this page.