diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 7410557d2eb6d2..b1a8f689ab3fab 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -137,11 +137,7 @@ RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local wrapper) { // end RetainedAsyncInfo -static void DestroyAsyncIdsCallback(uv_timer_t* handle) { - Environment* env = Environment::from_destroy_async_ids_timer_handle(handle); - - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); +static void DestroyAsyncIdsCallback(Environment* env, void* data) { Local fn = env->async_hooks_destroy_function(); TryCatch try_catch(env->isolate()); @@ -689,8 +685,7 @@ void AsyncWrap::EmitDestroy(Environment* env, double async_id) { return; if (env->destroy_async_id_list()->empty()) { - uv_timer_start(env->destroy_async_ids_timer_handle(), - DestroyAsyncIdsCallback, 0, 0); + env->SetImmediate(DestroyAsyncIdsCallback, nullptr); } env->destroy_async_id_list()->push_back(async_id); diff --git a/src/env-inl.h b/src/env-inl.h index f7a7559b3f2818..1db7c03d97fd9a 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -368,15 +368,6 @@ inline uv_idle_t* Environment::immediate_idle_handle() { return &immediate_idle_handle_; } -inline Environment* Environment::from_destroy_async_ids_timer_handle( - uv_timer_t* handle) { - return ContainerOf(&Environment::destroy_async_ids_timer_handle_, handle); -} - -inline uv_timer_t* Environment::destroy_async_ids_timer_handle() { - return &destroy_async_ids_timer_handle_; -} - inline void Environment::RegisterHandleCleanup(uv_handle_t* handle, HandleCleanupCb cb, void *arg) { @@ -518,6 +509,13 @@ Environment::scheduled_immediate_count() { return scheduled_immediate_count_; } +void Environment::SetImmediate(native_immediate_callback cb, void* data) { + native_immediate_callbacks_.push_back({ cb, data }); + if (scheduled_immediate_count_[0] == 0) + ActivateImmediateCheck(); + scheduled_immediate_count_[0] = scheduled_immediate_count_[0] + 1; +} + inline performance::performance_state* Environment::performance_state() { return performance_state_; } diff --git a/src/env.cc b/src/env.cc index d4ca34aa74bf15..a02321cc6b1bbd 100644 --- a/src/env.cc +++ b/src/env.cc @@ -102,8 +102,6 @@ void Environment::Start(int argc, uv_unref(reinterpret_cast(&idle_prepare_handle_)); uv_unref(reinterpret_cast(&idle_check_handle_)); - uv_timer_init(event_loop(), destroy_async_ids_timer_handle()); - auto close_and_finish = [](Environment* env, uv_handle_t* handle, void* arg) { handle->data = env; @@ -128,10 +126,6 @@ void Environment::Start(int argc, reinterpret_cast(&idle_check_handle_), close_and_finish, nullptr); - RegisterHandleCleanup( - reinterpret_cast(&destroy_async_ids_timer_handle_), - close_and_finish, - nullptr); if (start_profiler_idle_notifier) { StartProfilerIdleNotifier(); @@ -282,6 +276,59 @@ void Environment::EnvPromiseHook(v8::PromiseHookType type, } } +void Environment::RunAndClearNativeImmediates() { + size_t count = native_immediate_callbacks_.size(); + if (count > 0) { + std::vector list; + native_immediate_callbacks_.swap(list); + for (const auto& cb : list) { + cb.cb_(this, cb.data_); + } + +#ifdef DEBUG + CHECK_GE(scheduled_immediate_count_[0], count); +#endif + scheduled_immediate_count_[0] = scheduled_immediate_count_[0] - count; + } +} + +static bool MaybeStopImmediate(Environment* env) { + if (env->scheduled_immediate_count()[0] == 0) { + uv_check_stop(env->immediate_check_handle()); + uv_idle_stop(env->immediate_idle_handle()); + return true; + } + return false; +} + + +void Environment::CheckImmediate(uv_check_t* handle) { + Environment* env = Environment::from_immediate_check_handle(handle); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + + if (MaybeStopImmediate(env)) + return; + + env->RunAndClearNativeImmediates(); + + MakeCallback(env->isolate(), + env->process_object(), + env->immediate_callback_string(), + 0, + nullptr, + {0, 0}).ToLocalChecked(); + + MaybeStopImmediate(env); +} + +void Environment::ActivateImmediateCheck() { + uv_check_start(&immediate_check_handle_, CheckImmediate); + // Idle handle is needed only to stop the event loop from blocking in poll. + uv_idle_start(&immediate_idle_handle_, [](uv_idle_t*){ }); +} + + void CollectExceptionInfo(Environment* env, v8::Local obj, int errorno, diff --git a/src/env.h b/src/env.h index 862431df2ddab4..57c4bc196ce699 100644 --- a/src/env.h +++ b/src/env.h @@ -564,11 +564,8 @@ class Environment { inline uint32_t watched_providers() const; static inline Environment* from_immediate_check_handle(uv_check_t* handle); - static inline Environment* from_destroy_async_ids_timer_handle( - uv_timer_t* handle); inline uv_check_t* immediate_check_handle(); inline uv_idle_t* immediate_idle_handle(); - inline uv_timer_t* destroy_async_ids_timer_handle(); // Register clean-up cb to be called on environment destruction. inline void RegisterHandleCleanup(uv_handle_t* handle, @@ -709,6 +706,11 @@ class Environment { bool RemovePromiseHook(promise_hook_func fn, void* arg); bool EmitNapiWarning(); + typedef void (*native_immediate_callback)(Environment* env, void* data); + inline void SetImmediate(native_immediate_callback cb, void* data); + // This needs to be available for the JS-land setImmediate(). + void ActivateImmediateCheck(); + private: inline void ThrowError(v8::Local (*fun)(v8::Local), const char* errmsg); @@ -717,7 +719,6 @@ class Environment { IsolateData* const isolate_data_; uv_check_t immediate_check_handle_; uv_idle_t immediate_idle_handle_; - uv_timer_t destroy_async_ids_timer_handle_; uv_prepare_t idle_prepare_handle_; uv_check_t idle_check_handle_; @@ -769,6 +770,14 @@ class Environment { }; std::vector promise_hooks_; + struct NativeImmediateCallback { + native_immediate_callback cb_; + void* data_; + }; + std::vector native_immediate_callbacks_; + void RunAndClearNativeImmediates(); + static void CheckImmediate(uv_check_t* handle); + static void EnvPromiseHook(v8::PromiseHookType type, v8::Local promise, v8::Local parent); diff --git a/src/node.cc b/src/node.cc index 81ea92b7e20b1d..6fa074bca0c9cb 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3000,40 +3000,9 @@ static void DebugEnd(const FunctionCallbackInfo& args); namespace { -bool MaybeStopImmediate(Environment* env) { - if (env->scheduled_immediate_count()[0] == 0) { - uv_check_stop(env->immediate_check_handle()); - uv_idle_stop(env->immediate_idle_handle()); - return true; - } - return false; -} - -void CheckImmediate(uv_check_t* handle) { - Environment* env = Environment::from_immediate_check_handle(handle); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - - if (MaybeStopImmediate(env)) - return; - - MakeCallback(env->isolate(), - env->process_object(), - env->immediate_callback_string(), - 0, - nullptr, - {0, 0}).ToLocalChecked(); - - MaybeStopImmediate(env); -} - - void ActivateImmediateCheck(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - uv_check_start(env->immediate_check_handle(), CheckImmediate); - // Idle handle is needed only to stop the event loop from blocking in poll. - uv_idle_start(env->immediate_idle_handle(), - [](uv_idle_t*){ /* do nothing, just keep the loop running */ }); + env->ActivateImmediateCheck(); } diff --git a/test/async-hooks/test-signalwrap.js b/test/async-hooks/test-signalwrap.js index ff7d08bc1207b0..fa975ff0178f3c 100644 --- a/test/async-hooks/test-signalwrap.js +++ b/test/async-hooks/test-signalwrap.js @@ -66,12 +66,14 @@ function onsigusr2() { } function onsigusr2Again() { - checkInvocations( - signal1, { init: 1, before: 2, after: 2, destroy: 1 }, - 'signal1: when second SIGUSR2 handler is called'); - checkInvocations( - signal2, { init: 1, before: 1 }, - 'signal2: when second SIGUSR2 handler is called'); + setImmediate(() => { + checkInvocations( + signal1, { init: 1, before: 2, after: 2, destroy: 1 }, + 'signal1: when second SIGUSR2 handler is called'); + checkInvocations( + signal2, { init: 1, before: 1 }, + 'signal2: when second SIGUSR2 handler is called'); + }); } process.on('exit', onexit); diff --git a/test/async-hooks/test-tcpwrap.js b/test/async-hooks/test-tcpwrap.js index 1f4fc6af0d6b5f..4693e730bfbb15 100644 --- a/test/async-hooks/test-tcpwrap.js +++ b/test/async-hooks/test-tcpwrap.js @@ -128,8 +128,10 @@ function onconnection(c) { function onserverClosed() { checkInvocations(tcp1, { init: 1, before: 1, after: 1, destroy: 1 }, 'tcp1 when server is closed'); - checkInvocations(tcp2, { init: 1, before: 2, after: 2, destroy: 1 }, - 'tcp2 when server is closed'); + setImmediate(() => { + checkInvocations(tcp2, { init: 1, before: 2, after: 2, destroy: 1 }, + 'tcp2 after server is closed'); + }); checkInvocations(tcp3, { init: 1, before: 1, after: 1 }, 'tcp3 synchronously when server is closed'); tick(2, () => {