Skip to content

Commit 3a4f387

Browse files
joyeecheungtargos
authored andcommitted
process: support hrtime in the snapshot
Put the hrtime backing store in the process methods binding data so that it can be integrated into the snapshot builder. For now we simply discard the contents of the hrtime buffer during serialization and create new buffers upon deserialization because the contents are only useful in a synchronous call. This also moves the helper function for creating V8 FastAPI methods into `Environment::SetFastMethod()` for code reuse. The v8::CFunction need to be created before the Environment is created so that we have the CTypeInfo address available for external reference registration. PR-URL: #40649 Refs: #35711 Refs: #37476 Reviewed-By: James M Snell <[email protected]>
1 parent 88b57bc commit 3a4f387

10 files changed

+253
-179
lines changed

lib/internal/bootstrap/node.js

+3
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ const rawMethods = internalBinding('process_methods');
162162
process.kill = wrapped.kill;
163163
process.exit = wrapped.exit;
164164

165+
process.hrtime = perThreadSetup.hrtime;
166+
process.hrtime.bigint = perThreadSetup.hrtimeBigInt;
167+
165168
process.openStdin = function() {
166169
process.stdin.resume();
167170
return process.stdin;

lib/internal/bootstrap/pre_execution.js

+1-10
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,7 @@ function patchProcessObject(expandArgv1) {
8282
const binding = internalBinding('process_methods');
8383
binding.patchProcessObject(process);
8484

85-
// TODO(joyeecheung): snapshot fast APIs (which need to work with
86-
// array buffers, whose connection with C++ needs to be rebuilt after
87-
// deserialization).
88-
const {
89-
hrtime,
90-
hrtimeBigInt
91-
} = require('internal/process/per_thread').getFastAPIs(binding);
92-
93-
process.hrtime = hrtime;
94-
process.hrtime.bigint = hrtimeBigInt;
85+
require('internal/process/per_thread').refreshHrtimeBuffer();
9586

9687
ObjectDefineProperty(process, 'argv0', {
9788
enumerable: true,

lib/internal/process/per_thread.js

+34-33
Original file line numberDiff line numberDiff line change
@@ -54,49 +54,48 @@ function assert(x, msg) {
5454
if (!x) throw new ERR_ASSERTION(msg || 'assertion error');
5555
}
5656

57-
function getFastAPIs(binding) {
58-
const {
59-
hrtime: _hrtime
60-
} = binding.getFastAPIs();
57+
const binding = internalBinding('process_methods');
58+
59+
let hrValues;
60+
let hrBigintValues;
6161

62+
function refreshHrtimeBuffer() {
6263
// The 3 entries filled in by the original process.hrtime contains
6364
// the upper/lower 32 bits of the second part of the value,
6465
// and the remaining nanoseconds of the value.
65-
const hrValues = new Uint32Array(_hrtime.buffer);
66+
hrValues = new Uint32Array(binding.hrtimeBuffer);
67+
// Use a BigUint64Array in the closure because this is actually a bit
68+
// faster than simply returning a BigInt from C++ in V8 7.1.
69+
hrBigintValues = new BigUint64Array(binding.hrtimeBuffer, 0, 1);
70+
}
6671

67-
function hrtime(time) {
68-
_hrtime.hrtime();
72+
// Create the buffers.
73+
refreshHrtimeBuffer();
6974

70-
if (time !== undefined) {
71-
validateArray(time, 'time');
72-
if (time.length !== 2) {
73-
throw new ERR_OUT_OF_RANGE('time', 2, time.length);
74-
}
75+
function hrtime(time) {
76+
binding.hrtime();
7577

76-
const sec = (hrValues[0] * 0x100000000 + hrValues[1]) - time[0];
77-
const nsec = hrValues[2] - time[1];
78-
const needsBorrow = nsec < 0;
79-
return [needsBorrow ? sec - 1 : sec, needsBorrow ? nsec + 1e9 : nsec];
78+
if (time !== undefined) {
79+
validateArray(time, 'time');
80+
if (time.length !== 2) {
81+
throw new ERR_OUT_OF_RANGE('time', 2, time.length);
8082
}
8183

82-
return [
83-
hrValues[0] * 0x100000000 + hrValues[1],
84-
hrValues[2],
85-
];
84+
const sec = (hrValues[0] * 0x100000000 + hrValues[1]) - time[0];
85+
const nsec = hrValues[2] - time[1];
86+
const needsBorrow = nsec < 0;
87+
return [needsBorrow ? sec - 1 : sec, needsBorrow ? nsec + 1e9 : nsec];
8688
}
8789

88-
// Use a BigUint64Array in the closure because this is actually a bit
89-
// faster than simply returning a BigInt from C++ in V8 7.1.
90-
const hrBigintValues = new BigUint64Array(_hrtime.buffer, 0, 1);
91-
function hrtimeBigInt() {
92-
_hrtime.hrtimeBigInt();
93-
return hrBigintValues[0];
94-
}
90+
return [
91+
hrValues[0] * 0x100000000 + hrValues[1],
92+
hrValues[2],
93+
];
94+
}
9595

96-
return {
97-
hrtime,
98-
hrtimeBigInt,
99-
};
96+
function hrtimeBigInt() {
97+
binding.hrtimeBigInt();
98+
return hrBigintValues[0];
10099
}
101100

102101
// The execution of this function itself should not cause any side effects.
@@ -396,8 +395,10 @@ function toggleTraceCategoryState(asyncHooksEnabled) {
396395

397396
module.exports = {
398397
toggleTraceCategoryState,
399-
getFastAPIs,
400398
assert,
401399
buildAllowedFlags,
402-
wrapProcessMethods
400+
wrapProcessMethods,
401+
hrtime,
402+
hrtimeBigInt,
403+
refreshHrtimeBuffer,
403404
};

src/env-inl.h

+36-9
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@
2929
#include "callback_queue-inl.h"
3030
#include "env.h"
3131
#include "node.h"
32+
#include "node_context_data.h"
33+
#include "node_perf_common.h"
3234
#include "util-inl.h"
3335
#include "uv.h"
36+
#include "v8-fast-api-calls.h"
3437
#include "v8.h"
35-
#include "node_perf_common.h"
36-
#include "node_context_data.h"
3738

3839
#include <cstddef>
3940
#include <cstdint>
@@ -1036,13 +1037,20 @@ inline void Environment::ThrowUVException(int errorno,
10361037
UVException(isolate(), errorno, syscall, message, path, dest));
10371038
}
10381039

1039-
inline v8::Local<v8::FunctionTemplate>
1040-
Environment::NewFunctionTemplate(v8::FunctionCallback callback,
1041-
v8::Local<v8::Signature> signature,
1042-
v8::ConstructorBehavior behavior,
1043-
v8::SideEffectType side_effect_type) {
1044-
return v8::FunctionTemplate::New(isolate(), callback, v8::Local<v8::Value>(),
1045-
signature, 0, behavior, side_effect_type);
1040+
inline v8::Local<v8::FunctionTemplate> Environment::NewFunctionTemplate(
1041+
v8::FunctionCallback callback,
1042+
v8::Local<v8::Signature> signature,
1043+
v8::ConstructorBehavior behavior,
1044+
v8::SideEffectType side_effect_type,
1045+
const v8::CFunction* c_function) {
1046+
return v8::FunctionTemplate::New(isolate(),
1047+
callback,
1048+
v8::Local<v8::Value>(),
1049+
signature,
1050+
0,
1051+
behavior,
1052+
side_effect_type,
1053+
c_function);
10461054
}
10471055

10481056
inline void Environment::SetMethod(v8::Local<v8::Object> that,
@@ -1063,6 +1071,25 @@ inline void Environment::SetMethod(v8::Local<v8::Object> that,
10631071
function->SetName(name_string); // NODE_SET_METHOD() compatibility.
10641072
}
10651073

1074+
inline void Environment::SetFastMethod(v8::Local<v8::Object> that,
1075+
const char* name,
1076+
v8::FunctionCallback slow_callback,
1077+
const v8::CFunction* c_function) {
1078+
v8::Local<v8::Context> context = isolate()->GetCurrentContext();
1079+
v8::Local<v8::Function> function =
1080+
NewFunctionTemplate(slow_callback,
1081+
v8::Local<v8::Signature>(),
1082+
v8::ConstructorBehavior::kThrow,
1083+
v8::SideEffectType::kHasNoSideEffect,
1084+
c_function)
1085+
->GetFunction(context)
1086+
.ToLocalChecked();
1087+
const v8::NewStringType type = v8::NewStringType::kInternalized;
1088+
v8::Local<v8::String> name_string =
1089+
v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked();
1090+
that->Set(context, name_string, function).Check();
1091+
}
1092+
10661093
inline void Environment::SetMethodNoSideEffect(v8::Local<v8::Object> that,
10671094
const char* name,
10681095
v8::FunctionCallback callback) {

src/env.h

+12-8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "handle_wrap.h"
3535
#include "node.h"
3636
#include "node_binding.h"
37+
#include "node_external_reference.h"
3738
#include "node_main_instance.h"
3839
#include "node_options.h"
3940
#include "node_perf_common.h"
@@ -1239,20 +1240,23 @@ class Environment : public MemoryRetainer {
12391240
const char* path = nullptr,
12401241
const char* dest = nullptr);
12411242

1242-
inline v8::Local<v8::FunctionTemplate>
1243-
NewFunctionTemplate(v8::FunctionCallback callback,
1244-
v8::Local<v8::Signature> signature =
1245-
v8::Local<v8::Signature>(),
1246-
v8::ConstructorBehavior behavior =
1247-
v8::ConstructorBehavior::kAllow,
1248-
v8::SideEffectType side_effect =
1249-
v8::SideEffectType::kHasSideEffect);
1243+
inline v8::Local<v8::FunctionTemplate> NewFunctionTemplate(
1244+
v8::FunctionCallback callback,
1245+
v8::Local<v8::Signature> signature = v8::Local<v8::Signature>(),
1246+
v8::ConstructorBehavior behavior = v8::ConstructorBehavior::kAllow,
1247+
v8::SideEffectType side_effect = v8::SideEffectType::kHasSideEffect,
1248+
const v8::CFunction* c_function = nullptr);
12501249

12511250
// Convenience methods for NewFunctionTemplate().
12521251
inline void SetMethod(v8::Local<v8::Object> that,
12531252
const char* name,
12541253
v8::FunctionCallback callback);
12551254

1255+
inline void SetFastMethod(v8::Local<v8::Object> that,
1256+
const char* name,
1257+
v8::FunctionCallback slow_callback,
1258+
const v8::CFunction* c_function);
1259+
12561260
inline void SetProtoMethod(v8::Local<v8::FunctionTemplate> that,
12571261
const char* name,
12581262
v8::FunctionCallback callback);

src/node_external_reference.h

+5
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,22 @@
55

66
#include <cinttypes>
77
#include <vector>
8+
#include "v8-fast-api-calls.h"
89
#include "v8.h"
910

1011
namespace node {
1112

13+
using CFunctionCallback = void (*)(v8::Local<v8::Value> receiver);
14+
1215
// This class manages the external references from the V8 heap
1316
// to the C++ addresses in Node.js.
1417
class ExternalReferenceRegistry {
1518
public:
1619
ExternalReferenceRegistry();
1720

1821
#define ALLOWED_EXTERNAL_REFERENCE_TYPES(V) \
22+
V(CFunctionCallback) \
23+
V(const v8::CFunctionInfo*) \
1924
V(v8::FunctionCallback) \
2025
V(v8::AccessorGetterCallback) \
2126
V(v8::AccessorSetterCallback) \

src/node_process.h

+54
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33

44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

6+
#include "node_snapshotable.h"
7+
#include "v8-fast-api-calls.h"
68
#include "v8.h"
79

810
namespace node {
911

12+
class Environment;
13+
class MemoryTracker;
14+
class ExternalReferenceRegistry;
15+
1016
v8::MaybeLocal<v8::Object> CreateEnvVarProxy(v8::Local<v8::Context> context,
1117
v8::Isolate* isolate);
1218

@@ -38,6 +44,54 @@ v8::Maybe<bool> ProcessEmitDeprecationWarning(Environment* env,
3844
v8::MaybeLocal<v8::Object> CreateProcessObject(Environment* env);
3945
void PatchProcessObject(const v8::FunctionCallbackInfo<v8::Value>& args);
4046

47+
namespace process {
48+
class BindingData : public SnapshotableObject {
49+
public:
50+
void AddMethods();
51+
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
52+
53+
SERIALIZABLE_OBJECT_METHODS()
54+
static constexpr FastStringKey type_name{"node::process::BindingData"};
55+
static constexpr EmbedderObjectType type_int =
56+
EmbedderObjectType::k_process_binding_data;
57+
58+
BindingData(Environment* env, v8::Local<v8::Object> object);
59+
60+
void MemoryInfo(MemoryTracker* tracker) const override;
61+
SET_MEMORY_INFO_NAME(BindingData)
62+
SET_SELF_SIZE(BindingData)
63+
64+
static BindingData* FromV8Value(v8::Local<v8::Value> receiver);
65+
static void NumberImpl(BindingData* receiver);
66+
67+
static void FastNumber(v8::Local<v8::Value> receiver) {
68+
NumberImpl(FromV8Value(receiver));
69+
}
70+
71+
static void SlowNumber(const v8::FunctionCallbackInfo<v8::Value>& args);
72+
73+
static void BigIntImpl(BindingData* receiver);
74+
75+
static void FastBigInt(v8::Local<v8::Value> receiver) {
76+
BigIntImpl(FromV8Value(receiver));
77+
}
78+
79+
static void SlowBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
80+
81+
private:
82+
static constexpr size_t kBufferSize =
83+
std::max(sizeof(uint64_t), sizeof(uint32_t) * 3);
84+
v8::Global<v8::ArrayBuffer> array_buffer_;
85+
std::shared_ptr<v8::BackingStore> backing_store_;
86+
87+
// These need to be static so that we have their addresses available to
88+
// register as external references in the snapshot at environment creation
89+
// time.
90+
static v8::CFunction fast_number_;
91+
static v8::CFunction fast_bigint_;
92+
};
93+
94+
} // namespace process
4195
} // namespace node
4296
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
4397
#endif // SRC_NODE_PROCESS_H_

0 commit comments

Comments
 (0)