diff --git a/src/async-wrap-inl.h b/src/async-wrap-inl.h index 841c7235f52d33..7a88ae827f2328 100644 --- a/src/async-wrap-inl.h +++ b/src/async-wrap-inl.h @@ -40,6 +40,7 @@ inline AsyncWrap::AsyncWrap(Environment* env, v8::TryCatch try_catch; v8::Local n = v8::Int32::New(env->isolate(), provider); + env->inc_callout_count(); env->async_hooks_init_function()->Call(object, 1, &n); if (try_catch.HasCaught()) diff --git a/src/async-wrap.cc b/src/async-wrap.cc index ccf357ba24a21c..aff83d9ec417a6 100644 --- a/src/async-wrap.cc +++ b/src/async-wrap.cc @@ -185,6 +185,7 @@ Local AsyncWrap::MakeCallback(const Local cb, if (has_domain) { Local enter_v = domain->Get(env()->enter_string()); if (enter_v->IsFunction()) { + env()->inc_callout_count(); enter_v.As()->Call(domain, 0, nullptr); if (try_catch.HasCaught()) return Undefined(env()->isolate()); @@ -193,12 +194,14 @@ Local AsyncWrap::MakeCallback(const Local cb, if (has_async_queue()) { try_catch.SetVerbose(false); + env()->inc_callout_count(); env()->async_hooks_pre_function()->Call(context, 0, nullptr); if (try_catch.HasCaught()) FatalError("node::AsyncWrap::MakeCallback", "pre hook threw"); try_catch.SetVerbose(true); } + env()->inc_callout_count(); Local ret = cb->Call(context, argc, argv); if (try_catch.HasCaught()) { @@ -207,6 +210,7 @@ Local AsyncWrap::MakeCallback(const Local cb, if (has_async_queue()) { try_catch.SetVerbose(false); + env()->inc_callout_count(); env()->async_hooks_post_function()->Call(context, 0, nullptr); if (try_catch.HasCaught()) FatalError("node::AsyncWrap::MakeCallback", "post hook threw"); @@ -216,6 +220,7 @@ Local AsyncWrap::MakeCallback(const Local cb, if (has_domain) { Local exit_v = domain->Get(env()->exit_string()); if (exit_v->IsFunction()) { + env()->inc_callout_count(); exit_v.As()->Call(domain, 0, nullptr); if (try_catch.HasCaught()) return Undefined(env()->isolate()); @@ -239,6 +244,7 @@ Local AsyncWrap::MakeCallback(const Local cb, tick_info->set_in_tick(true); + env()->inc_callout_count(); env()->tick_callback_function()->Call(process, 0, nullptr); tick_info->set_in_tick(false); diff --git a/src/env-inl.h b/src/env-inl.h index 0b4b4a5a4a5ef2..b584bc0ce5048b 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -387,6 +387,21 @@ inline void Environment::set_http_parser_buffer(char* buffer) { http_parser_buffer_ = buffer; } + +inline unsigned int Environment::callout_count() const { + return callout_count_; +} + + +inline void Environment::reset_callout_count() { + callout_count_ = 0; +} + + +inline void Environment::inc_callout_count() { + callout_count_++; +} + inline Environment* Environment::from_cares_timer_handle(uv_timer_t* handle) { return ContainerOf(&Environment::cares_timer_handle_, handle); } diff --git a/src/env.h b/src/env.h index 4a11c5064cadad..48c67105ae12b7 100644 --- a/src/env.h +++ b/src/env.h @@ -452,6 +452,10 @@ class Environment { inline char* http_parser_buffer() const; inline void set_http_parser_buffer(char* buffer); + inline unsigned int callout_count() const; + inline void reset_callout_count(); + inline void inc_callout_count(); + inline void ThrowError(const char* errmsg); inline void ThrowTypeError(const char* errmsg); inline void ThrowRangeError(const char* errmsg); @@ -551,6 +555,7 @@ class Environment { uint32_t* heap_statistics_buffer_ = nullptr; char* http_parser_buffer_; + unsigned int callout_count_ = 0; #define V(PropertyName, TypeName) \ v8::Persistent PropertyName ## _; diff --git a/src/node.cc b/src/node.cc index d3e7d1983ceade..6a6856f5e26ebd 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1015,6 +1015,7 @@ void PromiseRejectCallback(PromiseRejectMessage message) { Local args[] = { event, promise, value }; Local process = env->process_object(); + env->inc_callout_count(); callback->Call(process, ARRAY_SIZE(args), args); } @@ -1068,7 +1069,8 @@ Local MakeCallback(Environment* env, if (has_domain) { Local enter_v = domain->Get(env->enter_string()); if (enter_v->IsFunction()) { - enter_v.As()->Call(domain, 0, nullptr); + env->inc_callout_count(); + enter_v.As()->Call(domain, 0, nullptr); if (try_catch.HasCaught()) return Undefined(env->isolate()); } @@ -1076,16 +1078,19 @@ Local MakeCallback(Environment* env, if (has_async_queue) { try_catch.SetVerbose(false); + env->inc_callout_count(); env->async_hooks_pre_function()->Call(object, 0, nullptr); if (try_catch.HasCaught()) FatalError("node::MakeCallback", "pre hook threw"); try_catch.SetVerbose(true); } + env->inc_callout_count(); Local ret = callback->Call(recv, argc, argv); if (has_async_queue) { try_catch.SetVerbose(false); + env->inc_callout_count(); env->async_hooks_post_function()->Call(object, 0, nullptr); if (try_catch.HasCaught()) FatalError("node::MakeCallback", "post hook threw"); @@ -1095,6 +1100,7 @@ Local MakeCallback(Environment* env, if (has_domain) { Local exit_v = domain->Get(env->exit_string()); if (exit_v->IsFunction()) { + env->inc_callout_count(); exit_v.As()->Call(domain, 0, nullptr); if (try_catch.HasCaught()) return Undefined(env->isolate()); @@ -3971,17 +3977,11 @@ static void StartNodeInstance(void* arg) { v8::platform::PumpMessageLoop(default_platform, isolate); EmitBeforeExit(env); - // If there are only unrefed handles left, we need to run an - // extra event loop turn to purge the unrefed handles. - if (event_loop->active_handles == 0 && - event_loop->handle_queue[0] != event_loop->handle_queue[1]) - uv_run(event_loop, UV_RUN_NOWAIT); - // Emit `beforeExit` if the loop became alive either after emitting // event, or after running some callbacks. - more = uv_loop_alive(event_loop); - if (uv_run(event_loop, UV_RUN_NOWAIT) != 0) - more = true; + env->reset_callout_count(); + uv_run(event_loop, UV_RUN_NOWAIT); + more = uv_loop_alive(event_loop) || env->callout_count() != 0; } } while (more == true); }