diff options
author | Anatoli Papirovski <apapirovski@mac.com> | 2018-05-13 17:42:22 +0200 |
---|---|---|
committer | Anatoli Papirovski <apapirovski@mac.com> | 2018-06-24 21:35:05 -0700 |
commit | 2930bd1317d15d12738a4896c0a6c05700411b47 (patch) | |
tree | 5c9225b9740c79d83ea2ded69d63b94a66846036 /src/env.cc | |
parent | 6f63f8d730c8c3b19de7a591c35d376d428a4d56 (diff) | |
download | android-node-v8-2930bd1317d15d12738a4896c0a6c05700411b47.tar.gz android-node-v8-2930bd1317d15d12738a4896c0a6c05700411b47.tar.bz2 android-node-v8-2930bd1317d15d12738a4896c0a6c05700411b47.zip |
src: refactor timers to remove TimerWrap
Refactor Timers to behave more similarly to Immediates by having
a single uv_timer_t handle which is stored on the Environment.
No longer expose timers in a public binding and instead make
it part of the internalBinding.
PR-URL: https://github.com/nodejs/node/pull/20894
Fixes: https://github.com/nodejs/node/issues/10154
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
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); |