Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7f2c810

Browse filesBrowse files
src,tools: initialize cppgc
This patch: - Initializes cppgc in InitializeOncePerProcess() when kNoInitializeCppgc is not set - Create a CppHeap and attach it to the Isolate when there isn't one already during IsolateData initialization. The CppHeap is detached and terminated when IsolateData is freed. - Publishes the cppgc headers in the tarball. This allows C++ addons to start using cppgc to manage objects. A helper node::SetCppgcReference() is also added to help addons enable cppgc tracing in a user-defined object. Co-authored-by: Joyee Cheung <joyeec9h3@gmail.com> Refs: #40786 PR-URL: #45704 Refs: https://docs.google.com/document/d/1ny2Qz_EsUnXGKJRGxoA-FXIE2xpLgaMAN6jD7eAkqFQ/edit Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
1 parent 7bbcb29 commit 7f2c810
Copy full SHA for 7f2c810

File tree

Expand file treeCollapse file tree

11 files changed

+290
-3
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

11 files changed

+290
-3
lines changed
Open diff view settings
Collapse file

‎src/env-inl.h‎

Copy file name to clipboardExpand all lines: src/env-inl.h
+26Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "node_realm-inl.h"
3535
#include "util-inl.h"
3636
#include "uv.h"
37+
#include "v8-cppgc.h"
3738
#include "v8.h"
3839

3940
#include <cstddef>
@@ -61,6 +62,31 @@ inline uv_loop_t* IsolateData::event_loop() const {
6162
return event_loop_;
6263
}
6364

65+
inline void IsolateData::SetCppgcReference(v8::Isolate* isolate,
66+
v8::Local<v8::Object> object,
67+
void* wrappable) {
68+
v8::CppHeap* heap = isolate->GetCppHeap();
69+
CHECK_NOT_NULL(heap);
70+
v8::WrapperDescriptor descriptor = heap->wrapper_descriptor();
71+
uint16_t required_size = std::max(descriptor.wrappable_instance_index,
72+
descriptor.wrappable_type_index);
73+
CHECK_GT(object->InternalFieldCount(), required_size);
74+
75+
uint16_t* id_ptr = nullptr;
76+
{
77+
Mutex::ScopedLock lock(isolate_data_mutex_);
78+
auto it =
79+
wrapper_data_map_.find(descriptor.embedder_id_for_garbage_collected);
80+
CHECK_NE(it, wrapper_data_map_.end());
81+
id_ptr = &(it->second->cppgc_id);
82+
}
83+
84+
object->SetAlignedPointerInInternalField(descriptor.wrappable_type_index,
85+
id_ptr);
86+
object->SetAlignedPointerInInternalField(descriptor.wrappable_instance_index,
87+
wrappable);
88+
}
89+
6490
inline uint16_t* IsolateData::embedder_id_for_cppgc() const {
6591
return &(wrapper_data_->cppgc_id);
6692
}
Collapse file

‎src/env.cc‎

Copy file name to clipboardExpand all lines: src/env.cc
+26Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ using errors::TryCatchScope;
3737
using v8::Array;
3838
using v8::Boolean;
3939
using v8::Context;
40+
using v8::CppHeap;
41+
using v8::CppHeapCreateParams;
4042
using v8::EmbedderGraph;
4143
using v8::EscapableHandleScope;
4244
using v8::Function;
@@ -61,6 +63,7 @@ using v8::TracingController;
6163
using v8::TryCatch;
6264
using v8::Undefined;
6365
using v8::Value;
66+
using v8::WrapperDescriptor;
6467
using worker::Worker;
6568

6669
int const ContextEmbedderTag::kNodeContextTag = 0x6e6f64;
@@ -538,6 +541,14 @@ IsolateData::IsolateData(Isolate* isolate,
538541
// for embedder ID, V8 could accidentally enable cppgc on them. So
539542
// safe guard against this.
540543
DCHECK_NE(descriptor.wrappable_type_index, BaseObject::kSlot);
544+
} else {
545+
cpp_heap_ = CppHeap::Create(
546+
platform,
547+
CppHeapCreateParams{
548+
{},
549+
WrapperDescriptor(
550+
BaseObject::kEmbedderType, BaseObject::kSlot, cppgc_id)});
551+
isolate->AttachCppHeap(cpp_heap_.get());
541552
}
542553
// We do not care about overflow since we just want this to be different
543554
// from the cppgc id.
@@ -565,6 +576,21 @@ IsolateData::IsolateData(Isolate* isolate,
565576
}
566577
}
567578

579+
IsolateData::~IsolateData() {
580+
if (cpp_heap_ != nullptr) {
581+
// The CppHeap must be detached before being terminated.
582+
isolate_->DetachCppHeap();
583+
cpp_heap_->Terminate();
584+
}
585+
}
586+
587+
// Public API
588+
void SetCppgcReference(Isolate* isolate,
589+
Local<Object> object,
590+
void* wrappable) {
591+
IsolateData::SetCppgcReference(isolate, object, wrappable);
592+
}
593+
568594
void IsolateData::MemoryInfo(MemoryTracker* tracker) const {
569595
#define V(PropertyName, StringValue) \
570596
tracker->TrackField(#PropertyName, PropertyName());
Collapse file

‎src/env.h‎

Copy file name to clipboardExpand all lines: src/env.h
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
#include <unordered_set>
6363
#include <vector>
6464

65+
namespace v8 {
66+
class CppHeap;
67+
}
68+
6569
namespace node {
6670

6771
namespace shadow_realm {
@@ -136,6 +140,7 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
136140
MultiIsolatePlatform* platform = nullptr,
137141
ArrayBufferAllocator* node_allocator = nullptr,
138142
const SnapshotData* snapshot_data = nullptr);
143+
~IsolateData();
139144

140145
SET_MEMORY_INFO_NAME(IsolateData)
141146
SET_SELF_SIZE(IsolateData)
@@ -148,6 +153,10 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
148153
uint16_t* embedder_id_for_cppgc() const;
149154
uint16_t* embedder_id_for_non_cppgc() const;
150155

156+
static inline void SetCppgcReference(v8::Isolate* isolate,
157+
v8::Local<v8::Object> object,
158+
void* wrappable);
159+
151160
inline uv_loop_t* event_loop() const;
152161
inline MultiIsolatePlatform* platform() const;
153162
inline const SnapshotData* snapshot_data() const;
@@ -229,6 +238,7 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
229238
NodeArrayBufferAllocator* const node_allocator_;
230239
MultiIsolatePlatform* platform_;
231240
const SnapshotData* snapshot_data_;
241+
std::unique_ptr<v8::CppHeap> cpp_heap_;
232242
std::shared_ptr<PerIsolateOptions> options_;
233243
worker::Worker* worker_context_ = nullptr;
234244
bool is_building_snapshot_ = false;
Collapse file

‎src/node.cc‎

Copy file name to clipboardExpand all lines: src/node.cc
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
#endif // NODE_USE_V8_PLATFORM
6363
#include "v8-profiler.h"
6464

65+
#include "cppgc/platform.h"
66+
6567
#if HAVE_INSPECTOR
6668
#include "inspector/worker_inspector.h" // ParentInspectorHandle
6769
#endif
@@ -1096,6 +1098,14 @@ InitializeOncePerProcessInternal(const std::vector<std::string>& args,
10961098
V8::Initialize();
10971099
}
10981100

1101+
if (!(flags & ProcessInitializationFlags::kNoInitializeCppgc)) {
1102+
v8::PageAllocator* allocator = nullptr;
1103+
if (result->platform_ != nullptr) {
1104+
allocator = result->platform_->GetPageAllocator();
1105+
}
1106+
cppgc::InitializeProcess(allocator);
1107+
}
1108+
10991109
performance::performance_v8_start = PERFORMANCE_NOW();
11001110
per_process::v8_initialized = true;
11011111

@@ -1115,6 +1125,10 @@ void TearDownOncePerProcess() {
11151125
ResetSignalHandlers();
11161126
}
11171127

1128+
if (!(flags & ProcessInitializationFlags::kNoInitializeCppgc)) {
1129+
cppgc::ShutdownProcess();
1130+
}
1131+
11181132
per_process::v8_initialized = false;
11191133
if (!(flags & ProcessInitializationFlags::kNoInitializeV8)) {
11201134
V8::Dispose();
Collapse file

‎src/node.h‎

Copy file name to clipboardExpand all lines: src/node.h
+24-1Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,10 @@ enum Flags : uint32_t {
261261
kNoUseLargePages = 1 << 11,
262262
// Skip printing output for --help, --version, --v8-options.
263263
kNoPrintHelpOrVersionOutput = 1 << 12,
264+
// Do not perform cppgc initialization. If set, the embedder must call
265+
// cppgc::InitializeProcess() before creating a Node.js environment
266+
// and call cppgc::ShutdownProcess() before process shutdown.
267+
kNoInitializeCppgc = 1 << 13,
264268

265269
// Emulate the behavior of InitializeNodeWithArgs() when passing
266270
// a flags argument to the InitializeOncePerProcess() replacement
@@ -269,7 +273,7 @@ enum Flags : uint32_t {
269273
kNoStdioInitialization | kNoDefaultSignalHandling | kNoInitializeV8 |
270274
kNoInitializeNodeV8Platform | kNoInitOpenSSL |
271275
kNoParseGlobalDebugVariables | kNoAdjustResourceLimits |
272-
kNoUseLargePages | kNoPrintHelpOrVersionOutput,
276+
kNoUseLargePages | kNoPrintHelpOrVersionOutput | kNoInitializeCppgc,
273277
};
274278
} // namespace ProcessInitializationFlags
275279
namespace ProcessFlags = ProcessInitializationFlags; // Legacy alias.
@@ -1486,6 +1490,25 @@ void RegisterSignalHandler(int signal,
14861490
bool reset_handler = false);
14871491
#endif // _WIN32
14881492

1493+
// Configure the layout of the JavaScript object with a cppgc::GarbageCollected
1494+
// instance so that when the JavaScript object is reachable, the garbage
1495+
// collected instance would have its Trace() method invoked per the cppgc
1496+
// contract. To make it work, the process must have called
1497+
// cppgc::InitializeProcess() before, which is usually the case for addons
1498+
// loaded by the stand-alone Node.js executable. Embedders of Node.js can use
1499+
// either need to call it themselves or make sure that
1500+
// ProcessInitializationFlags::kNoInitializeCppgc is *not* set for cppgc to
1501+
// work.
1502+
// If the CppHeap is owned by Node.js, which is usually the case for addon,
1503+
// the object must be created with at least two internal fields available,
1504+
// and the first two internal fields would be configured by Node.js.
1505+
// This may be superseded by a V8 API in the future, see
1506+
// https://bugs.chromium.org/p/v8/issues/detail?id=13960. Until then this
1507+
// serves as a helper for Node.js isolates.
1508+
NODE_EXTERN void SetCppgcReference(v8::Isolate* isolate,
1509+
v8::Local<v8::Object> object,
1510+
void* wrappable);
1511+
14891512
} // namespace node
14901513

14911514
#endif // SRC_NODE_H_
Collapse file

‎src/node_main_instance.cc‎

Copy file name to clipboardExpand all lines: src/node_main_instance.cc
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ NodeMainInstance::~NodeMainInstance() {
6868
return;
6969
}
7070
// This should only be done on a main instance that owns its isolate.
71+
// IsolateData must be freed before UnregisterIsolate() is called.
72+
isolate_data_.reset();
7173
platform_->UnregisterIsolate(isolate_);
7274
isolate_->Dispose();
7375
}
Collapse file

‎src/node_worker.cc‎

Copy file name to clipboardExpand all lines: src/node_worker.cc
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "node_snapshot_builder.h"
1212
#include "permission/permission.h"
1313
#include "util-inl.h"
14+
#include "v8-cppgc.h"
1415

1516
#include <memory>
1617
#include <string>
Collapse file
+78Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#include <cppgc/allocation.h>
2+
#include <cppgc/garbage-collected.h>
3+
#include <cppgc/heap.h>
4+
#include <node.h>
5+
#include <v8-cppgc.h>
6+
#include <v8.h>
7+
#include <algorithm>
8+
9+
class CppGCed : public cppgc::GarbageCollected<CppGCed> {
10+
public:
11+
static uint16_t states[2];
12+
static constexpr int kDestructCount = 0;
13+
static constexpr int kTraceCount = 1;
14+
15+
static void New(const v8::FunctionCallbackInfo<v8::Value>& args) {
16+
v8::Isolate* isolate = args.GetIsolate();
17+
v8::Local<v8::Object> js_object = args.This();
18+
CppGCed* gc_object = cppgc::MakeGarbageCollected<CppGCed>(
19+
isolate->GetCppHeap()->GetAllocationHandle());
20+
node::SetCppgcReference(isolate, js_object, gc_object);
21+
args.GetReturnValue().Set(js_object);
22+
}
23+
24+
static v8::Local<v8::Function> GetConstructor(
25+
v8::Local<v8::Context> context) {
26+
auto ft = v8::FunctionTemplate::New(context->GetIsolate(), New);
27+
auto ot = ft->InstanceTemplate();
28+
v8::WrapperDescriptor descriptor =
29+
context->GetIsolate()->GetCppHeap()->wrapper_descriptor();
30+
uint16_t required_size = std::max(descriptor.wrappable_instance_index,
31+
descriptor.wrappable_type_index);
32+
ot->SetInternalFieldCount(required_size + 1);
33+
return ft->GetFunction(context).ToLocalChecked();
34+
}
35+
36+
CppGCed() = default;
37+
38+
~CppGCed() { states[kDestructCount]++; }
39+
40+
void Trace(cppgc::Visitor* visitor) const { states[kTraceCount]++; }
41+
};
42+
43+
uint16_t CppGCed::states[] = {0, 0};
44+
45+
void InitModule(v8::Local<v8::Object> exports) {
46+
v8::Isolate* isolate = v8::Isolate::GetCurrent();
47+
auto context = isolate->GetCurrentContext();
48+
49+
auto store = v8::ArrayBuffer::NewBackingStore(
50+
CppGCed::states,
51+
sizeof(uint16_t) * 2,
52+
[](void*, size_t, void*) {},
53+
nullptr);
54+
auto ab = v8::ArrayBuffer::New(isolate, std::move(store));
55+
56+
exports
57+
->Set(context,
58+
v8::String::NewFromUtf8(isolate, "CppGCed").ToLocalChecked(),
59+
CppGCed::GetConstructor(context))
60+
.FromJust();
61+
exports
62+
->Set(context,
63+
v8::String::NewFromUtf8(isolate, "states").ToLocalChecked(),
64+
v8::Uint16Array::New(ab, 0, 2))
65+
.FromJust();
66+
exports
67+
->Set(context,
68+
v8::String::NewFromUtf8(isolate, "kDestructCount").ToLocalChecked(),
69+
v8::Integer::New(isolate, CppGCed::kDestructCount))
70+
.FromJust();
71+
exports
72+
->Set(context,
73+
v8::String::NewFromUtf8(isolate, "kTraceCount").ToLocalChecked(),
74+
v8::Integer::New(isolate, CppGCed::kTraceCount))
75+
.FromJust();
76+
}
77+
78+
NODE_MODULE(NODE_GYP_MODULE_NAME, InitModule)
Collapse file
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
'targets': [
3+
{
4+
'target_name': 'binding',
5+
'sources': [ 'binding.cc' ],
6+
'includes': ['../common.gypi'],
7+
}
8+
]
9+
}
Collapse file

‎test/addons/cppgc-object/test.js‎

Copy file name to clipboard
+51Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
3+
// Flags: --expose-gc
4+
5+
const common = require('../../common');
6+
7+
// Verify that addons can create GarbageCollected objects and
8+
// have them traced properly.
9+
10+
const assert = require('assert');
11+
const {
12+
CppGCed, states, kDestructCount, kTraceCount,
13+
} = require(`./build/${common.buildType}/binding`);
14+
15+
assert.strictEqual(states[kDestructCount], 0);
16+
assert.strictEqual(states[kTraceCount], 0);
17+
18+
let array = [];
19+
const count = 100;
20+
for (let i = 0; i < count; ++i) {
21+
array.push(new CppGCed());
22+
}
23+
24+
globalThis.gc();
25+
26+
setTimeout(async function() {
27+
// GC should have invoked Trace() on at least some of the CppGCed objects,
28+
// but they should all be alive at this point.
29+
assert.strictEqual(states[kDestructCount], 0);
30+
assert.notStrictEqual(states[kTraceCount], 0);
31+
32+
// Replace the old CppGCed objects with new ones, after GC we should have
33+
// destructed all the old ones and called Trace() on the
34+
// new ones.
35+
for (let i = 0; i < count; ++i) {
36+
array[i] = new CppGCed();
37+
}
38+
await common.gcUntil(
39+
'All old CppGCed are destroyed',
40+
() => states[kDestructCount] === count,
41+
);
42+
// Release all the CppGCed objects, after GC we should have destructed
43+
// all of them.
44+
array = null;
45+
globalThis.gc();
46+
47+
await common.gcUntil(
48+
'All old CppGCed are destroyed',
49+
() => states[kDestructCount] === count * 2,
50+
);
51+
}, 1);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.