New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
src,tools: initialize cppgc #45704
base: main
Are you sure you want to change the base?
src,tools: initialize cppgc #45704
Conversation
|
Review requested: |
src/node_main_instance.cc
Outdated
| WrapperDescriptor( | ||
| BaseObject::kEmbedderType, | ||
| BaseObject::kSlot, | ||
| 0x90df |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A recent commit made kEmbedderType and kSlot the correct values here (8b0e5b19bd0). The embedder id in that commit was specified as 0x90de. When Chromium initializes the CppHeap its embedder id is 0x1 (kEmbedderBlink). Pre-cppgc it used to be the case that for embedder tracing you had to specify the embedder id when you create objects, and it would need to match. With cppgc, you just use cppgc::MakeGarbageCollected<> and I believe it uses the values that were specified when the CppHeap was created. As a result, add-ons using cppgc don't need to be aware of the value chosen by Node. Given though that the reason 0x90de was picked was so that non-cppgc objects don't get mixed up with cppgc objects, we probably want to use a different embedder id than 0x90de which Node is currently using for non-cppgc objects. I just picked the next number here.
| @@ -197,15 +197,56 @@ def files(action): | |||
| def headers(action): | |||
| def wanted_v8_headers(files_arg, dest): | |||
| v8_headers = [ | |||
| 'deps/v8/include/cppgc/allocation.h', | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be better to go back to excluding the headers you don't want to export?
Whilst I'm here though, I'll mention that we actually do make use of the v8-inspector.h and v8-inspector-protocol.h headers, so we have a local patch to include those also. Those were the only headers that used to be in the disallow list (See: 38f3238).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the concern is that we don't want to accidentally expand the API surface. If we have a disallow list we might fail to notice when V8 adds new headers and then breaks the API by removing them. If the list is explicit it helps us notice about changes like this and we can make some fixups or adjust the timing of the V8 upgrade.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the subject enough to have an opinion on whether we should expose all these additional headers, but I'm afraid that doing so will effectively block us from doing semver-minor V8 upgrades in the future.
| @@ -84,6 +88,17 @@ NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data, | |||
| SetIsolateCreateParamsForNode(isolate_params_.get()); | |||
| Isolate::Initialize(isolate_, *isolate_params_); | |||
|
|
|||
| cpp_heap_ = CppHeap::Create( | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this should be optional, or conditional on the earlier kNoInitializeCppgc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it doesn’t make a difference since for NodeMainInstance, we are in control of how the process is initialized. For the code in CommonEnvironmentSetup this is probably a valid question though – I assume this doesn’t work if Node.js was initialized with kNoInitializeCppgc? Does it crash or just do nothing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it crashes.
InitializeProcess sets up the global GC info table: https://github.com/v8/v8/blob/5fe0aa3bc79c0a9d3ad546b79211f07105f09585/src/heap/cppgc/platform.cc#LL63
https://github.com/v8/v8/blob/5fe0aa3bc79c0a9d3ad546b79211f07105f09585/src/heap/cppgc/gc-info-table.cc#LL55
Later code will just assume it's available:
https://github.com/v8/v8/blob/5fe0aa3bc79c0a9d3ad546b79211f07105f09585/src/heap/cppgc/gc-info-table.h#L104-L106
I guess the assumption with kNoInitializeNodeV8Platform is that the embedder will do the required initialization. Here it's going to be similar. But this is new initialization existing embedders will need to perform (if they're explicitly asking node not to).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test for kNoInitializeCppgc, similar to the one in embedtest.cc? I assume doing CppHeap::Create() and Isolate:: AttachCppHeap() right after CommonEnvironmentSetup::Create() would be timely enough? It would be good to know when we add something in CommonEnvironmentSetup::Create() that makes it no longer the case though.
I think for NodeMainInstance() we can probably just do this unconditionally for the add-ons. It does not seem to be necessary for embedders who use NodeMainInstance() and also enable cppgc for add-ons (do they exist?) to customize this part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can add kNoInitializeCppgc to embedtest.cc and have it call cppgc::InitializeProcess manually. CppHeap::Create() and Isolate::AttachCppHeap() happen internally in CommonEnvironmentSetup however. Are you saying you don't want that to be the case?
I think it makes sense, if Node starts allowing embedders to use cppgc, to expect that embedders specifying kNoInitializeCppgc need to manually initialize it, similar to how they are currently required to initialize v8.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for embedders who use
NodeMainInstance()and also enable cppgc for add-ons (do they exist?)
I wouldn’t spend too much time worrying about embedders who don’t stick to the public API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you saying you don't want that to be the case?
I meant that we can condition the CppHeap::Create() and Isolate::AttachCppHeap() calls in CommonEnvironmentSetup::Create() with kNoInitializeCppgc - so, users who set kNoInitializeCppgc would have to call these themselves after CommonEnvironmentSetup::Create(), which I think is what you meant by "Here it's going to be similar"? And if so we should test that doing something like:
auto setup = CommonEnvironmentSetup::Create(platform, errors, args, exec_args, node::EnvironmentFlags::kNoInitializeCppgc);
auto cpp_heap = CppHeap::Create(...);
setup->isolate()->AttachCppHeap(cpp_heap.get());
// Before CommonEnvironmentSetup goes out of scope
setup->isolate()->DetachCppHeap();
cpp_heap->Terminate();is actually working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I misread, there is only one flag for the process initialization, so it's not propagated to environments. So I guess we can just add a flag for cppgc creation to IsolateSettingsFlags, and then do the calls unconditionally in CommonEnvironmentSetup::Create() (this means that the user cannot use CommonEnvironmentSetup if they want to configure the cppgc heap, but that's probably fine).
|
In Electron, the cppgc heap will be initialized by Chromium. I don't think anything in this PR clashes with what Electron will want. When they initialize node, they can use They don't use CC: @codebytere / @nornagon for awareness |
src/api/embed_helpers.cc
Outdated
| WrapperDescriptor( | ||
| BaseObject::kEmbedderType, | ||
| BaseObject::kSlot, | ||
| 0x90df) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would embedders which don’t use CommonEnvironmentSetup have to re-use the same value here? What happens if they call InitializeOncePerProcess() but don’t call isolate->AttachCppHeap() (which is what currently would happen)?
Would it make sense to expose a function to embedders that creates (and attaches?) the CppHeap the same way it’s being done here, then use that here and in NodeMainInstance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the exact value matters a whole lot. In Electron, Chromium will initialize the heap with 0x1. Node addons making use of cppgc don't need to specify the value, it's automatically set by cppgc::MakeGarbageCollected.
What does matter, is that it doesn't match the value used in the embedder fields for non-cppgc objects. That was why 8b0e5b19bd0 updated the value in the first place.
We could certainly expose a method that uses this fixed value that embedders can use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question is: where? It could potentially be something extra done in SetIsolateUpForNode (that is called by Electron, so I guess it would need to be optionally done based on something in IsolateSettings). Or it could be a new method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that is called by Electron, so I guess it would need to be optionally done based on something in
IsolateSettings
I think that would seem okay to me IsolateSettings was added specifically so that Electron can customize this a bit more iirc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One issue with this is that the CppHeap needs to be owned by someone. With the changes I have thus far, it's either owned by CommonEnvironmentSetup::Impl, or NodeMainInstance.
SetIsolateUpForNode doesn't have any place to own the CppHeap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we could make it part of the MultiIsolatePlatform’s job to attach/detach the cpp heap in RegisterIsolate/UnregisterIsolate? And if we need this behavior to be toggle-able in the default implementation, we could add an extra argument to MultiIsolatePlatform::Create()…
| @@ -84,6 +88,17 @@ NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data, | |||
| SetIsolateCreateParamsForNode(isolate_params_.get()); | |||
| Isolate::Initialize(isolate_, *isolate_params_); | |||
|
|
|||
| cpp_heap_ = CppHeap::Create( | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it doesn’t make a difference since for NodeMainInstance, we are in control of how the process is initialized. For the code in CommonEnvironmentSetup this is probably a valid question though – I assume this doesn’t work if Node.js was initialized with kNoInitializeCppgc? Does it crash or just do nothing?
|
FYI I believe we don't currently initialize cppgc in the main process in Electron (though Blink does of course initialize it). So we would want to be able to initialize Node with cppgc initialization in the main process (either by doing it ourselves or through this initialization option), and without cppgc initialization in the renderer process (since that would clash with Blink's initialization). |
|
Erp, I hit the close button by mistake. Please re-approve running the workflows >_< |
|
The previous test runs seemed to fail, and actually running my local build of node likewise fails. It seems to be due to a bug in v8: https://github.com/v8/v8/blob/main/src/heap/cppgc/heap-base.cc#L104 I believe this should be CC: @mlippautz |
Yep, changing that fixes my local build. |
| @@ -84,6 +88,17 @@ NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data, | |||
| SetIsolateCreateParamsForNode(isolate_params_.get()); | |||
| Isolate::Initialize(isolate_, *isolate_params_); | |||
|
|
|||
| cpp_heap_ = CppHeap::Create( | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test for kNoInitializeCppgc, similar to the one in embedtest.cc? I assume doing CppHeap::Create() and Isolate:: AttachCppHeap() right after CommonEnvironmentSetup::Create() would be timely enough? It would be good to know when we add something in CommonEnvironmentSetup::Create() that makes it no longer the case though.
I think for NodeMainInstance() we can probably just do this unconditionally for the add-ons. It does not seem to be necessary for embedders who use NodeMainInstance() and also enable cppgc for add-ons (do they exist?) to customize this part.
| @@ -197,15 +197,56 @@ def files(action): | |||
| def headers(action): | |||
| def wanted_v8_headers(files_arg, dest): | |||
| v8_headers = [ | |||
| 'deps/v8/include/cppgc/allocation.h', | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the concern is that we don't want to accidentally expand the API surface. If we have a disallow list we might fail to notice when V8 adds new headers and then breaks the API by removing them. If the list is explicit it helps us notice about changes like this and we can make some fixups or adjust the timing of the V8 upgrade.
Initialize cppgc, create a CppHeap, and attach it to the Isolate. This allows C++ addons to start using cppgc to manage objects. Refs: nodejs#40786
I added a second commit to this PR which fixes the above. We can switch the commit to cherry-pick the v8 commit once it gets fixed there. Hopefully the CI checks will proceed now. |
|
Node is still segfaulting when I run it locally: It looks like |
|
Looks like there's no After digging a bit, it's because CC: @mlippautz |
|
Submitted the following v8 bugs: |
When cppgc is initialized without a page allocator, the platform held by HeapBase is not an instance of 'CppgcPlatformAdapter', so it's invalid to assume that it is. The assumption was made for setting the isolate, so this commit adds a method to the protocol for setting the isolate so it can be fowarded through to the underlying platform correctly.
|
I added a commit to add |
|
Please re-approve running the workflows |


Initialize cppgc, create a CppHeap, and attach it to the Isolate.
This allows C++ addons to start using cppgc to manage objects.
Refs: #40786