@@ -314,16 +314,38 @@ class RefBase : protected Finalizer, RefTracker {
314
314
};
315
315
316
316
class Reference : public RefBase {
317
+ class PersistentWeakParameter {
318
+ public:
319
+ explicit PersistentWeakParameter (Reference* reference)
320
+ : _reference(reference), _second_pass_callback_scheduled(false ) {}
321
+
322
+ inline Reference* reference () { return _reference; }
323
+
324
+ inline void Reset (Reference* reference = nullptr ) {
325
+ _reference = reference;
326
+ }
327
+
328
+ inline bool second_pass_callback_scheduled () {
329
+ return _second_pass_callback_scheduled;
330
+ }
331
+
332
+ inline void set_second_pass_callback_scheduled () {
333
+ _second_pass_callback_scheduled = true ;
334
+ }
335
+
336
+ private:
337
+ Reference* _reference;
338
+ bool _second_pass_callback_scheduled;
339
+ };
340
+
317
341
protected:
318
342
template <typename ... Args>
319
- Reference (napi_env env,
320
- v8::Local<v8::Value> value,
321
- Args&&... args)
343
+ Reference (napi_env env, v8::Local<v8::Value> value, Args&&... args)
322
344
: RefBase(env, std::forward<Args>(args)...),
323
- _persistent (env->isolate, value) {
345
+ _persistent (env->isolate, value),
346
+ _parameter(new PersistentWeakParameter(this )) {
324
347
if (RefCount () == 0 ) {
325
- _persistent.SetWeak (
326
- this , FinalizeCallback, v8::WeakCallbackType::kParameter );
348
+ SetWeak ();
327
349
}
328
350
}
329
351
@@ -344,10 +366,18 @@ class Reference : public RefBase {
344
366
finalize_hint);
345
367
}
346
368
369
+ virtual ~Reference () {
370
+ if (!_parameter->second_pass_callback_scheduled ()) {
371
+ // The second pass weak callback is not scheduled, there is no pending
372
+ // weak callbacks to release the weak parameter. Just deleting it.
373
+ delete _parameter;
374
+ }
375
+ }
376
+
347
377
inline uint32_t Ref () {
348
378
uint32_t refcount = RefBase::Ref ();
349
379
if (refcount == 1 ) {
350
- _persistent. ClearWeak ();
380
+ ClearWeak ();
351
381
}
352
382
return refcount;
353
383
}
@@ -356,8 +386,7 @@ class Reference : public RefBase {
356
386
uint32_t old_refcount = RefCount ();
357
387
uint32_t refcount = RefBase::Unref ();
358
388
if (old_refcount == 1 && refcount == 0 ) {
359
- _persistent.SetWeak (
360
- this , FinalizeCallback, v8::WeakCallbackType::kParameter );
389
+ SetWeak ();
361
390
}
362
391
return refcount;
363
392
}
@@ -374,38 +403,76 @@ class Reference : public RefBase {
374
403
inline void Finalize (bool is_env_teardown = false ) override {
375
404
// During env teardown, `~napi_env()` alone is responsible for finalizing.
376
405
// Thus, we don't want any stray gc passes to trigger a second call to
377
- // `Finalize()`, so let's reset the persistent here if nothing is
378
- // keeping it alive.
379
- if (is_env_teardown && _persistent.IsWeak ()) {
380
- _persistent.ClearWeak ();
406
+ // `RefBase::Finalize()`, so let's clear the weakness here.
407
+ if (is_env_teardown) {
408
+ ClearWeak ();
381
409
}
382
410
383
411
// Chain up to perform the rest of the finalization.
384
412
RefBase::Finalize (is_env_teardown);
385
413
}
386
414
387
415
private:
416
+ // The persistent may have been reset in the first pass weak callback.
417
+ // However a second pass callback may have been scheduled already, we
418
+ // must ensure that the weak parameter no longer link to this reference
419
+ // so that the second pass callback is not able to calling into the
420
+ // `Reference::Finalize()`.
421
+ inline void ClearWeak () {
422
+ if (!_persistent.IsEmpty ()) {
423
+ _persistent.ClearWeak ();
424
+ }
425
+ _parameter->Reset ();
426
+ }
427
+
428
+ // Setup the weakness callback and parameter.
429
+ inline void SetWeak () {
430
+ _persistent.SetWeak (
431
+ _parameter, FinalizeCallback, v8::WeakCallbackType::kParameter );
432
+ _parameter->Reset (this );
433
+ }
434
+
388
435
// The N-API finalizer callback may make calls into the engine. V8's heap is
389
436
// not in a consistent state during the weak callback, and therefore it does
390
437
// not support calls back into it. However, it provides a mechanism for adding
391
438
// a finalizer which may make calls back into the engine by allowing us to
392
439
// attach such a second-pass finalizer from the first pass finalizer. Thus,
393
440
// we do that here to ensure that the N-API finalizer callback is free to call
394
441
// into the engine.
395
- static void FinalizeCallback (const v8::WeakCallbackInfo<Reference>& data) {
396
- Reference* reference = data.GetParameter ();
442
+ static void FinalizeCallback (
443
+ const v8::WeakCallbackInfo<PersistentWeakParameter>& data) {
444
+ PersistentWeakParameter* parameter = data.GetParameter ();
445
+ Reference* reference = parameter->reference ();
446
+ if (reference == nullptr ) {
447
+ return ;
448
+ }
397
449
398
450
// The reference must be reset during the first pass.
399
451
reference->_persistent .Reset ();
452
+ // Mark the parameter not delete-able until the second pass callback is
453
+ // invoked.
454
+ parameter->set_second_pass_callback_scheduled ();
400
455
401
456
data.SetSecondPassCallback (SecondPassCallback);
402
457
}
403
458
404
- static void SecondPassCallback (const v8::WeakCallbackInfo<Reference>& data) {
405
- data.GetParameter ()->Finalize ();
459
+ // Second pass callbacks are scheduled with platform tasks. At env teardown,
460
+ // the tasks may have already be scheduled and we are unable to cancel the
461
+ // second pass callback task. So we have to make sure that parameter is kept
462
+ // alive until the second pass callback is been invoked.
463
+ static void SecondPassCallback (
464
+ const v8::WeakCallbackInfo<PersistentWeakParameter>& data) {
465
+ PersistentWeakParameter* parameter = data.GetParameter ();
466
+ Reference* reference = parameter->reference ();
467
+ delete parameter;
468
+ if (reference == nullptr ) {
469
+ return ;
470
+ }
471
+ reference->Finalize ();
406
472
}
407
473
408
474
v8impl::Persistent<v8::Value> _persistent;
475
+ PersistentWeakParameter* _parameter;
409
476
};
410
477
411
478
enum UnwrapAction {
0 commit comments