diff options
Diffstat (limited to 'src/env.cc')
-rw-r--r-- | src/env.cc | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/src/env.cc b/src/env.cc index 2236893dd7..f940c18d16 100644 --- a/src/env.cc +++ b/src/env.cc @@ -13,6 +13,7 @@ namespace node { using v8::Context; +using v8::Function; using v8::FunctionTemplate; using v8::HandleScope; using v8::Integer; @@ -25,6 +26,7 @@ using v8::StackFrame; using v8::StackTrace; using v8::String; using v8::Symbol; +using v8::TryCatch; using v8::Value; using worker::Worker; @@ -173,6 +175,9 @@ void Environment::Start(int argc, HandleScope handle_scope(isolate()); Context::Scope context_scope(context()); + CHECK_EQ(0, uv_timer_init(event_loop(), timer_handle())); + uv_unref(reinterpret_cast<uv_handle_t*>(timer_handle())); + uv_check_init(event_loop(), immediate_check_handle()); uv_unref(reinterpret_cast<uv_handle_t*>(immediate_check_handle())); @@ -228,6 +233,10 @@ void Environment::RegisterHandleCleanups() { }; RegisterHandleCleanup( + reinterpret_cast<uv_handle_t*>(timer_handle()), + close_and_finish, + nullptr); + RegisterHandleCleanup( reinterpret_cast<uv_handle_t*>(immediate_check_handle()), close_and_finish, nullptr); @@ -470,6 +479,78 @@ void Environment::RunAndClearNativeImmediates() { } +void Environment::ScheduleTimer(int64_t duration_ms) { + uv_timer_start(timer_handle(), RunTimers, duration_ms, 0); +} + +void Environment::ToggleTimerRef(bool ref) { + if (ref) { + uv_ref(reinterpret_cast<uv_handle_t*>(timer_handle())); + } else { + uv_unref(reinterpret_cast<uv_handle_t*>(timer_handle())); + } +} + +void Environment::RunTimers(uv_timer_t* handle) { + Environment* env = Environment::from_timer_handle(handle); + + if (!env->can_call_into_js()) + return; + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local<Object> process = env->process_object(); + InternalCallbackScope scope(env, process, {0, 0}); + + Local<Function> cb = env->timers_callback_function(); + MaybeLocal<Value> ret; + Local<Value> arg = env->GetNow(); + // This code will loop until all currently due timers will process. It is + // impossible for us to end up in an infinite loop due to how the JS-side + // is structured. + do { + TryCatch try_catch(env->isolate()); + try_catch.SetVerbose(true); + ret = cb->Call(env->context(), process, 1, &arg); + } while (ret.IsEmpty() && env->can_call_into_js()); + + // NOTE(apapirovski): If it ever becomes possibble that `call_into_js` above + // is reset back to `true` after being previously set to `false` then this + // code becomes invalid and needs to be rewritten. Otherwise catastrophic + // timers corruption will occurr and all timers behaviour will become + // entirely unpredictable. + if (ret.IsEmpty()) + return; + + // To allow for less JS-C++ boundary crossing, the value returned from JS + // serves a few purposes: + // 1. If it's 0, no more timers exist and the handle should be unrefed + // 2. If it's > 0, the value represents the next timer's expiry and there + // is at least one timer remaining that is refed. + // 3. If it's < 0, the absolute value represents the next timer's expiry + // and there are no timers that are refed. + int64_t expiry_ms = + ret.ToLocalChecked()->IntegerValue(env->context()).FromJust(); + + uv_handle_t* h = reinterpret_cast<uv_handle_t*>(handle); + + if (expiry_ms != 0) { + int64_t duration_ms = + llabs(expiry_ms) - (uv_now(env->event_loop()) - env->timer_base()); + + env->ScheduleTimer(duration_ms > 0 ? duration_ms : 1); + + if (expiry_ms > 0) + uv_ref(h); + else + uv_unref(h); + } else { + uv_unref(h); + } +} + + void Environment::CheckImmediate(uv_check_t* handle) { Environment* env = Environment::from_immediate_check_handle(handle); |