Skip to content

Commit 03ec71e

Browse files
committed
timers: use V8 fast API calls
1 parent 5092346 commit 03ec71e

12 files changed

+245
-49
lines changed

lib/internal/timers.js

+9-12
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,11 @@ const {
8181
Symbol,
8282
} = primordials;
8383

84+
const binding = internalBinding('timers');
8485
const {
85-
scheduleTimer,
86-
toggleTimerRef,
87-
getLibuvNow,
8886
immediateInfo,
8987
timeoutInfo,
90-
toggleImmediateRef
91-
} = internalBinding('timers');
88+
} = binding;
9289

9390
const {
9491
getDefaultTriggerAsyncId,
@@ -307,12 +304,12 @@ const immediateQueue = new ImmediateList();
307304

308305
function incRefCount() {
309306
if (timeoutInfo[0]++ === 0)
310-
toggleTimerRef(true);
307+
binding.toggleTimerRef(true);
311308
}
312309

313310
function decRefCount() {
314311
if (--timeoutInfo[0] === 0)
315-
toggleTimerRef(false);
312+
binding.toggleTimerRef(false);
316313
}
317314

318315
// Schedule or re-schedule a timer.
@@ -356,7 +353,7 @@ function insertGuarded(item, refed, start) {
356353
item[kRefed] = refed;
357354
}
358355

359-
function insert(item, msecs, start = getLibuvNow()) {
356+
function insert(item, msecs, start = binding.getLibuvNow()) {
360357
// Truncate so that accuracy of sub-millisecond timers is not assumed.
361358
msecs = MathTrunc(msecs);
362359
item._idleStart = start;
@@ -370,7 +367,7 @@ function insert(item, msecs, start = getLibuvNow()) {
370367
timerListQueue.insert(list);
371368

372369
if (nextExpiry > expiry) {
373-
scheduleTimer(msecs);
370+
binding.scheduleTimer(msecs);
374371
nextExpiry = expiry;
375372
}
376373
}
@@ -560,7 +557,7 @@ function getTimerCallbacks(runNextTicks) {
560557

561558
let start;
562559
if (timer._repeat)
563-
start = getLibuvNow();
560+
start = binding.getLibuvNow();
564561

565562
try {
566563
const args = timer._timerArgs;
@@ -628,7 +625,7 @@ class Immediate {
628625
if (this[kRefed] === false) {
629626
this[kRefed] = true;
630627
if (immediateInfo[kRefCount]++ === 0)
631-
toggleImmediateRef(true);
628+
binding.toggleImmediateRef(true);
632629
}
633630
return this;
634631
}
@@ -637,7 +634,7 @@ class Immediate {
637634
if (this[kRefed] === true) {
638635
this[kRefed] = false;
639636
if (--immediateInfo[kRefCount] === 0)
640-
toggleImmediateRef(false);
637+
binding.toggleImmediateRef(false);
641638
}
642639
return this;
643640
}

lib/timers.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ const {
2727
SymbolToPrimitive
2828
} = primordials;
2929

30+
const binding = internalBinding('timers');
3031
const {
3132
immediateInfo,
32-
toggleImmediateRef
33-
} = internalBinding('timers');
33+
} = binding;
3434
const L = require('internal/linkedlist');
3535
const {
3636
async_id_symbol,
@@ -324,7 +324,7 @@ function clearImmediate(immediate) {
324324
immediate._destroyed = true;
325325

326326
if (immediate[kRefed] && --immediateInfo[kRefCount] === 0)
327-
toggleImmediateRef(false);
327+
binding.toggleImmediateRef(false);
328328
immediate[kRefed] = null;
329329

330330
if (destroyHooksExist() && immediate[async_id_symbol] !== undefined) {

node.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@
661661
'src/string_decoder-inl.h',
662662
'src/string_search.h',
663663
'src/tcp_wrap.h',
664+
'src/timers.h',
664665
'src/tracing/agent.h',
665666
'src/tracing/node_trace_buffer.h',
666667
'src/tracing/node_trace_writer.h',

src/env.cc

+6-2
Original file line numberDiff line numberDiff line change
@@ -1273,12 +1273,16 @@ void Environment::ToggleImmediateRef(bool ref) {
12731273
}
12741274
}
12751275

1276-
1277-
Local<Value> Environment::GetNow() {
1276+
uint64_t Environment::GetNowUint64() {
12781277
uv_update_time(event_loop());
12791278
uint64_t now = uv_now(event_loop());
12801279
CHECK_GE(now, timer_base());
12811280
now -= timer_base();
1281+
return now;
1282+
}
1283+
1284+
Local<Value> Environment::GetNow() {
1285+
uint64_t now = GetNowUint64();
12821286
if (now <= 0xffffffff)
12831287
return Integer::NewFromUnsigned(isolate(), static_cast<uint32_t>(now));
12841288
else

src/env.h

+2
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,8 @@ class Environment : public MemoryRetainer {
908908
static inline Environment* ForAsyncHooks(AsyncHooks* hooks);
909909

910910
v8::Local<v8::Value> GetNow();
911+
uint64_t GetNowUint64();
912+
911913
void ScheduleTimer(int64_t duration);
912914
void ToggleTimerRef(bool ref);
913915

src/node_external_reference.h

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
namespace node {
1212

1313
using CFunctionCallback = void (*)(v8::Local<v8::Value> receiver);
14+
using CFunctionCallbackReturnDouble =
15+
double (*)(v8::Local<v8::Object> receiver);
16+
using CFunctionCallbackWithInt64 = void (*)(v8::Local<v8::Object> receiver,
17+
int64_t);
18+
using CFunctionCallbackWithBool = void (*)(v8::Local<v8::Object> receiver,
19+
bool);
1420

1521
// This class manages the external references from the V8 heap
1622
// to the C++ addresses in Node.js.
@@ -20,6 +26,9 @@ class ExternalReferenceRegistry {
2026

2127
#define ALLOWED_EXTERNAL_REFERENCE_TYPES(V) \
2228
V(CFunctionCallback) \
29+
V(CFunctionCallbackReturnDouble) \
30+
V(CFunctionCallbackWithInt64) \
31+
V(CFunctionCallbackWithBool) \
2332
V(const v8::CFunctionInfo*) \
2433
V(v8::FunctionCallback) \
2534
V(v8::AccessorGetterCallback) \

src/node_snapshotable.cc

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "node_util.h"
2121
#include "node_v8.h"
2222
#include "node_v8_platform-inl.h"
23+
#include "timers.h"
2324

2425
#if HAVE_INSPECTOR
2526
#include "inspector/worker_inspector.h" // ParentInspectorHandle

src/node_snapshotable.h

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct PropInfo {
2727
V(v8_binding_data, v8_utils::BindingData) \
2828
V(blob_binding_data, BlobBindingData) \
2929
V(process_binding_data, process::BindingData) \
30+
V(timers_binding_data, timers::BindingData) \
3031
V(util_weak_reference, util::WeakReference)
3132

3233
enum class EmbedderObjectType : uint8_t {

src/timers.cc

+139-28
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "timers.h"
12
#include "env-inl.h"
23
#include "node_external_reference.h"
34
#include "util-inl.h"
@@ -6,16 +7,17 @@
67
#include <cstdint>
78

89
namespace node {
9-
namespace {
10+
namespace timers {
1011

1112
using v8::Context;
1213
using v8::Function;
1314
using v8::FunctionCallbackInfo;
1415
using v8::Local;
16+
using v8::Number;
1517
using v8::Object;
1618
using v8::Value;
1719

18-
void SetupTimers(const FunctionCallbackInfo<Value>& args) {
20+
void BindingData::SetupTimers(const FunctionCallbackInfo<Value>& args) {
1921
CHECK(args[0]->IsFunction());
2022
CHECK(args[1]->IsFunction());
2123
auto env = Environment::GetCurrent(args);
@@ -24,36 +26,128 @@ void SetupTimers(const FunctionCallbackInfo<Value>& args) {
2426
env->set_timers_callback_function(args[1].As<Function>());
2527
}
2628

27-
void GetLibuvNow(const FunctionCallbackInfo<Value>& args) {
28-
Environment* env = Environment::GetCurrent(args);
29-
args.GetReturnValue().Set(env->GetNow());
29+
void BindingData::SlowGetLibuvNow(const FunctionCallbackInfo<Value>& args) {
30+
double now = GetLibuvNowImpl(Environment::GetBindingData<BindingData>(args));
31+
args.GetReturnValue().Set(Number::New(args.GetIsolate(), now));
3032
}
3133

32-
void ScheduleTimer(const FunctionCallbackInfo<Value>& args) {
33-
auto env = Environment::GetCurrent(args);
34-
env->ScheduleTimer(args[0]->IntegerValue(env->context()).FromJust());
34+
double BindingData::FastGetLibuvNow(Local<Object> receiver) {
35+
return GetLibuvNowImpl(FromJSObject<BindingData>(receiver));
36+
}
37+
38+
double BindingData::GetLibuvNowImpl(BindingData* data) {
39+
return static_cast<double>(data->env()->GetNowUint64());
40+
}
41+
42+
void BindingData::SlowScheduleTimers(const FunctionCallbackInfo<Value>& args) {
43+
int64_t duration =
44+
args[0]->IntegerValue(args.GetIsolate()->GetCurrentContext()).FromJust();
45+
ScheduleTimersImpl(Environment::GetBindingData<BindingData>(args), duration);
46+
}
47+
48+
void BindingData::FastScheduleTimers(Local<Object> receiver, int64_t duration) {
49+
ScheduleTimersImpl(FromJSObject<BindingData>(receiver), duration);
50+
}
51+
52+
void BindingData::ScheduleTimersImpl(BindingData* data, int64_t duration) {
53+
data->env()->ScheduleTimer(duration);
54+
}
55+
56+
void BindingData::SlowToggleTimerRef(
57+
const v8::FunctionCallbackInfo<v8::Value>& args) {
58+
ToggleTimerRefImpl(Environment::GetBindingData<BindingData>(args),
59+
args[0]->IsTrue());
60+
}
61+
62+
void BindingData::FastToggleTimerRef(Local<Object> receiver, bool ref) {
63+
ToggleTimerRefImpl(FromJSObject<BindingData>(receiver), ref);
64+
}
65+
66+
void BindingData::ToggleTimerRefImpl(BindingData* data, bool ref) {
67+
data->env()->ToggleTimerRef(ref);
68+
}
69+
70+
void BindingData::SlowToggleImmediateRef(
71+
const v8::FunctionCallbackInfo<v8::Value>& args) {
72+
ToggleImmediateRefImpl(Environment::GetBindingData<BindingData>(args),
73+
args[0]->IsTrue());
74+
}
75+
76+
void BindingData::FastToggleImmediateRef(Local<Object> receiver, bool ref) {
77+
ToggleImmediateRefImpl(FromJSObject<BindingData>(receiver), ref);
78+
}
79+
80+
void BindingData::ToggleImmediateRefImpl(BindingData* data, bool ref) {
81+
data->env()->ToggleImmediateRef(ref);
82+
}
83+
84+
BindingData::BindingData(Environment* env, Local<Object> object)
85+
: SnapshotableObject(env, object, type_int) {}
86+
87+
bool BindingData::PrepareForSerialization(Local<Context> context,
88+
v8::SnapshotCreator* creator) {
89+
// Return true because we need to maintain the reference to the binding from
90+
// JS land.
91+
return true;
3592
}
3693

37-
void ToggleTimerRef(const FunctionCallbackInfo<Value>& args) {
38-
Environment::GetCurrent(args)->ToggleTimerRef(args[0]->IsTrue());
94+
InternalFieldInfoBase* BindingData::Serialize(int index) {
95+
DCHECK_EQ(index, BaseObject::kEmbedderType);
96+
InternalFieldInfo* info =
97+
InternalFieldInfoBase::New<InternalFieldInfo>(type());
98+
return info;
3999
}
40100

41-
void ToggleImmediateRef(const FunctionCallbackInfo<Value>& args) {
42-
Environment::GetCurrent(args)->ToggleImmediateRef(args[0]->IsTrue());
101+
void BindingData::Deserialize(Local<Context> context,
102+
Local<Object> holder,
103+
int index,
104+
InternalFieldInfoBase* info) {
105+
DCHECK_EQ(index, BaseObject::kEmbedderType);
106+
v8::HandleScope scope(context->GetIsolate());
107+
Environment* env = Environment::GetCurrent(context);
108+
// Recreate the buffer in the constructor.
109+
BindingData* binding = env->AddBindingData<BindingData>(context, holder);
110+
CHECK_NOT_NULL(binding);
43111
}
44112

45-
void Initialize(Local<Object> target,
46-
Local<Value> unused,
47-
Local<Context> context,
48-
void* priv) {
113+
v8::CFunction BindingData::fast_get_libuv_now_(
114+
v8::CFunction::Make(FastGetLibuvNow));
115+
v8::CFunction BindingData::fast_schedule_timers_(
116+
v8::CFunction::Make(FastScheduleTimers));
117+
v8::CFunction BindingData::fast_toggle_timer_ref_(
118+
v8::CFunction::Make(FastToggleTimerRef));
119+
v8::CFunction BindingData::fast_toggle_immediate_ref_(
120+
v8::CFunction::Make(FastToggleImmediateRef));
121+
122+
void BindingData::Initialize(Local<Object> target,
123+
Local<Value> unused,
124+
Local<Context> context,
125+
void* priv) {
49126
Environment* env = Environment::GetCurrent(context);
127+
BindingData* const binding_data =
128+
env->AddBindingData<BindingData>(context, target);
129+
if (binding_data == nullptr) return;
50130

51-
SetMethod(context, target, "getLibuvNow", GetLibuvNow);
52131
SetMethod(context, target, "setupTimers", SetupTimers);
53-
SetMethod(context, target, "scheduleTimer", ScheduleTimer);
54-
SetMethod(context, target, "toggleTimerRef", ToggleTimerRef);
55-
SetMethod(context, target, "toggleImmediateRef", ToggleImmediateRef);
132+
SetFastMethod(
133+
context, target, "getLibuvNow", SlowGetLibuvNow, &fast_get_libuv_now_);
134+
SetFastMethod(context,
135+
target,
136+
"scheduleTimer",
137+
SlowScheduleTimers,
138+
&fast_schedule_timers_);
139+
SetFastMethod(context,
140+
target,
141+
"toggleTimerRef",
142+
SlowToggleTimerRef,
143+
&fast_toggle_timer_ref_);
144+
SetFastMethod(context,
145+
target,
146+
"toggleImmediateRef",
147+
SlowToggleImmediateRef,
148+
&fast_toggle_immediate_ref_);
56149

150+
// TODO(joyeecheung): move these into BindingData.
57151
target
58152
->Set(context,
59153
FIXED_ONE_BYTE_STRING(env->isolate(), "immediateInfo"),
@@ -66,16 +160,33 @@ void Initialize(Local<Object> target,
66160
env->timeout_info().GetJSArray())
67161
.Check();
68162
}
69-
} // anonymous namespace
70-
void RegisterTimerExternalReferences(ExternalReferenceRegistry* registry) {
71-
registry->Register(GetLibuvNow);
163+
164+
void BindingData::RegisterTimerExternalReferences(
165+
ExternalReferenceRegistry* registry) {
72166
registry->Register(SetupTimers);
73-
registry->Register(ScheduleTimer);
74-
registry->Register(ToggleTimerRef);
75-
registry->Register(ToggleImmediateRef);
167+
168+
registry->Register(SlowGetLibuvNow);
169+
registry->Register(FastGetLibuvNow);
170+
registry->Register(fast_get_libuv_now_.GetTypeInfo());
171+
172+
registry->Register(SlowScheduleTimers);
173+
registry->Register(FastScheduleTimers);
174+
registry->Register(fast_schedule_timers_.GetTypeInfo());
175+
176+
registry->Register(SlowToggleTimerRef);
177+
registry->Register(FastToggleTimerRef);
178+
registry->Register(fast_toggle_timer_ref_.GetTypeInfo());
179+
180+
registry->Register(SlowToggleImmediateRef);
181+
registry->Register(FastToggleImmediateRef);
182+
registry->Register(fast_toggle_immediate_ref_.GetTypeInfo());
76183
}
77184

185+
} // namespace timers
186+
78187
} // namespace node
79188

80-
NODE_BINDING_CONTEXT_AWARE_INTERNAL(timers, node::Initialize)
81-
NODE_BINDING_EXTERNAL_REFERENCE(timers, node::RegisterTimerExternalReferences)
189+
NODE_BINDING_CONTEXT_AWARE_INTERNAL(timers,
190+
node::timers::BindingData::Initialize)
191+
NODE_BINDING_EXTERNAL_REFERENCE(
192+
timers, node::timers::BindingData::RegisterTimerExternalReferences)

0 commit comments

Comments
 (0)