diff --git a/lib/timers.js b/lib/timers.js index 8e45eeb9d81e0d..c7237a25353e5f 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -46,6 +46,13 @@ const { kInit, kDestroy, kAsyncIdCounter } = async_wrap.constants; const async_id_symbol = Symbol('asyncId'); const trigger_async_id_symbol = Symbol('triggerAsyncId'); +/* This is an Uint32Array for easier sharing with C++ land. */ +const scheduledImmediateCount = process._scheduledImmediateCount; +delete process._scheduledImmediateCount; +/* Kick off setImmediate processing */ +const activateImmediateCheck = process._activateImmediateCheck; +delete process._activateImmediateCheck; + // Timeout values > TIMEOUT_MAX are set to 1. const TIMEOUT_MAX = 2147483647; // 2^31-1 @@ -731,15 +738,9 @@ function processImmediate() { else immediate = next; } - - // Only round-trip to C++ land if we have to. Calling clearImmediate() on an - // immediate that's in |queue| is okay. Worst case is we make a superfluous - // call to NeedImmediateCallbackSetter(). - if (!immediateQueue.head) { - process._needImmediateCallback = false; - } } +process._immediateCallback = processImmediate; // An optimization so that the try/finally only de-optimizes (since at least v8 // 4.7) what is in this smaller function. @@ -751,13 +752,17 @@ function tryOnImmediate(immediate, oldTail) { runCallback(immediate); threw = false; } finally { - // clearImmediate checks _callback === null for kDestroy hooks. immediate._callback = null; if (!threw) emitAfter(immediate[async_id_symbol]); - if (async_hook_fields[kDestroy] > 0 && !immediate._destroyed) { - emitDestroy(immediate[async_id_symbol]); + + if (!immediate._destroyed) { immediate._destroyed = true; + scheduledImmediateCount[0]--; + + if (async_hook_fields[kDestroy] > 0) { + emitDestroy(immediate[async_id_symbol]); + } } if (threw && immediate._idleNext) { @@ -860,10 +865,9 @@ function createImmediate(args, callback) { immediate._argv = args; immediate._onImmediate = callback; - if (!process._needImmediateCallback) { - process._needImmediateCallback = true; - process._immediateCallback = processImmediate; - } + if (scheduledImmediateCount[0] === 0) + activateImmediateCheck(); + scheduledImmediateCount[0]++; immediateQueue.append(immediate); @@ -874,18 +878,16 @@ function createImmediate(args, callback) { exports.clearImmediate = function(immediate) { if (!immediate) return; - if (async_hook_fields[kDestroy] > 0 && - immediate._callback !== null && - !immediate._destroyed) { - emitDestroy(immediate[async_id_symbol]); + if (!immediate._destroyed) { + scheduledImmediateCount[0]--; immediate._destroyed = true; + + if (async_hook_fields[kDestroy] > 0) { + emitDestroy(immediate[async_id_symbol]); + } } immediate._onImmediate = null; immediateQueue.remove(immediate); - - if (!immediateQueue.head) { - process._needImmediateCallback = false; - } }; diff --git a/src/env-inl.h b/src/env-inl.h index 6288f45345d0a4..66c677dd6ebccf 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -304,6 +304,7 @@ inline Environment::Environment(IsolateData* isolate_data, abort_on_uncaught_exception_(false), emit_napi_warning_(true), makecallback_cntr_(0), + scheduled_immediate_count_(isolate_, 1), #if HAVE_INSPECTOR inspector_agent_(new inspector::Agent(this)), #endif @@ -533,6 +534,11 @@ inline void Environment::set_fs_stats_field_array(double* fields) { fs_stats_field_array_ = fields; } +inline AliasedBuffer& +Environment::scheduled_immediate_count() { + return scheduled_immediate_count_; +} + inline performance::performance_state* Environment::performance_state() { return performance_state_; } diff --git a/src/env.h b/src/env.h index 93f18e8cbc5a61..ac5ed89a5a7ba8 100644 --- a/src/env.h +++ b/src/env.h @@ -619,6 +619,8 @@ class Environment { inline double* fs_stats_field_array() const; inline void set_fs_stats_field_array(double* fields); + inline AliasedBuffer& scheduled_immediate_count(); + inline performance::performance_state* performance_state(); inline std::map* performance_marks(); @@ -715,6 +717,8 @@ class Environment { size_t makecallback_cntr_; std::vector destroy_async_id_list_; + AliasedBuffer scheduled_immediate_count_; + performance::performance_state* performance_state_ = nullptr; std::map performance_marks_; diff --git a/src/node.cc b/src/node.cc index 261303de6f19ca..7d8d9bdc424ea1 100644 --- a/src/node.cc +++ b/src/node.cc @@ -387,25 +387,6 @@ static void PrintErrorString(const char* format, ...) { } -static void CheckImmediate(uv_check_t* handle) { - Environment* env = Environment::from_immediate_check_handle(handle); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - MakeCallback(env->isolate(), - env->process_object(), - env->immediate_callback_string(), - 0, - nullptr, - {0, 0}).ToLocalChecked(); -} - - -static void IdleImmediateDummy(uv_idle_t* handle) { - // Do nothing. Only for maintaining event loop. - // TODO(bnoordhuis) Maybe make libuv accept nullptr idle callbacks. -} - - static inline const char *errno_string(int errorno) { #define ERRNO_CASE(e) case e: return #e; switch (errorno) { @@ -3284,39 +3265,40 @@ static void DebugEnd(const FunctionCallbackInfo& args); namespace { -void NeedImmediateCallbackGetter(Local property, - const PropertyCallbackInfo& info) { - Environment* env = Environment::GetCurrent(info); - const uv_check_t* immediate_check_handle = env->immediate_check_handle(); - bool active = uv_is_active( - reinterpret_cast(immediate_check_handle)); - info.GetReturnValue().Set(active); +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()); -void NeedImmediateCallbackSetter( - Local property, - Local value, - const PropertyCallbackInfo& info) { - Environment* env = Environment::GetCurrent(info); + if (MaybeStopImmediate(env)) + return; - uv_check_t* immediate_check_handle = env->immediate_check_handle(); - bool active = uv_is_active( - reinterpret_cast(immediate_check_handle)); + MakeCallback(env->isolate(), + env->process_object(), + env->immediate_callback_string(), + 0, + nullptr, + {0, 0}).ToLocalChecked(); - if (active == value->BooleanValue()) - return; + MaybeStopImmediate(env); +} - uv_idle_t* immediate_idle_handle = env->immediate_idle_handle(); - if (active) { - uv_check_stop(immediate_check_handle); - uv_idle_stop(immediate_idle_handle); - } else { - 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, IdleImmediateDummy); - } +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 */ }); } @@ -3542,12 +3524,11 @@ void SetupProcessObject(Environment* env, process->SetAccessor(FIXED_ONE_BYTE_STRING(env->isolate(), "ppid"), GetParentProcessId); - auto need_immediate_callback_string = - FIXED_ONE_BYTE_STRING(env->isolate(), "_needImmediateCallback"); - CHECK(process->SetAccessor(env->context(), need_immediate_callback_string, - NeedImmediateCallbackGetter, - NeedImmediateCallbackSetter, - env->as_external()).FromJust()); + auto scheduled_immediate_count = + FIXED_ONE_BYTE_STRING(env->isolate(), "_scheduledImmediateCount"); + CHECK(process->Set(env->context(), + scheduled_immediate_count, + env->scheduled_immediate_count().GetJSArray()).FromJust()); // -e, --eval if (eval_string) { @@ -3673,6 +3654,9 @@ void SetupProcessObject(Environment* env, env->as_external()).FromJust()); // define various internal methods + env->SetMethod(process, + "_activateImmediateCheck", + ActivateImmediateCheck); env->SetMethod(process, "_startProfilerIdleNotifier", StartProfilerIdleNotifier);