Skip to content

Commit

Permalink
async_wrap: only call SetupHooks() once
Browse files Browse the repository at this point in the history
The call to async_wrap.setupHooks() will soon become an internal call.
While other public APIs will be exposed through async_hooks. In
preparation for this, only allow SetupHooks() to be called once. If
called again, or if a non-function is passed, throw.
  • Loading branch information
trevnorris committed Mar 15, 2017
1 parent c053ec3 commit 7d42f1f
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 29 deletions.
47 changes: 26 additions & 21 deletions src/async-wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,32 +151,37 @@ static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
if (!args[0]->IsObject())
return env->ThrowTypeError("first argument must be an object");

// All of init, before, after, destroy are supplied by async_hooks
// internally, so this should every only be called once. At which time all
// the functions should be set. Detect this by checking if init !IsEmpty()
// and returning early if that's the case.
if (!env->async_hooks_init_function().IsEmpty())
return env->ThrowError("async_hook callbacks have already been setup");

Local<Object> fn_obj = args[0].As<Object>();

Local<Value> init_v = fn_obj->Get(
env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "init")).ToLocalChecked();
Local<Value> pre_v = fn_obj->Get(
Local<Value> before_v = fn_obj->Get(
env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "pre")).ToLocalChecked();
Local<Value> post_v = fn_obj->Get(
FIXED_ONE_BYTE_STRING(env->isolate(), "before")).ToLocalChecked();
Local<Value> after_v = fn_obj->Get(
env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "post")).ToLocalChecked();
FIXED_ONE_BYTE_STRING(env->isolate(), "after")).ToLocalChecked();
Local<Value> destroy_v = fn_obj->Get(
env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "destroy")).ToLocalChecked();

if (!init_v->IsFunction())
return env->ThrowTypeError("init callback must be a function");
if (!init_v->IsFunction() || !before_v->IsFunction() ||
!after_v->IsFunction() || !destroy_v->IsFunction()) {
return env->ThrowTypeError("all callbacks must be functions");
}

env->set_async_hooks_init_function(init_v.As<Function>());

if (pre_v->IsFunction())
env->set_async_hooks_pre_function(pre_v.As<Function>());
if (post_v->IsFunction())
env->set_async_hooks_post_function(post_v.As<Function>());
if (destroy_v->IsFunction())
env->set_async_hooks_destroy_function(destroy_v.As<Function>());
env->set_async_hooks_before_function(before_v.As<Function>());
env->set_async_hooks_after_function(after_v.As<Function>());
env->set_async_hooks_destroy_function(destroy_v.As<Function>());
}


Expand All @@ -200,8 +205,8 @@ void AsyncWrap::Initialize(Local<Object> target,
target->Set(FIXED_ONE_BYTE_STRING(isolate, "Providers"), async_providers);

env->set_async_hooks_init_function(Local<Function>());
env->set_async_hooks_pre_function(Local<Function>());
env->set_async_hooks_post_function(Local<Function>());
env->set_async_hooks_before_function(Local<Function>());
env->set_async_hooks_after_function(Local<Function>());
env->set_async_hooks_destroy_function(Local<Function>());
}

Expand Down Expand Up @@ -328,8 +333,8 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
Local<Value>* argv) {
CHECK(env()->context() == env()->isolate()->GetCurrentContext());

Local<Function> pre_fn = env()->async_hooks_pre_function();
Local<Function> post_fn = env()->async_hooks_post_function();
Local<Function> before_fn = env()->async_hooks_before_function();
Local<Function> after_fn = env()->async_hooks_after_function();
Local<Value> uid = Number::New(env()->isolate(), get_id());
Local<Object> context = object();
Local<Object> domain;
Expand Down Expand Up @@ -357,9 +362,9 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
}
}

if (ran_init_callback() && !pre_fn.IsEmpty()) {
if (ran_init_callback() && !before_fn.IsEmpty()) {
TryCatch try_catch(env()->isolate());
MaybeLocal<Value> ar = pre_fn->Call(env()->context(), context, 1, &uid);
MaybeLocal<Value> ar = before_fn->Call(env()->context(), context, 1, &uid);
if (ar.IsEmpty()) {
ClearFatalExceptionHandlers(env());
FatalException(env()->isolate(), try_catch);
Expand All @@ -369,12 +374,12 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,

Local<Value> ret = cb->Call(context, argc, argv);

if (ran_init_callback() && !post_fn.IsEmpty()) {
if (ran_init_callback() && !after_fn.IsEmpty()) {
Local<Value> did_throw = Boolean::New(env()->isolate(), ret.IsEmpty());
Local<Value> vals[] = { uid, did_throw };
TryCatch try_catch(env()->isolate());
MaybeLocal<Value> ar =
post_fn->Call(env()->context(), context, arraysize(vals), vals);
after_fn->Call(env()->context(), context, arraysize(vals), vals);
if (ar.IsEmpty()) {
ClearFatalExceptionHandlers(env());
FatalException(env()->isolate(), try_catch);
Expand Down
4 changes: 2 additions & 2 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,8 @@ namespace node {
V(as_external, v8::External) \
V(async_hooks_destroy_function, v8::Function) \
V(async_hooks_init_function, v8::Function) \
V(async_hooks_post_function, v8::Function) \
V(async_hooks_pre_function, v8::Function) \
V(async_hooks_before_function, v8::Function) \
V(async_hooks_after_function, v8::Function) \
V(binding_cache_object, v8::Object) \
V(buffer_constructor_function, v8::Function) \
V(buffer_prototype_object, v8::Object) \
Expand Down
12 changes: 6 additions & 6 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1228,8 +1228,8 @@ Local<Value> MakeCallback(Environment* env,
// If you hit this assertion, you forgot to enter the v8::Context first.
CHECK_EQ(env->context(), env->isolate()->GetCurrentContext());

Local<Function> pre_fn = env->async_hooks_pre_function();
Local<Function> post_fn = env->async_hooks_post_function();
Local<Function> before_fn = env->async_hooks_before_function();
Local<Function> after_fn = env->async_hooks_after_function();
Local<Object> object, domain;
bool ran_init_callback = false;
bool has_domain = false;
Expand Down Expand Up @@ -1266,9 +1266,9 @@ Local<Value> MakeCallback(Environment* env,
}
}

if (ran_init_callback && !pre_fn.IsEmpty()) {
if (ran_init_callback && !before_fn.IsEmpty()) {
TryCatch try_catch(env->isolate());
MaybeLocal<Value> ar = pre_fn->Call(env->context(), object, 0, nullptr);
MaybeLocal<Value> ar = before_fn->Call(env->context(), object, 0, nullptr);
if (ar.IsEmpty()) {
ClearFatalExceptionHandlers(env);
FatalException(env->isolate(), try_catch);
Expand All @@ -1278,15 +1278,15 @@ Local<Value> MakeCallback(Environment* env,

Local<Value> ret = callback->Call(recv, argc, argv);

if (ran_init_callback && !post_fn.IsEmpty()) {
if (ran_init_callback && !after_fn.IsEmpty()) {
Local<Value> did_throw = Boolean::New(env->isolate(), ret.IsEmpty());
// Currently there's no way to retrieve an uid from node::MakeCallback().
// This needs to be fixed.
Local<Value> vals[] =
{ Undefined(env->isolate()).As<Value>(), did_throw };
TryCatch try_catch(env->isolate());
MaybeLocal<Value> ar =
post_fn->Call(env->context(), object, arraysize(vals), vals);
after_fn->Call(env->context(), object, arraysize(vals), vals);
if (ar.IsEmpty()) {
ClearFatalExceptionHandlers(env);
FatalException(env->isolate(), try_catch);
Expand Down

0 comments on commit 7d42f1f

Please sign in to comment.