Skip to content

Commit

Permalink
async_hooks: use new v8::Context PromiseHook API
Browse files Browse the repository at this point in the history
PR-URL: nodejs#36394
Backport-PR-URL: nodejs#38577
Reviewed-By: Bryan English <bryan@bryanenglish.com>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com>
Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
Qard authored and foxxyz committed Oct 18, 2021
1 parent 44570a1 commit bbdd8c6
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 48 deletions.
96 changes: 48 additions & 48 deletions lib/internal/async_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const {
ErrorCaptureStackTrace,
ObjectPrototypeHasOwnProperty,
ObjectDefineProperty,
Promise,
Symbol,
} = primordials;

Expand Down Expand Up @@ -53,7 +52,7 @@ const {
clearAsyncIdStack,
} = async_wrap;
// For performance reasons, only track Promises when a hook is enabled.
const { enablePromiseHook, disablePromiseHook } = async_wrap;
const { enablePromiseHook, disablePromiseHook, setPromiseHooks } = 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.
Expand Down Expand Up @@ -303,71 +302,68 @@ function restoreActiveHooks() {
active_hooks.tmp_fields = null;
}

function trackPromise(promise, parent, silent) {
const asyncId = getOrSetAsyncId(promise);
function trackPromise(promise, parent) {
if (promise[async_id_symbol]) {
return;
}

promise[async_id_symbol] = newAsyncId();
promise[trigger_async_id_symbol] = parent ? getOrSetAsyncId(parent) :
getDefaultTriggerAsyncId();
}

if (!silent && initHooksExist()) {
const triggerId = promise[trigger_async_id_symbol];
emitInitScript(asyncId, 'PROMISE', triggerId, promise);
}
function promiseInitHook(promise, parent) {
trackPromise(promise, parent);
const asyncId = promise[async_id_symbol];
const triggerAsyncId = promise[trigger_async_id_symbol];
emitInitScript(asyncId, 'PROMISE', triggerAsyncId, promise);
}

function fastPromiseHook(type, promise, parent) {
if (type === kInit || !promise[async_id_symbol]) {
const silent = type !== kInit;
if (parent instanceof Promise) {
trackPromise(promise, parent, silent);
} else {
trackPromise(promise, null, silent);
}
function promiseBeforeHook(promise) {
trackPromise(promise);
const asyncId = promise[async_id_symbol];
const triggerId = promise[trigger_async_id_symbol];
emitBeforeScript(asyncId, triggerId, promise);
}

if (!silent) return;
function promiseAfterHook(promise) {
trackPromise(promise);
const asyncId = promise[async_id_symbol];
if (hasHooks(kAfter)) {
emitAfterNative(asyncId);
}
if (asyncId === executionAsyncId()) {
// This condition might not be true if async_hooks was enabled during
// the promise callback execution.
// Popping it off the stack can be skipped in that case, because it is
// known that it would correspond to exactly one call with
// PromiseHookType::kBefore that was not witnessed by the PromiseHook.
popAsyncContext(asyncId);
}
}

function promiseResolveHook(promise) {
trackPromise(promise);
const asyncId = promise[async_id_symbol];
switch (type) {
case kBefore:
const triggerId = promise[trigger_async_id_symbol];
emitBeforeScript(asyncId, triggerId, promise);
break;
case kAfter:
if (hasHooks(kAfter)) {
emitAfterNative(asyncId);
}
if (asyncId === executionAsyncId()) {
// This condition might not be true if async_hooks was enabled during
// the promise callback execution.
// Popping it off the stack can be skipped in that case, because it is
// known that it would correspond to exactly one call with
// PromiseHookType::kBefore that was not witnessed by the PromiseHook.
popAsyncContext(asyncId);
}
break;
case kPromiseResolve:
emitPromiseResolveNative(asyncId);
break;
}
emitPromiseResolveNative(asyncId);
}

let wantPromiseHook = false;
function enableHooks() {
async_hook_fields[kCheck] += 1;
}

let promiseHookMode = -1;
function updatePromiseHookMode() {
wantPromiseHook = true;
if (destroyHooksExist()) {
if (promiseHookMode !== 1) {
promiseHookMode = 1;
enablePromiseHook();
}
} else if (promiseHookMode !== 0) {
promiseHookMode = 0;
enablePromiseHook(fastPromiseHook);
enablePromiseHook();
} else {
setPromiseHooks(
initHooksExist() ? promiseInitHook : undefined,
promiseBeforeHook,
promiseAfterHook,
promiseResolveHooksExist() ? promiseResolveHook : undefined,
);
}
}

Expand All @@ -383,8 +379,8 @@ function disableHooks() {

function disablePromiseHookIfNecessary() {
if (!wantPromiseHook) {
promiseHookMode = -1;
disablePromiseHook();
setPromiseHooks(undefined, undefined, undefined, undefined);
}
}

Expand Down Expand Up @@ -458,6 +454,10 @@ function destroyHooksExist() {
return hasHooks(kDestroy);
}

function promiseResolveHooksExist() {
return hasHooks(kPromiseResolve);
}


function emitInitScript(asyncId, type, triggerAsyncId, resource) {
// Short circuit all checks for the common case. Which is that no hooks have
Expand Down
10 changes: 10 additions & 0 deletions src/async_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,15 @@ static void EnablePromiseHook(const FunctionCallbackInfo<Value>& args) {
}
}

static void SetPromiseHooks(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Context> ctx = env->context();
ctx->SetPromiseHooks(
args[0]->IsFunction() ? args[0].As<Function>() : Local<Function>(),
args[1]->IsFunction() ? args[1].As<Function>() : Local<Function>(),
args[2]->IsFunction() ? args[2].As<Function>() : Local<Function>(),
args[3]->IsFunction() ? args[3].As<Function>() : Local<Function>());
}

static void DisablePromiseHook(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Expand Down Expand Up @@ -670,6 +679,7 @@ void AsyncWrap::Initialize(Local<Object> target,
env->SetMethod(target, "clearAsyncIdStack", ClearAsyncIdStack);
env->SetMethod(target, "queueDestroyAsyncId", QueueDestroyAsyncId);
env->SetMethod(target, "enablePromiseHook", EnablePromiseHook);
env->SetMethod(target, "setPromiseHooks", SetPromiseHooks);
env->SetMethod(target, "disablePromiseHook", DisablePromiseHook);
env->SetMethod(target, "registerDestroyHook", RegisterDestroyHook);

Expand Down

0 comments on commit bbdd8c6

Please sign in to comment.