From 47880193232eb718c98428df2109a7677af3a9c1 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Wed, 18 Dec 2019 17:08:56 -0800 Subject: [PATCH] Convert execution async resource to use a stack --- lib/async_hooks.js | 2 +- lib/internal/async_hooks.js | 11 ++++++----- lib/internal/timers.js | 6 ++++-- src/async_wrap.cc | 30 ++++++++++++++++++++++-------- src/env-inl.h | 21 +++++++++++++-------- src/env.h | 7 ++++--- 6 files changed, 50 insertions(+), 27 deletions(-) diff --git a/lib/async_hooks.js b/lib/async_hooks.js index 6bdb4033acf047..bc30b555e0f246 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -171,7 +171,7 @@ class AsyncResource { runInAsyncScope(fn, thisArg, ...args) { 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 ? fn(...args) : diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 208306e7c136b7..31f81158b27a71 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -76,7 +76,8 @@ const active_hooks = { const { registerDestroyHook } = async_wrap; const { enqueueMicrotask } = internalBinding('task_queue'); -const { executionAsyncResource, setExecutionAsyncResource } = async_wrap; +const { executionAsyncResource, pushExecutionAsyncResource, + popExecutionAsyncResource } = 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 @@ -360,7 +361,7 @@ function emitBeforeScript(asyncId, triggerAsyncId, resource) { validateAsyncId(asyncId, 'asyncId'); validateAsyncId(triggerAsyncId, 'triggerAsyncId'); - setExecutionAsyncResource(resource); + pushExecutionAsyncResource(resource); pushAsyncIds(asyncId, triggerAsyncId); if (async_hook_fields[kBefore] > 0) @@ -374,8 +375,7 @@ function emitAfterScript(asyncId) { if (async_hook_fields[kAfter] > 0) emitAfterNative(asyncId); - setExecutionAsyncResource(null); - + popExecutionAsyncResource(); popAsyncIds(asyncId); } @@ -477,7 +477,8 @@ module.exports = { emitAfter: emitAfterScript, emitDestroy: emitDestroyScript, registerDestroyHook, - setExecutionAsyncResource, + pushExecutionAsyncResource, + popExecutionAsyncResource, nativeHooks: { init: emitInitNative, before: emitBeforeNative, diff --git a/lib/internal/timers.js b/lib/internal/timers.js index 9cad9460efdc42..8cd42964d63251 100644 --- a/lib/internal/timers.js +++ b/lib/internal/timers.js @@ -97,7 +97,8 @@ const { emitBefore, emitAfter, emitDestroy, - setExecutionAsyncResource + popExecutionAsyncResource, + pushExecutionAsyncResource, } = require('internal/async_hooks'); // Symbols for storing async id state. @@ -539,7 +540,7 @@ function getTimerCallbacks(runNextTicks) { else timer._onTimeout(...args); } finally { - setExecutionAsyncResource(null); + popExecutionAsyncResource(); if (timer._repeat && timer._idleTimeout !== -1) { timer._idleTimeout = timer._repeat; if (start === undefined) @@ -555,6 +556,7 @@ function getTimerCallbacks(runNextTicks) { } timer._destroyed = true; } + pushExecutionAsyncResource(timer); } emitAfter(asyncId); diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 242c5440869e6b..d3fb8a1940703c 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -148,7 +148,7 @@ void AsyncWrap::EmitTraceEventBefore() { void AsyncWrap::EmitBefore(Environment* env, double async_id, v8::Local resource) { v8::Local context = env->isolate()->GetCurrentContext(); - env->async_hooks()->set_execution_async_resource(resource); + env->async_hooks()->push_execution_async_resource(resource); Emit(env, async_id, AsyncHooks::kBefore, env->async_hooks_before_function()); @@ -180,7 +180,7 @@ void AsyncWrap::EmitAfter(Environment* env, double async_id) { Emit(env, async_id, AsyncHooks::kAfter, env->async_hooks_after_function()); - env->async_hooks()->clear_execution_async_resource(); + env->async_hooks()->pop_execution_async_resource(); } class PromiseWrap : public AsyncWrap { @@ -266,10 +266,14 @@ static void PromiseHook(PromiseHookType type, Local promise, // needed for async functions :/ // the top level will not emit before and after - env->async_hooks()->set_execution_async_resource(wrap->object()); + env->async_hooks()->push_execution_async_resource(wrap->object()); } } + if (type == PromiseHookType::kResolve && !parent->IsPromise()) { + env->async_hooks()->pop_execution_async_resource(); + } + if (wrap == nullptr) return; if (type == PromiseHookType::kBefore) { @@ -402,11 +406,19 @@ static void GetExecutionAsyncResource(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(env->async_hooks()->get_execution_async_resource()); } -static void SetExecutionAsyncResource(const FunctionCallbackInfo& args) { +static void PushExecutionAsyncResource( + const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Environment* env = Environment::GetCurrent(args); + v8::Local context = isolate->GetCurrentContext(); + env->async_hooks()->push_execution_async_resource(args[0]); +} + +static void PopExecutionAsyncResource(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); Environment* env = Environment::GetCurrent(args); v8::Local context = isolate->GetCurrentContext(); - env->async_hooks()->set_execution_async_resource(args[0]); + env->async_hooks()->pop_execution_async_resource(); } void AsyncWrap::GetAsyncId(const FunctionCallbackInfo& args) { @@ -497,10 +509,12 @@ void AsyncWrap::Initialize(Local target, env->SetMethod(target, "disablePromiseHook", DisablePromiseHook); env->SetMethod(target, "registerDestroyHook", RegisterDestroyHook); env->SetMethod(target, "executionAsyncResource", GetExecutionAsyncResource); - env->SetMethod(target, "setExecutionAsyncResource", - SetExecutionAsyncResource); + env->SetMethod(target, "pushExecutionAsyncResource", + PushExecutionAsyncResource); + env->SetMethod(target, "popExecutionAsyncResource", + PopExecutionAsyncResource); - env->async_hooks()->clear_execution_async_resource(); + env->async_hooks()->push_execution_async_resource(v8::Object::New(isolate)); PropertyAttribute ReadOnlyDontDelete = static_cast(ReadOnly | DontDelete); diff --git a/src/env-inl.h b/src/env-inl.h index 6591add4ca060c..74a692d5100bf9 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -67,8 +67,7 @@ inline MultiIsolatePlatform* IsolateData::platform() const { inline AsyncHooks::AsyncHooks() : async_ids_stack_(env()->isolate(), 16 * 2), fields_(env()->isolate(), kFieldsCount), - async_id_fields_(env()->isolate(), kUidFieldsCount), - execution_async_resource_(env()->isolate(), v8::Null(env()->isolate())) { + async_id_fields_(env()->isolate(), kUidFieldsCount) { v8::HandleScope handle_scope(env()->isolate()); // Always perform async_hooks checks, not just when async_hooks is enabled. @@ -207,17 +206,23 @@ inline AsyncHooks::DefaultTriggerAsyncIdScope ::~DefaultTriggerAsyncIdScope() { old_default_trigger_async_id_; } -inline void AsyncHooks::set_execution_async_resource( +inline void AsyncHooks::push_execution_async_resource( v8::Local execution_async_resource) { - execution_async_resource_.Reset(env()->isolate(), execution_async_resource); + execution_async_resources_.push(v8::Global( + env()->isolate(), execution_async_resource)); } -inline v8::Local AsyncHooks::get_execution_async_resource() { - return execution_async_resource_.Get(env()->isolate()); +inline void AsyncHooks::pop_execution_async_resource() { + if (execution_async_resources_.size()) { + execution_async_resources_.pop(); + } } -inline void AsyncHooks::clear_execution_async_resource() { - execution_async_resource_.Reset(); +inline v8::Local AsyncHooks::get_execution_async_resource() { + if (!execution_async_resources_.size()) { + return v8::Null(env()->isolate()); + } + return execution_async_resources_.top().Get(env()->isolate()); } Environment* Environment::ForAsyncHooks(AsyncHooks* hooks) { diff --git a/src/env.h b/src/env.h index 8dd75f9f910c20..505cbfa3892f68 100644 --- a/src/env.h +++ b/src/env.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -684,10 +685,10 @@ class AsyncHooks : public MemoryRetainer { inline bool pop_async_id(double async_id); inline void clear_async_id_stack(); // Used in fatal exceptions. - inline void set_execution_async_resource( + inline void push_execution_async_resource( v8::Local execution_async_resource_); + inline void pop_execution_async_resource(); inline v8::Local get_execution_async_resource(); - inline void clear_execution_async_resource(); AsyncHooks(const AsyncHooks&) = delete; AsyncHooks& operator=(const AsyncHooks&) = delete; @@ -732,7 +733,7 @@ class AsyncHooks : public MemoryRetainer { void grow_async_ids_stack(); - v8::Persistent execution_async_resource_; + std::stack> execution_async_resources_; }; class ImmediateInfo : public MemoryRetainer {