@@ -1113,13 +1113,13 @@ class MyWrap final : CPPGC_MIXIN(MyWrap) {
11131113```
11141114
11151115If the wrapper needs to perform cleanups when it's destroyed and that
1116- cleanup relies on a living Node.js ` Environment ` , it should implement a
1116+ cleanup relies on a living Node.js ` Realm ` , it should implement a
11171117pattern like this:
11181118
11191119``` cpp
1120- ~MyWrap () { this->Clean (); }
1121- void CleanEnvResource(Environment * env) override {
1122- // Do cleanup that relies on a living Environemnt .
1120+ ~MyWrap () { this->Finalize (); }
1121+ void Clean(Realm * env) override {
1122+ // Do cleanup that relies on a living Realm .
11231123 }
11241124```
11251125
@@ -1277,6 +1277,17 @@ referrer->Set(
12771277).ToLocalChecked();
12781278```
12791279
1280+ #### Creating references between cppgc-managed objects and ` BaseObject ` s
1281+
1282+ This is currently unsupported with the existing helpers. If this has
1283+ to be done, new helpers must be implemented first. Consult the cppgc
1284+ headers when trying to implement it.
1285+
1286+ Another way to work around it is to always do the migration bottom-to-top.
1287+ If a cppgc-managed object needs to reference a ` BaseObject ` , convert
1288+ that ` BaseObject ` to be cppgc-managed first, and then use ` cppgc::Member `
1289+ to create the references.
1290+
12801291#### Lifetime and cleanups of cppgc-managed objects
12811292
12821293Typically, a newly created cppgc-managed wrapper object should be held alive
@@ -1285,31 +1296,57 @@ staying alive in a closure). Long-lived cppgc objects can also
12851296be held alive from C++ using persistent handles (see
12861297` deps/v8/include/cppgc/persistent.h ` ) or as members of other living
12871298cppgc-managed objects (see ` deps/v8/include/cppgc/member.h ` ) if necessary.
1288- Its destructor will be called when no other objects from the V8 heap reference
1289- it, this can happen at any time after the garbage collector notices that
1290- it's no longer reachable and before the V8 isolate is torn down.
1291- See the [ Oilpan documentation in Chromium] [ ] for more details.
1292-
1293- If the cppgc-managed objects does not need to perform any special cleanup,
1294- it's fine to use the default destructor. If it needs to perform only trivial
1295- cleanup that only affects its own members without calling into JS, potentially
1296- triggering garbage collection or relying on a living ` Environment ` , then it's
1297- fine to just implement the trivial cleanup in the destructor directly. If it
1298- needs to do any substantial cleanup that involves a living ` Environment ` , because
1299- the destructor can be called at any time by the garbage collection, even after
1300- the ` Environment ` is already gone, it must implement the cleanup with this pattern:
13011299
1302- ``` cpp
1303- ~MyWrap () { this->Clean(); }
1304- void CleanEnvResource(Environment* env) override {
1305- // Do cleanup that relies on a living Environemnt. This would be
1306- // called by CppgcMixin::Clean() first during Environment shutdown,
1307- // while the Environment is still alive. If the destructor calls
1308- // Clean() again later during garbage collection that happens after
1309- // Environment shutdown, CleanEnvResource() would be skipped, preventing
1310- // invalid access to the Environment.
1311- }
1312- ```
1300+ When a cppgc-managed object is no longer reachable in the heap, its destructor
1301+ will be invoked by the garbage collection, which can happen after the ` Realm `
1302+ is already gone, or after any object it references is gone. It is therefore
1303+ unsafe to invoke V8 APIs directly in the destructors. To ensure safety,
1304+ the cleanups of a cppgc-managed object should adhere to different patterns,
1305+ depending on what it needs to do:
1306+
1307+ 1 . If it does not need to do any non-trivial cleanup, nor does its members, just use
1308+ the default destructor. Note that cleanup of ` v8::TracedReference ` and
1309+ ` cppgc::Member ` are already handled automatically by V8 so if they are all the
1310+ non-trivial members the class has, this case applies.
1311+ 2 . If the cleanup relies on a living ` Realm ` , but does not need to access V8
1312+ APIs, the class should use this pattern in its class body:
1313+
1314+ ``` cpp
1315+ ~MyWrap () { this->Finalize(); }
1316+ void Clean(Realm* env) override {
1317+ // Do cleanup that relies on a living Realm. This would be
1318+ // called by CppgcMixin::Finalize() first during Realm shutdown,
1319+ // while the Realm is still alive. If the destructor calls
1320+ // Finalize() again later during garbage collection that happens after
1321+ // Realm shutdown, Clean() would be skipped, preventing
1322+ // invalid access to the Realm.
1323+ }
1324+ ```
1325+
1326+ If implementers want to call `Finalize()` from `Clean()` again, they
1327+ need to make sure that calling `Clean()` recursively is safe.
1328+ 3. If the cleanup relies on access to the V8 heap, including using any V8
1329+ handles, in addition to 2, it should use the `CPPGC_USING_PRE_FINALIZER`
1330+ macro (from the [`cppgc/prefinalizer.h` header][]) in the private
1331+ section of its class body:
1332+
1333+ ```cpp
1334+ private:
1335+ CPPGC_USING_PRE_FINALIZER(MyWrap, Finalize);
1336+ ```
1337+
1338+ Both the destructor and the pre-finalizer are always called on the thread
1339+ in which the object is created.
1340+
1341+ It's worth noting that the use of pre-finalizers would have a negative impact
1342+ on the garbage collection performance as V8 needs to scan all of them during
1343+ each sweeping. If the object is expected to be created frequently in large
1344+ amounts in the application, it's better to avoid access to the V8 heap in its
1345+ cleanup to avoid having to use a pre-finalizer.
1346+
1347+ For more information about the cleanup of cppgc-managed objects and
1348+ what can be done in a pre-finalizer, see the [ cppgc documentation] [ ] and
1349+ the [ ` cppgc/prefinalizer.h ` header] [ ] .
13131350
13141351### Callback scopes
13151352
@@ -1436,6 +1473,7 @@ static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
14361473[ `async_hooks` module ] : https://nodejs.org/api/async_hooks.html
14371474[ `async_wrap.h` ] : async_wrap.h
14381475[ `base_object.h` ] : base_object.h
1476+ [ `cppgc/prefinalizer.h` header ] : ../deps/v8/include/cppgc/prefinalizer.h
14391477[ `handle_wrap.h` ] : handle_wrap.h
14401478[ `memory_tracker.h` ] : memory_tracker.h
14411479[ `req_wrap.h` ] : req_wrap.h
@@ -1446,6 +1484,7 @@ static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
14461484[ `vm` module ] : https://nodejs.org/api/vm.html
14471485[ binding function ] : #binding-functions
14481486[ cleanup hooks ] : #cleanup-hooks
1487+ [ cppgc documentation ] : ../deps/v8/include/cppgc/README.md
14491488[ event loop ] : #event-loop
14501489[ exception handling ] : #exception-handling
14511490[ fast API calls ] : ../doc/contributing/adding-v8-fast-api.md
0 commit comments