@@ -1113,13 +1113,13 @@ class MyWrap final : CPPGC_MIXIN(MyWrap) {
1113
1113
```
1114
1114
1115
1115
If 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
1117
1117
pattern like this:
1118
1118
1119
1119
``` 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 .
1123
1123
}
1124
1124
```
1125
1125
@@ -1277,6 +1277,17 @@ referrer->Set(
1277
1277
).ToLocalChecked();
1278
1278
```
1279
1279
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
+
1280
1291
#### Lifetime and cleanups of cppgc-managed objects
1281
1292
1282
1293
Typically, 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
1285
1296
be held alive from C++ using persistent handles (see
1286
1297
` deps/v8/include/cppgc/persistent.h ` ) or as members of other living
1287
1298
cppgc-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:
1301
1299
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] [ ] .
1313
1350
1314
1351
### Callback scopes
1315
1352
@@ -1436,6 +1473,7 @@ static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
1436
1473
[ `async_hooks` module ] : https://nodejs.org/api/async_hooks.html
1437
1474
[ `async_wrap.h` ] : async_wrap.h
1438
1475
[ `base_object.h` ] : base_object.h
1476
+ [ `cppgc/prefinalizer.h` header ] : ../deps/v8/include/cppgc/prefinalizer.h
1439
1477
[ `handle_wrap.h` ] : handle_wrap.h
1440
1478
[ `memory_tracker.h` ] : memory_tracker.h
1441
1479
[ `req_wrap.h` ] : req_wrap.h
@@ -1446,6 +1484,7 @@ static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
1446
1484
[ `vm` module ] : https://nodejs.org/api/vm.html
1447
1485
[ binding function ] : #binding-functions
1448
1486
[ cleanup hooks ] : #cleanup-hooks
1487
+ [ cppgc documentation ] : ../deps/v8/include/cppgc/README.md
1449
1488
[ event loop ] : #event-loop
1450
1489
[ exception handling ] : #exception-handling
1451
1490
[ fast API calls ] : ../doc/contributing/adding-v8-fast-api.md
0 commit comments