summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAnatoli Papirovski <apapirovski@mac.com>2018-05-13 17:42:22 +0200
committerAnatoli Papirovski <apapirovski@mac.com>2018-06-24 21:35:05 -0700
commit2930bd1317d15d12738a4896c0a6c05700411b47 (patch)
tree5c9225b9740c79d83ea2ded69d63b94a66846036 /src
parent6f63f8d730c8c3b19de7a591c35d376d428a4d56 (diff)
downloadandroid-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')
-rw-r--r--src/async_wrap.h1
-rw-r--r--src/env-inl.h8
-rw-r--r--src/env.cc81
-rw-r--r--src/env.h8
-rw-r--r--src/node_internals.h2
-rw-r--r--src/timers.cc64
6 files changed, 162 insertions, 2 deletions
diff --git a/src/async_wrap.h b/src/async_wrap.h
index 82c5791092..64b74ac209 100644
--- a/src/async_wrap.h
+++ b/src/async_wrap.h
@@ -63,7 +63,6 @@ namespace node {
V(TCPCONNECTWRAP) \
V(TCPSERVERWRAP) \
V(TCPWRAP) \
- V(TIMERWRAP) \
V(TTYWRAP) \
V(UDPSENDWRAP) \
V(UDPWRAP) \
diff --git a/src/env-inl.h b/src/env-inl.h
index bbb80c6f7a..40fa5dfa68 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -334,6 +334,14 @@ inline tracing::Agent* Environment::tracing_agent() const {
return tracing_agent_;
}
+inline Environment* Environment::from_timer_handle(uv_timer_t* handle) {
+ return ContainerOf(&Environment::timer_handle_, handle);
+}
+
+inline uv_timer_t* Environment::timer_handle() {
+ return &timer_handle_;
+}
+
inline Environment* Environment::from_immediate_check_handle(
uv_check_t* handle) {
return ContainerOf(&Environment::immediate_check_handle_, handle);
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);
diff --git a/src/env.h b/src/env.h
index 786f0846f4..8f4c5ea1a6 100644
--- a/src/env.h
+++ b/src/env.h
@@ -628,6 +628,9 @@ class Environment {
inline uv_loop_t* event_loop() const;
inline uint32_t watched_providers() const;
+ static inline Environment* from_timer_handle(uv_timer_t* handle);
+ inline uv_timer_t* timer_handle();
+
static inline Environment* from_immediate_check_handle(uv_check_t* handle);
inline uv_check_t* immediate_check_handle();
inline uv_idle_t* immediate_idle_handle();
@@ -840,6 +843,8 @@ class Environment {
static inline Environment* ForAsyncHooks(AsyncHooks* hooks);
v8::Local<v8::Value> GetNow();
+ void ScheduleTimer(int64_t duration);
+ void ToggleTimerRef(bool ref);
inline void AddCleanupHook(void (*fn)(void*), void* arg);
inline void RemoveCleanupHook(void (*fn)(void*), void* arg);
@@ -857,6 +862,7 @@ class Environment {
v8::Isolate* const isolate_;
IsolateData* const isolate_data_;
tracing::Agent* const tracing_agent_;
+ uv_timer_t timer_handle_;
uv_check_t immediate_check_handle_;
uv_idle_t immediate_idle_handle_;
uv_prepare_t idle_prepare_handle_;
@@ -919,6 +925,8 @@ class Environment {
worker::Worker* worker_context_ = nullptr;
+ static void RunTimers(uv_timer_t* handle);
+
struct ExitCallback {
void (*cb_)(void* arg);
void* arg_;
diff --git a/src/node_internals.h b/src/node_internals.h
index cd791f8c05..860566e314 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -129,7 +129,7 @@ struct sockaddr;
V(string_decoder) \
V(symbols) \
V(tcp_wrap) \
- V(timer_wrap) \
+ V(timers) \
V(trace_events) \
V(tty_wrap) \
V(types) \
diff --git a/src/timers.cc b/src/timers.cc
new file mode 100644
index 0000000000..e9daf8d37c
--- /dev/null
+++ b/src/timers.cc
@@ -0,0 +1,64 @@
+#include "node_internals.h"
+
+#include <stdint.h>
+
+namespace node {
+namespace {
+
+using v8::Array;
+using v8::Context;
+using v8::Function;
+using v8::FunctionCallbackInfo;
+using v8::Integer;
+using v8::Local;
+using v8::Object;
+using v8::Value;
+
+void SetupTimers(const FunctionCallbackInfo<Value>& args) {
+ CHECK(args[0]->IsFunction());
+ CHECK(args[1]->IsFunction());
+ auto env = Environment::GetCurrent(args);
+
+ env->set_immediate_callback_function(args[0].As<Function>());
+ env->set_timers_callback_function(args[1].As<Function>());
+}
+
+void GetLibuvNow(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ args.GetReturnValue().Set(env->GetNow());
+}
+
+void ScheduleTimer(const FunctionCallbackInfo<Value>& args) {
+ auto env = Environment::GetCurrent(args);
+ env->ScheduleTimer(args[0]->IntegerValue(env->context()).FromJust());
+}
+
+void ToggleTimerRef(const FunctionCallbackInfo<Value>& args) {
+ Environment::GetCurrent(args)->ToggleTimerRef(args[0]->IsTrue());
+}
+
+void ToggleImmediateRef(const FunctionCallbackInfo<Value>& args) {
+ Environment::GetCurrent(args)->ToggleImmediateRef(args[0]->IsTrue());
+}
+
+void Initialize(Local<Object> target,
+ Local<Value> unused,
+ Local<Context> context) {
+ Environment* env = Environment::GetCurrent(context);
+
+ env->SetMethod(target, "getLibuvNow", GetLibuvNow);
+ env->SetMethod(target, "setupTimers", SetupTimers);
+ env->SetMethod(target, "scheduleTimer", ScheduleTimer);
+ env->SetMethod(target, "toggleTimerRef", ToggleTimerRef);
+ env->SetMethod(target, "toggleImmediateRef", ToggleImmediateRef);
+
+ target->Set(env->context(),
+ FIXED_ONE_BYTE_STRING(env->isolate(), "immediateInfo"),
+ env->immediate_info()->fields().GetJSArray()).FromJust();
+}
+
+
+} // anonymous namespace
+} // namespace node
+
+NODE_MODULE_CONTEXT_AWARE_INTERNAL(timers, node::Initialize)