Skip to content

Commit

Permalink
timers: use V8 fast API calls
Browse files Browse the repository at this point in the history
  • Loading branch information
joyeecheung committed Feb 9, 2023
1 parent 5092346 commit a97da50
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 49 deletions.
21 changes: 9 additions & 12 deletions lib/internal/timers.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,11 @@ const {
Symbol,
} = primordials;

const binding = internalBinding('timers');
const {
scheduleTimer,
toggleTimerRef,
getLibuvNow,
immediateInfo,
timeoutInfo,
toggleImmediateRef
} = internalBinding('timers');
} = binding;

const {
getDefaultTriggerAsyncId,
Expand Down Expand Up @@ -307,12 +304,12 @@ const immediateQueue = new ImmediateList();

function incRefCount() {
if (timeoutInfo[0]++ === 0)
toggleTimerRef(true);
binding.toggleTimerRef(true);
}

function decRefCount() {
if (--timeoutInfo[0] === 0)
toggleTimerRef(false);
binding.toggleTimerRef(false);
}

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

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

if (nextExpiry > expiry) {
scheduleTimer(msecs);
binding.scheduleTimer(msecs);
nextExpiry = expiry;
}
}
Expand Down Expand Up @@ -560,7 +557,7 @@ function getTimerCallbacks(runNextTicks) {

let start;
if (timer._repeat)
start = getLibuvNow();
start = binding.getLibuvNow();

try {
const args = timer._timerArgs;
Expand Down Expand Up @@ -628,7 +625,7 @@ class Immediate {
if (this[kRefed] === false) {
this[kRefed] = true;
if (immediateInfo[kRefCount]++ === 0)
toggleImmediateRef(true);
binding.toggleImmediateRef(true);
}
return this;
}
Expand All @@ -637,7 +634,7 @@ class Immediate {
if (this[kRefed] === true) {
this[kRefed] = false;
if (--immediateInfo[kRefCount] === 0)
toggleImmediateRef(false);
binding.toggleImmediateRef(false);
}
return this;
}
Expand Down
6 changes: 3 additions & 3 deletions lib/timers.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ const {
SymbolToPrimitive
} = primordials;

const binding = internalBinding('timers');
const {
immediateInfo,
toggleImmediateRef
} = internalBinding('timers');
} = binding;
const L = require('internal/linkedlist');
const {
async_id_symbol,
Expand Down Expand Up @@ -324,7 +324,7 @@ function clearImmediate(immediate) {
immediate._destroyed = true;

if (immediate[kRefed] && --immediateInfo[kRefCount] === 0)
toggleImmediateRef(false);
binding.toggleImmediateRef(false);
immediate[kRefed] = null;

if (destroyHooksExist() && immediate[async_id_symbol] !== undefined) {
Expand Down
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@
'src/string_decoder-inl.h',
'src/string_search.h',
'src/tcp_wrap.h',
'src/timers.h',
'src/tracing/agent.h',
'src/tracing/node_trace_buffer.h',
'src/tracing/node_trace_writer.h',
Expand Down
8 changes: 6 additions & 2 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1273,12 +1273,16 @@ void Environment::ToggleImmediateRef(bool ref) {
}
}


Local<Value> Environment::GetNow() {
uint64_t Environment::GetNowUint64() {
uv_update_time(event_loop());
uint64_t now = uv_now(event_loop());
CHECK_GE(now, timer_base());
now -= timer_base();
return now;
}

Local<Value> Environment::GetNow() {
uint64_t now = GetNowUint64();
if (now <= 0xffffffff)
return Integer::NewFromUnsigned(isolate(), static_cast<uint32_t>(now));
else
Expand Down
2 changes: 2 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,8 @@ class Environment : public MemoryRetainer {
static inline Environment* ForAsyncHooks(AsyncHooks* hooks);

v8::Local<v8::Value> GetNow();
uint64_t GetNowUint64();

void ScheduleTimer(int64_t duration);
void ToggleTimerRef(bool ref);

Expand Down
7 changes: 7 additions & 0 deletions src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
namespace node {

using CFunctionCallback = void (*)(v8::Local<v8::Value> receiver);
using CFunctionCallbackReturnDouble = double (*)(v8::Local<v8::Object> receiver);
using CFunctionCallbackWithInt64 = void (*)(v8::Local<v8::Object> receiver,
int64_t);
using CFunctionCallbackWithBool = void (*)(v8::Local<v8::Object> receiver, bool);

// This class manages the external references from the V8 heap
// to the C++ addresses in Node.js.
Expand All @@ -20,6 +24,9 @@ class ExternalReferenceRegistry {

#define ALLOWED_EXTERNAL_REFERENCE_TYPES(V) \
V(CFunctionCallback) \
V(CFunctionCallbackReturnDouble) \
V(CFunctionCallbackWithInt64) \
V(CFunctionCallbackWithBool) \
V(const v8::CFunctionInfo*) \
V(v8::FunctionCallback) \
V(v8::AccessorGetterCallback) \
Expand Down
1 change: 1 addition & 0 deletions src/node_snapshotable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "node_util.h"
#include "node_v8.h"
#include "node_v8_platform-inl.h"
#include "timers.h"

#if HAVE_INSPECTOR
#include "inspector/worker_inspector.h" // ParentInspectorHandle
Expand Down
1 change: 1 addition & 0 deletions src/node_snapshotable.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct PropInfo {
V(v8_binding_data, v8_utils::BindingData) \
V(blob_binding_data, BlobBindingData) \
V(process_binding_data, process::BindingData) \
V(timers_binding_data, timers::BindingData) \
V(util_weak_reference, util::WeakReference)

enum class EmbedderObjectType : uint8_t {
Expand Down
162 changes: 134 additions & 28 deletions src/timers.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "timers.h"
#include "env-inl.h"
#include "node_external_reference.h"
#include "util-inl.h"
Expand All @@ -6,16 +7,17 @@
#include <cstdint>

namespace node {
namespace {
namespace timers {

using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::Value;

void SetupTimers(const FunctionCallbackInfo<Value>& args) {
void BindingData::SetupTimers(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsFunction());
CHECK(args[1]->IsFunction());
auto env = Environment::GetCurrent(args);
Expand All @@ -24,36 +26,124 @@ void SetupTimers(const FunctionCallbackInfo<Value>& args) {
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 BindingData::SlowGetLibuvNow(const FunctionCallbackInfo<Value>& args) {
double now = GetLibuvNowImpl(Environment::GetBindingData<BindingData>(args));
args.GetReturnValue().Set(Number::New(args.GetIsolate(), now));
}

void ScheduleTimer(const FunctionCallbackInfo<Value>& args) {
auto env = Environment::GetCurrent(args);
env->ScheduleTimer(args[0]->IntegerValue(env->context()).FromJust());
double BindingData::FastGetLibuvNow(Local<Object> receiver) {
return GetLibuvNowImpl(FromJSObject<BindingData>(receiver));
}

double BindingData::GetLibuvNowImpl(BindingData* data) {
return static_cast<double>(data->env()->GetNowUint64());
}

void BindingData::SlowScheduleTimers(const FunctionCallbackInfo<Value>& args) {
int64_t duration =
args[0]->IntegerValue(args.GetIsolate()->GetCurrentContext()).FromJust();
ScheduleTimersImpl(Environment::GetBindingData<BindingData>(args), duration);
}

void BindingData::FastScheduleTimers(Local<Object> receiver, int64_t duration) {
ScheduleTimersImpl(FromJSObject<BindingData>(receiver), duration);
}

void BindingData::ScheduleTimersImpl(BindingData* data, int64_t duration) {

data->env()->ScheduleTimer(duration);
}

void BindingData::SlowToggleTimerRef(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ToggleTimerRefImpl(Environment::GetBindingData<BindingData>(args), args[0]->IsTrue());
}

void BindingData::FastToggleTimerRef(Local<Object> receiver, bool ref) {
ToggleTimerRefImpl(FromJSObject<BindingData>(receiver), ref);
}

void BindingData::ToggleTimerRefImpl(BindingData* data, bool ref) {
data->env()->ToggleTimerRef(ref);
}

void BindingData::SlowToggleImmediateRef(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ToggleImmediateRefImpl(Environment::GetBindingData<BindingData>(args), args[0]->IsTrue());
}

void BindingData::FastToggleImmediateRef(Local<Object> receiver,
bool ref) {
ToggleImmediateRefImpl(FromJSObject<BindingData>(receiver), ref);
}

void BindingData::ToggleImmediateRefImpl(BindingData* data, bool ref) {
data->env()->ToggleImmediateRef(ref);
}

BindingData::BindingData(Environment* env, Local<Object> object)
: SnapshotableObject(env, object, type_int) {}

bool BindingData::PrepareForSerialization(Local<Context> context,
v8::SnapshotCreator* creator) {
// Return true because we need to maintain the reference to the binding from
// JS land.
return true;
}

void ToggleTimerRef(const FunctionCallbackInfo<Value>& args) {
Environment::GetCurrent(args)->ToggleTimerRef(args[0]->IsTrue());
InternalFieldInfoBase* BindingData::Serialize(int index) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
InternalFieldInfo* info =
InternalFieldInfoBase::New<InternalFieldInfo>(type());
return info;
}

void ToggleImmediateRef(const FunctionCallbackInfo<Value>& args) {
Environment::GetCurrent(args)->ToggleImmediateRef(args[0]->IsTrue());
void BindingData::Deserialize(Local<Context> context,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
v8::HandleScope scope(context->GetIsolate());
Environment* env = Environment::GetCurrent(context);
// Recreate the buffer in the constructor.
BindingData* binding = env->AddBindingData<BindingData>(context, holder);
CHECK_NOT_NULL(binding);
}

void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
v8::CFunction BindingData::fast_get_libuv_now_(v8::CFunction::Make(FastGetLibuvNow));
v8::CFunction BindingData::fast_schedule_timers_(v8::CFunction::Make(FastScheduleTimers));
v8::CFunction BindingData::fast_toggle_timer_ref_(v8::CFunction::Make(FastToggleTimerRef));
v8::CFunction BindingData::fast_toggle_immediate_ref_(v8::CFunction::Make(FastToggleImmediateRef));

void BindingData::Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Environment* env = Environment::GetCurrent(context);
BindingData* const binding_data =
env->AddBindingData<BindingData>(context, target);
if (binding_data == nullptr) return;

SetMethod(context, target, "getLibuvNow", GetLibuvNow);
SetMethod(context, target, "setupTimers", SetupTimers);
SetMethod(context, target, "scheduleTimer", ScheduleTimer);
SetMethod(context, target, "toggleTimerRef", ToggleTimerRef);
SetMethod(context, target, "toggleImmediateRef", ToggleImmediateRef);
SetFastMethod(
context, target, "getLibuvNow", SlowGetLibuvNow, &fast_get_libuv_now_);
SetFastMethod(context,
target,
"scheduleTimer",
SlowScheduleTimers,
&fast_schedule_timers_);
SetFastMethod(context,
target,
"toggleTimerRef",
SlowToggleTimerRef,
&fast_toggle_timer_ref_);
SetFastMethod(context,
target,
"toggleImmediateRef",
SlowToggleImmediateRef,
&fast_toggle_immediate_ref_);

// TODO(joyeecheung): move these into BindingData.
target
->Set(context,
FIXED_ONE_BYTE_STRING(env->isolate(), "immediateInfo"),
Expand All @@ -66,16 +156,32 @@ void Initialize(Local<Object> target,
env->timeout_info().GetJSArray())
.Check();
}
} // anonymous namespace
void RegisterTimerExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(GetLibuvNow);

void BindingData::RegisterTimerExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(SetupTimers);
registry->Register(ScheduleTimer);
registry->Register(ToggleTimerRef);
registry->Register(ToggleImmediateRef);

registry->Register(SlowGetLibuvNow);
registry->Register(FastGetLibuvNow);
registry->Register(fast_get_libuv_now_.GetTypeInfo());

registry->Register(SlowScheduleTimers);
registry->Register(FastScheduleTimers);
registry->Register(fast_schedule_timers_.GetTypeInfo());

registry->Register(SlowToggleTimerRef);
registry->Register(FastToggleTimerRef);
registry->Register(fast_toggle_timer_ref_.GetTypeInfo());

registry->Register(SlowToggleImmediateRef);
registry->Register(FastToggleImmediateRef);
registry->Register(fast_toggle_immediate_ref_.GetTypeInfo());
}

} // namespace timers

} // namespace node

NODE_BINDING_CONTEXT_AWARE_INTERNAL(timers, node::Initialize)
NODE_BINDING_EXTERNAL_REFERENCE(timers, node::RegisterTimerExternalReferences)
NODE_BINDING_CONTEXT_AWARE_INTERNAL(timers,
node::timers::BindingData::Initialize)
NODE_BINDING_EXTERNAL_REFERENCE(
timers, node::timers::BindingData::RegisterTimerExternalReferences)
Loading

0 comments on commit a97da50

Please sign in to comment.