Skip to content
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

Backporting Async Wrap changes #7048

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions src/async-wrap-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,26 @@ inline AsyncWrap::AsyncWrap(Environment* env,
v8::HandleScope scope(env->isolate());

v8::Local<v8::Value> argv[] = {
v8::Number::New(env->isolate(), get_uid()),
v8::Int32::New(env->isolate(), provider),
v8::Integer::New(env->isolate(), get_uid()),
Null(env->isolate()),
Null(env->isolate())
};

if (parent != nullptr)
argv[2] = parent->object();
if (parent != nullptr) {
argv[2] = v8::Number::New(env->isolate(), parent->get_uid());
argv[3] = parent->object();
}

v8::TryCatch try_catch(env->isolate());

v8::MaybeLocal<v8::Value> ret =
init_fn->Call(env->context(), object, arraysize(argv), argv);

if (ret.IsEmpty())
FatalError("node::AsyncWrap::AsyncWrap", "init hook threw");
if (ret.IsEmpty()) {
ClearFatalExceptionHandlers(env);
FatalException(env->isolate(), try_catch);
}

bits_ |= 1; // ran_init_callback() is true now.
}
Expand All @@ -65,11 +72,14 @@ inline AsyncWrap::~AsyncWrap() {
v8::Local<v8::Function> fn = env()->async_hooks_destroy_function();
if (!fn.IsEmpty()) {
v8::HandleScope scope(env()->isolate());
v8::Local<v8::Value> uid = v8::Integer::New(env()->isolate(), get_uid());
v8::Local<v8::Value> uid = v8::Number::New(env()->isolate(), get_uid());
v8::TryCatch try_catch(env()->isolate());
v8::MaybeLocal<v8::Value> ret =
fn->Call(env()->context(), v8::Null(env()->isolate()), 1, &uid);
if (ret.IsEmpty())
FatalError("node::AsyncWrap::~AsyncWrap", "destroy hook threw");
if (ret.IsEmpty()) {
ClearFatalExceptionHandlers(env());
FatalException(env()->isolate(), try_catch);
}
}
}

Expand Down
117 changes: 70 additions & 47 deletions src/async-wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "v8-profiler.h"

using v8::Array;
using v8::Boolean;
using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
Expand All @@ -17,6 +18,8 @@ using v8::HeapProfiler;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::MaybeLocal;
using v8::Number;
using v8::Object;
using v8::RetainedObjectInfo;
using v8::TryCatch;
Expand Down Expand Up @@ -121,18 +124,35 @@ static void SetupHooks(const FunctionCallbackInfo<Value>& args) {

if (env->async_hooks()->callbacks_enabled())
return env->ThrowError("hooks should not be set while also enabled");

if (!args[0]->IsFunction())
if (!args[0]->IsObject())
return env->ThrowTypeError("first argument must be an object");

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(
env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "pre")).ToLocalChecked();
Local<Value> post_v = fn_obj->Get(
env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "post")).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");

env->set_async_hooks_init_function(args[0].As<Function>());
env->set_async_hooks_init_function(init_v.As<Function>());

if (args[1]->IsFunction())
env->set_async_hooks_pre_function(args[1].As<Function>());
if (args[2]->IsFunction())
env->set_async_hooks_post_function(args[2].As<Function>());
if (args[3]->IsFunction())
env->set_async_hooks_destroy_function(args[3].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>());
}


Expand Down Expand Up @@ -173,94 +193,97 @@ void LoadAsyncWrapperInfo(Environment* env) {


Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
int argc,
Local<Value>* argv) {
int argc,
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<Value> uid = Number::New(env()->isolate(), get_uid());
Local<Object> context = object();
Local<Object> process = env()->process_object();
Local<Object> domain;
bool has_domain = false;

Environment::AsyncCallbackScope callback_scope(env());

if (env()->using_domains()) {
Local<Value> domain_v = context->Get(env()->domain_string());
has_domain = domain_v->IsObject();
if (has_domain) {
domain = domain_v.As<Object>();
if (domain->Get(env()->disposed_string())->IsTrue())
return Undefined(env()->isolate());
return Local<Value>();
}
}

TryCatch try_catch(env()->isolate());
try_catch.SetVerbose(true);

if (has_domain) {
Local<Value> enter_v = domain->Get(env()->enter_string());
if (enter_v->IsFunction()) {
enter_v.As<Function>()->Call(domain, 0, nullptr);
if (try_catch.HasCaught())
return Undefined(env()->isolate());
if (enter_v.As<Function>()->Call(domain, 0, nullptr).IsEmpty()) {
FatalError("node::AsyncWrap::MakeCallback",
"domain enter callback threw, please report this");
}
}
}

if (ran_init_callback() && !pre_fn.IsEmpty()) {
try_catch.SetVerbose(false);
pre_fn->Call(context, 0, nullptr);
if (try_catch.HasCaught())
FatalError("node::AsyncWrap::MakeCallback", "pre hook threw");
try_catch.SetVerbose(true);
TryCatch try_catch(env()->isolate());
MaybeLocal<Value> ar = pre_fn->Call(env()->context(), context, 1, &uid);
if (ar.IsEmpty()) {
ClearFatalExceptionHandlers(env());
FatalException(env()->isolate(), try_catch);
return Local<Value>();
}
}

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

if (try_catch.HasCaught()) {
return Undefined(env()->isolate());
if (ran_init_callback() && !post_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);
if (ar.IsEmpty()) {
ClearFatalExceptionHandlers(env());
FatalException(env()->isolate(), try_catch);
return Local<Value>();
}
}

if (ran_init_callback() && !post_fn.IsEmpty()) {
try_catch.SetVerbose(false);
post_fn->Call(context, 0, nullptr);
if (try_catch.HasCaught())
FatalError("node::AsyncWrap::MakeCallback", "post hook threw");
try_catch.SetVerbose(true);
if (ret.IsEmpty()) {
return ret;
}

if (has_domain) {
Local<Value> exit_v = domain->Get(env()->exit_string());
if (exit_v->IsFunction()) {
exit_v.As<Function>()->Call(domain, 0, nullptr);
if (try_catch.HasCaught())
return Undefined(env()->isolate());
if (exit_v.As<Function>()->Call(domain, 0, nullptr).IsEmpty()) {
FatalError("node::AsyncWrap::MakeCallback",
"domain exit callback threw, please report this");
}
}
}

Environment::TickInfo* tick_info = env()->tick_info();

if (tick_info->in_tick()) {
if (callback_scope.in_makecallback()) {
return ret;
}

Environment::TickInfo* tick_info = env()->tick_info();

if (tick_info->length() == 0) {
env()->isolate()->RunMicrotasks();
}

Local<Object> process = env()->process_object();

if (tick_info->length() == 0) {
tick_info->set_index(0);
return ret;
}

tick_info->set_in_tick(true);

env()->tick_callback_function()->Call(process, 0, nullptr);

tick_info->set_in_tick(false);

if (try_catch.HasCaught()) {
tick_info->set_last_threw(true);
return Undefined(env()->isolate());
if (env()->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
return Local<Value>();
}

return ret;
Expand Down
1 change: 1 addition & 0 deletions src/async-wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace node {
V(FSREQWRAP) \
V(GETADDRINFOREQWRAP) \
V(GETNAMEINFOREQWRAP) \
V(HTTPPARSER) \
V(JSSTREAM) \
V(PIPEWRAP) \
V(PIPECONNECTWRAP) \
Expand Down
32 changes: 15 additions & 17 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,19 @@ inline void Environment::AsyncHooks::set_enable_callbacks(uint32_t flag) {
fields_[kEnableCallbacks] = flag;
}

inline Environment::AsyncCallbackScope::AsyncCallbackScope(Environment* env)
: env_(env) {
env_->makecallback_cntr_++;
}

inline Environment::AsyncCallbackScope::~AsyncCallbackScope() {
env_->makecallback_cntr_--;
}

inline bool Environment::AsyncCallbackScope::in_makecallback() {
return env_->makecallback_cntr_ > 1;
}

inline Environment::DomainFlag::DomainFlag() {
for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0;
}
Expand All @@ -104,7 +117,7 @@ inline uint32_t Environment::DomainFlag::count() const {
return fields_[kCount];
}

inline Environment::TickInfo::TickInfo() : in_tick_(false), last_threw_(false) {
inline Environment::TickInfo::TickInfo() {
for (int i = 0; i < kFieldsCount; ++i)
fields_[i] = 0;
}
Expand All @@ -117,34 +130,18 @@ inline int Environment::TickInfo::fields_count() const {
return kFieldsCount;
}

inline bool Environment::TickInfo::in_tick() const {
return in_tick_;
}

inline uint32_t Environment::TickInfo::index() const {
return fields_[kIndex];
}

inline bool Environment::TickInfo::last_threw() const {
return last_threw_;
}

inline uint32_t Environment::TickInfo::length() const {
return fields_[kLength];
}

inline void Environment::TickInfo::set_in_tick(bool value) {
in_tick_ = value;
}

inline void Environment::TickInfo::set_index(uint32_t value) {
fields_[kIndex] = value;
}

inline void Environment::TickInfo::set_last_threw(bool value) {
last_threw_ = value;
}

inline Environment::ArrayBufferAllocatorInfo::ArrayBufferAllocatorInfo() {
for (int i = 0; i < kFieldsCount; ++i)
fields_[i] = 0;
Expand Down Expand Up @@ -210,6 +207,7 @@ inline Environment::Environment(v8::Local<v8::Context> context,
using_domains_(false),
printed_error_(false),
trace_sync_io_(false),
makecallback_cntr_(0),
async_wrap_uid_(0),
debugger_agent_(this),
http_parser_buffer_(nullptr),
Expand Down
35 changes: 1 addition & 34 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ using v8::Message;
using v8::StackFrame;
using v8::StackTrace;
using v8::TryCatch;
using v8::Value;

void Environment::PrintSyncTrace() const {
if (!trace_sync_io_)
Expand Down Expand Up @@ -56,38 +57,4 @@ void Environment::PrintSyncTrace() const {
fflush(stderr);
}


bool Environment::KickNextTick() {
TickInfo* info = tick_info();

if (info->in_tick()) {
return true;
}

if (info->length() == 0) {
isolate()->RunMicrotasks();
}

if (info->length() == 0) {
info->set_index(0);
return true;
}

info->set_in_tick(true);

// process nextTicks after call
TryCatch try_catch;
try_catch.SetVerbose(true);
tick_callback_function()->Call(process_object(), 0, nullptr);

info->set_in_tick(false);

if (try_catch.HasCaught()) {
info->set_last_threw(true);
return false;
}

return true;
}

} // namespace node
Loading