Skip to content

Commit 621c4d6

Browse files
legendecasUlisesGascon
authored andcommitted
src: make process binding data weak
Avoid the realm being strongly referenced by the process binding data. PR-URL: #48655 Backport-PR-URL: #51239 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent 9898140 commit 621c4d6

File tree

3 files changed

+52
-33
lines changed

3 files changed

+52
-33
lines changed

lib/internal/process/per_thread.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ const {
2828
StringPrototypeStartsWith,
2929
Symbol,
3030
SymbolIterator,
31-
Uint32Array,
3231
} = primordials;
3332

3433
const {
@@ -65,10 +64,10 @@ function refreshHrtimeBuffer() {
6564
// The 3 entries filled in by the original process.hrtime contains
6665
// the upper/lower 32 bits of the second part of the value,
6766
// and the remaining nanoseconds of the value.
68-
hrValues = new Uint32Array(binding.hrtimeBuffer);
67+
hrValues = binding.hrtimeBuffer;
6968
// Use a BigUint64Array in the closure because this is actually a bit
7069
// faster than simply returning a BigInt from C++ in V8 7.1.
71-
hrBigintValues = new BigUint64Array(binding.hrtimeBuffer, 0, 1);
70+
hrBigintValues = new BigUint64Array(binding.hrtimeBuffer.buffer, 0, 1);
7271
}
7372

7473
// Create the buffers.

src/node_process.h

+11-7
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,20 @@ void PatchProcessObject(const v8::FunctionCallbackInfo<v8::Value>& args);
4848
namespace process {
4949
class BindingData : public SnapshotableObject {
5050
public:
51+
struct InternalFieldInfo : public node::InternalFieldInfoBase {
52+
AliasedBufferIndex hrtime_buffer;
53+
};
54+
5155
static void AddMethods(v8::Isolate* isolate,
5256
v8::Local<v8::ObjectTemplate> target);
5357
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
5458

55-
using InternalFieldInfo = InternalFieldInfoBase;
56-
5759
SERIALIZABLE_OBJECT_METHODS()
5860
SET_BINDING_ID(process_binding_data)
5961

60-
BindingData(Realm* realm, v8::Local<v8::Object> object);
62+
BindingData(Realm* realm,
63+
v8::Local<v8::Object> object,
64+
InternalFieldInfo* info = nullptr);
6165

6266
void MemoryInfo(MemoryTracker* tracker) const override;
6367
SET_MEMORY_INFO_NAME(BindingData)
@@ -81,10 +85,10 @@ class BindingData : public SnapshotableObject {
8185
static void SlowBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
8286

8387
private:
84-
static constexpr size_t kBufferSize =
85-
std::max(sizeof(uint64_t), sizeof(uint32_t) * 3);
86-
v8::Global<v8::ArrayBuffer> array_buffer_;
87-
std::shared_ptr<v8::BackingStore> backing_store_;
88+
// Buffer length in uint32.
89+
static constexpr size_t kHrTimeBufferLength = 3;
90+
AliasedUint32Array hrtime_buffer_;
91+
InternalFieldInfo* internal_field_info_ = nullptr;
8892

8993
// These need to be static so that we have their addresses available to
9094
// register as external references in the snapshot at environment creation

src/node_process_methods.cc

+39-23
Original file line numberDiff line numberDiff line change
@@ -465,15 +465,29 @@ static void ReallyExit(const FunctionCallbackInfo<Value>& args) {
465465

466466
namespace process {
467467

468-
BindingData::BindingData(Realm* realm, v8::Local<v8::Object> object)
469-
: SnapshotableObject(realm, object, type_int) {
468+
BindingData::BindingData(Realm* realm,
469+
v8::Local<v8::Object> object,
470+
InternalFieldInfo* info)
471+
: SnapshotableObject(realm, object, type_int),
472+
hrtime_buffer_(realm->isolate(),
473+
kHrTimeBufferLength,
474+
MAYBE_FIELD_PTR(info, hrtime_buffer)) {
470475
Isolate* isolate = realm->isolate();
471476
Local<Context> context = realm->context();
472-
Local<ArrayBuffer> ab = ArrayBuffer::New(isolate, kBufferSize);
473-
array_buffer_.Reset(isolate, ab);
474-
object->Set(context, FIXED_ONE_BYTE_STRING(isolate, "hrtimeBuffer"), ab)
475-
.ToChecked();
476-
backing_store_ = ab->GetBackingStore();
477+
478+
if (info == nullptr) {
479+
object
480+
->Set(context,
481+
FIXED_ONE_BYTE_STRING(isolate, "hrtimeBuffer"),
482+
hrtime_buffer_.GetJSArray())
483+
.ToChecked();
484+
} else {
485+
hrtime_buffer_.Deserialize(realm->context());
486+
}
487+
488+
// The hrtime buffer is referenced from the binding data js object.
489+
// Make the native handle weak to avoid keeping the realm alive.
490+
hrtime_buffer_.MakeWeak();
477491
}
478492

479493
v8::CFunction BindingData::fast_number_(v8::CFunction::Make(FastNumber));
@@ -503,7 +517,7 @@ BindingData* BindingData::FromV8Value(Local<Value> value) {
503517
}
504518

505519
void BindingData::MemoryInfo(MemoryTracker* tracker) const {
506-
tracker->TrackField("array_buffer", array_buffer_);
520+
tracker->TrackField("hrtime_buffer", hrtime_buffer_);
507521
}
508522

509523
// This is the legacy version of hrtime before BigInt was introduced in
@@ -516,20 +530,19 @@ void BindingData::MemoryInfo(MemoryTracker* tracker) const {
516530
// because there is no Uint64Array in JS.
517531
// The third entry contains the remaining nanosecond part of the value.
518532
void BindingData::NumberImpl(BindingData* receiver) {
519-
// Make sure we don't accidentally access buffers wiped for snapshot.
520-
CHECK(!receiver->array_buffer_.IsEmpty());
521533
uint64_t t = uv_hrtime();
522-
uint32_t* fields = static_cast<uint32_t*>(receiver->backing_store_->Data());
523-
fields[0] = (t / NANOS_PER_SEC) >> 32;
524-
fields[1] = (t / NANOS_PER_SEC) & 0xffffffff;
525-
fields[2] = t % NANOS_PER_SEC;
534+
receiver->hrtime_buffer_[0] = (t / NANOS_PER_SEC) >> 32;
535+
receiver->hrtime_buffer_[1] = (t / NANOS_PER_SEC) & 0xffffffff;
536+
receiver->hrtime_buffer_[2] = t % NANOS_PER_SEC;
526537
}
527538

528539
void BindingData::BigIntImpl(BindingData* receiver) {
529-
// Make sure we don't accidentally access buffers wiped for snapshot.
530-
CHECK(!receiver->array_buffer_.IsEmpty());
531540
uint64_t t = uv_hrtime();
532-
uint64_t* fields = static_cast<uint64_t*>(receiver->backing_store_->Data());
541+
// The buffer is a Uint32Array, so we need to reinterpret it as a
542+
// Uint64Array to write the value. The buffer is valid at this scope so we
543+
// can safely cast away the constness.
544+
uint64_t* fields = reinterpret_cast<uint64_t*>(
545+
const_cast<uint32_t*>(receiver->hrtime_buffer_.GetNativeBuffer()));
533546
fields[0] = t;
534547
}
535548

@@ -543,18 +556,19 @@ void BindingData::SlowNumber(const v8::FunctionCallbackInfo<v8::Value>& args) {
543556

544557
bool BindingData::PrepareForSerialization(Local<Context> context,
545558
v8::SnapshotCreator* creator) {
546-
// It's not worth keeping.
547-
// Release it, we will recreate it when the instance is dehydrated.
548-
array_buffer_.Reset();
559+
DCHECK_NULL(internal_field_info_);
560+
internal_field_info_ = InternalFieldInfoBase::New<InternalFieldInfo>(type());
561+
internal_field_info_->hrtime_buffer =
562+
hrtime_buffer_.Serialize(context, creator);
549563
// Return true because we need to maintain the reference to the binding from
550564
// JS land.
551565
return true;
552566
}
553567

554568
InternalFieldInfoBase* BindingData::Serialize(int index) {
555569
DCHECK_IS_SNAPSHOT_SLOT(index);
556-
InternalFieldInfo* info =
557-
InternalFieldInfoBase::New<InternalFieldInfo>(type());
570+
InternalFieldInfo* info = internal_field_info_;
571+
internal_field_info_ = nullptr;
558572
return info;
559573
}
560574

@@ -566,7 +580,9 @@ void BindingData::Deserialize(Local<Context> context,
566580
v8::HandleScope scope(context->GetIsolate());
567581
Realm* realm = Realm::GetCurrent(context);
568582
// Recreate the buffer in the constructor.
569-
BindingData* binding = realm->AddBindingData<BindingData>(holder);
583+
InternalFieldInfo* casted_info = static_cast<InternalFieldInfo*>(info);
584+
BindingData* binding =
585+
realm->AddBindingData<BindingData>(holder, casted_info);
570586
CHECK_NOT_NULL(binding);
571587
}
572588

0 commit comments

Comments
 (0)