Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
process: use v8 fast api calls for hrtime
Refs: #33374

PR-URL: #33600
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
  • Loading branch information
devsnek committed Jun 6, 2020
commit d8eef83757c263672832687ac7667927a7d0c059
9 changes: 4 additions & 5 deletions lib/internal/process/per_thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ function assert(x, msg) {
function wrapProcessMethods(binding) {
const {
hrtime: _hrtime,
hrtimeBigInt: _hrtimeBigInt,
cpuUsage: _cpuUsage,
memoryUsage: _memoryUsage,
resourceUsage: _resourceUsage
Expand Down Expand Up @@ -113,10 +112,10 @@ function wrapProcessMethods(binding) {
// The 3 entries filled in by the original process.hrtime contains
// the upper/lower 32 bits of the second part of the value,
// and the remaining nanoseconds of the value.
const hrValues = new Uint32Array(3);
const hrValues = new Uint32Array(_hrtime.buffer);

function hrtime(time) {
_hrtime(hrValues);
_hrtime.hrtime();

if (time !== undefined) {
if (!ArrayIsArray(time)) {
Expand All @@ -140,9 +139,9 @@ function wrapProcessMethods(binding) {

// Use a BigUint64Array in the closure because this is actually a bit
// faster than simply returning a BigInt from C++ in V8 7.1.
const hrBigintValues = new BigUint64Array(1);
const hrBigintValues = new BigUint64Array(_hrtime.buffer, 0, 1);
function hrtimeBigInt() {
_hrtimeBigInt(hrBigintValues);
_hrtime.hrtimeBigInt();
return hrBigintValues[0];
}

Expand Down
2 changes: 2 additions & 0 deletions src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) {
// heap based on the actual physical memory.
params->constraints.ConfigureDefaults(total_memory, 0);
}
params->embedder_wrapper_object_index = BaseObject::InternalFields::kSlot;
params->embedder_wrapper_type_index = std::numeric_limits<int>::max();
}

void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
Expand Down
131 changes: 98 additions & 33 deletions src/node_process_methods.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "node_process.h"
#include "util-inl.h"
#include "uv.h"
#include "v8-fast-api-calls.h"
#include "v8.h"

#include <vector>
Expand All @@ -33,7 +34,7 @@ namespace node {

using v8::Array;
using v8::ArrayBuffer;
using v8::BigUint64Array;
using v8::BackingStore;
using v8::Context;
using v8::Float64Array;
using v8::FunctionCallbackInfo;
Expand All @@ -46,7 +47,6 @@ using v8::Number;
using v8::Object;
using v8::String;
using v8::Uint32;
using v8::Uint32Array;
using v8::Value;

namespace per_process {
Expand Down Expand Up @@ -131,35 +131,6 @@ static void Cwd(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(cwd);
}


// Hrtime exposes libuv's uv_hrtime() high-resolution timer.

// This is the legacy version of hrtime before BigInt was introduced in
// JavaScript.
// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds,
// so this function instead fills in an Uint32Array with 3 entries,
// to avoid any integer overflow possibility.
// The first two entries contain the second part of the value
// broken into the upper/lower 32 bits to be converted back in JS,
// because there is no Uint64Array in JS.
// The third entry contains the remaining nanosecond part of the value.
static void Hrtime(const FunctionCallbackInfo<Value>& args) {
uint64_t t = uv_hrtime();

Local<ArrayBuffer> ab = args[0].As<Uint32Array>()->Buffer();
uint32_t* fields = static_cast<uint32_t*>(ab->GetBackingStore()->Data());

fields[0] = (t / NANOS_PER_SEC) >> 32;
fields[1] = (t / NANOS_PER_SEC) & 0xffffffff;
fields[2] = t % NANOS_PER_SEC;
}

static void HrtimeBigInt(const FunctionCallbackInfo<Value>& args) {
Local<ArrayBuffer> ab = args[0].As<BigUint64Array>()->Buffer();
uint64_t* fields = static_cast<uint64_t*>(ab->GetBackingStore()->Data());
fields[0] = uv_hrtime();
}

static void Kill(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Context> context = env->context();
Expand Down Expand Up @@ -452,6 +423,85 @@ static void ReallyExit(const FunctionCallbackInfo<Value>& args) {
env->Exit(code);
}

class FastHrtime : public BaseObject {
public:
static Local<Object> New(Environment* env) {
Local<v8::ObjectTemplate> otmpl = v8::ObjectTemplate::New(env->isolate());
otmpl->SetInternalFieldCount(FastHrtime::kInternalFieldCount);

auto create_func = [env](auto fast_func, auto slow_func) {
auto cfunc = v8::CFunction::Make(fast_func);
return v8::FunctionTemplate::New(env->isolate(),
slow_func,
Local<Value>(),
Local<v8::Signature>(),
0,
v8::ConstructorBehavior::kThrow,
v8::SideEffectType::kHasNoSideEffect,
&cfunc);
};

otmpl->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "hrtime"),
create_func(FastNumber, SlowNumber));
otmpl->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "hrtimeBigInt"),
create_func(FastBigInt, SlowBigInt));

Local<Object> obj = otmpl->NewInstance(env->context()).ToLocalChecked();

Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), 12);
new FastHrtime(env, obj, ab->GetBackingStore());
obj->Set(
env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "buffer"), ab)
.ToChecked();

return obj;
}

private:
FastHrtime(Environment* env,
Local<Object> object,
std::shared_ptr<v8::BackingStore> backing_store)
: BaseObject(env, object), backing_store_(backing_store) {}

void MemoryInfo(MemoryTracker* tracker) const override {}

SET_MEMORY_INFO_NAME(FastHrtime)
SET_SELF_SIZE(FastHrtime)

// This is the legacy version of hrtime before BigInt was introduced in
// JavaScript.
// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds,
// so this function instead fills in an Uint32Array with 3 entries,
// to avoid any integer overflow possibility.
// The first two entries contain the second part of the value
// broken into the upper/lower 32 bits to be converted back in JS,
// because there is no Uint64Array in JS.
// The third entry contains the remaining nanosecond part of the value.
static void FastNumber(FastHrtime* receiver) {
uint64_t t = uv_hrtime();
uint32_t* fields = static_cast<uint32_t*>(receiver->backing_store_->Data());
fields[0] = (t / NANOS_PER_SEC) >> 32;
fields[1] = (t / NANOS_PER_SEC) & 0xffffffff;
fields[2] = t % NANOS_PER_SEC;
Comment thread
jasnell marked this conversation as resolved.
Outdated
}

static void SlowNumber(const FunctionCallbackInfo<Value>& args) {
FastNumber(FromJSObject<FastHrtime>(args.Holder()));
}

static void FastBigInt(FastHrtime* receiver) {
uint64_t t = uv_hrtime();
uint64_t* fields = static_cast<uint64_t*>(receiver->backing_store_->Data());
fields[0] = t;
}

static void SlowBigInt(const FunctionCallbackInfo<Value>& args) {
FastBigInt(FromJSObject<FastHrtime>(args.Holder()));
}

std::shared_ptr<BackingStore> backing_store_;
};

static void InitializeProcessMethods(Local<Object> target,
Local<Value> unused,
Local<Context> context,
Expand All @@ -475,8 +525,6 @@ static void InitializeProcessMethods(Local<Object> target,
env->SetMethod(target, "_rawDebug", RawDebug);
env->SetMethod(target, "memoryUsage", MemoryUsage);
env->SetMethod(target, "cpuUsage", CPUUsage);
env->SetMethod(target, "hrtime", Hrtime);
env->SetMethod(target, "hrtimeBigInt", HrtimeBigInt);
env->SetMethod(target, "resourceUsage", ResourceUsage);

env->SetMethod(target, "_getActiveRequests", GetActiveRequests);
Expand All @@ -488,9 +536,26 @@ static void InitializeProcessMethods(Local<Object> target,
env->SetMethod(target, "reallyExit", ReallyExit);
env->SetMethodNoSideEffect(target, "uptime", Uptime);
env->SetMethod(target, "patchProcessObject", PatchProcessObject);

target
->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "hrtime"),
FastHrtime::New(env))
.ToChecked();
}

} // namespace node

namespace v8 {
template <>
class WrapperTraits<node::FastHrtime> {
public:
static const void* GetTypeInfo() {
Comment thread
devsnek marked this conversation as resolved.
Outdated
static const int tag = 0;
return reinterpret_cast<const void*>(&tag);
}
};
} // namespace v8

NODE_MODULE_CONTEXT_AWARE_INTERNAL(process_methods,
node::InitializeProcessMethods)
52 changes: 32 additions & 20 deletions test/cctest/test_base_object_ptr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ using v8::Isolate;
using v8::Local;
using v8::Object;

// Environments may come with existing BaseObject instances.
// This variable offsets the expected BaseObject counts.
static const int BASE_OBJECT_COUNT = 1;

class BaseObjectPtrTest : public EnvironmentTestFixture {};

class DummyBaseObject : public BaseObject {
Expand Down Expand Up @@ -47,12 +51,12 @@ TEST_F(BaseObjectPtrTest, ScopedDetached) {
Env env_{handle_scope, argv};
Environment* env = *env_;

EXPECT_EQ(env->base_object_count(), 0);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT);
{
BaseObjectPtr<DummyBaseObject> ptr = DummyBaseObject::NewDetached(env);
EXPECT_EQ(env->base_object_count(), 1);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1);
}
EXPECT_EQ(env->base_object_count(), 0);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT);
}

TEST_F(BaseObjectPtrTest, ScopedDetachedWithWeak) {
Expand All @@ -63,14 +67,14 @@ TEST_F(BaseObjectPtrTest, ScopedDetachedWithWeak) {

BaseObjectWeakPtr<DummyBaseObject> weak_ptr;

EXPECT_EQ(env->base_object_count(), 0);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT);
{
BaseObjectPtr<DummyBaseObject> ptr = DummyBaseObject::NewDetached(env);
weak_ptr = ptr;
EXPECT_EQ(env->base_object_count(), 1);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1);
}
EXPECT_EQ(weak_ptr.get(), nullptr);
EXPECT_EQ(env->base_object_count(), 0);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT);
}

TEST_F(BaseObjectPtrTest, Undetached) {
Expand All @@ -79,12 +83,16 @@ TEST_F(BaseObjectPtrTest, Undetached) {
Env env_{handle_scope, argv};
Environment* env = *env_;

node::AddEnvironmentCleanupHook(isolate_, [](void* arg) {
EXPECT_EQ(static_cast<Environment*>(arg)->base_object_count(), 0);
}, env);
node::AddEnvironmentCleanupHook(
isolate_,
[](void* arg) {
EXPECT_EQ(static_cast<Environment*>(arg)->base_object_count(),
BASE_OBJECT_COUNT);
},
env);

BaseObjectPtr<DummyBaseObject> ptr = DummyBaseObject::New(env);
EXPECT_EQ(env->base_object_count(), 1);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1);
}

TEST_F(BaseObjectPtrTest, GCWeak) {
Expand All @@ -101,21 +109,21 @@ TEST_F(BaseObjectPtrTest, GCWeak) {
weak_ptr = ptr;
ptr->MakeWeak();

EXPECT_EQ(env->base_object_count(), 1);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1);
EXPECT_EQ(weak_ptr.get(), ptr.get());
EXPECT_EQ(weak_ptr->persistent().IsWeak(), false);

ptr.reset();
}

EXPECT_EQ(env->base_object_count(), 1);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1);
EXPECT_NE(weak_ptr.get(), nullptr);
EXPECT_EQ(weak_ptr->persistent().IsWeak(), true);

v8::V8::SetFlagsFromString("--expose-gc");
isolate_->RequestGarbageCollectionForTesting(Isolate::kFullGarbageCollection);

EXPECT_EQ(env->base_object_count(), 0);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT);
EXPECT_EQ(weak_ptr.get(), nullptr);
}

Expand All @@ -126,7 +134,7 @@ TEST_F(BaseObjectPtrTest, Moveable) {
Environment* env = *env_;

BaseObjectPtr<DummyBaseObject> ptr = DummyBaseObject::NewDetached(env);
EXPECT_EQ(env->base_object_count(), 1);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1);
BaseObjectWeakPtr<DummyBaseObject> weak_ptr { ptr };
EXPECT_EQ(weak_ptr.get(), ptr.get());

Expand All @@ -137,12 +145,12 @@ TEST_F(BaseObjectPtrTest, Moveable) {
BaseObjectWeakPtr<DummyBaseObject> weak_ptr2 = std::move(weak_ptr);
EXPECT_EQ(weak_ptr2.get(), ptr2.get());
EXPECT_EQ(weak_ptr.get(), nullptr);
EXPECT_EQ(env->base_object_count(), 1);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1);

ptr2.reset();

EXPECT_EQ(weak_ptr2.get(), nullptr);
EXPECT_EQ(env->base_object_count(), 0);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT);
}

TEST_F(BaseObjectPtrTest, NestedClasses) {
Expand All @@ -163,14 +171,18 @@ TEST_F(BaseObjectPtrTest, NestedClasses) {
Env env_{handle_scope, argv};
Environment* env = *env_;

node::AddEnvironmentCleanupHook(isolate_, [](void* arg) {
EXPECT_EQ(static_cast<Environment*>(arg)->base_object_count(), 0);
}, env);
node::AddEnvironmentCleanupHook(
isolate_,
[](void* arg) {
EXPECT_EQ(static_cast<Environment*>(arg)->base_object_count(),
BASE_OBJECT_COUNT);
},
env);

ObjectWithPtr* obj =
new ObjectWithPtr(env, DummyBaseObject::MakeJSObject(env));
obj->ptr1 = DummyBaseObject::NewDetached(env);
obj->ptr2 = DummyBaseObject::New(env);

EXPECT_EQ(env->base_object_count(), 3);
EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 3);
}