@@ -191,10 +191,10 @@ inline static napi_status ConcludeDeferred(napi_env env,
191
191
192
192
enum UnwrapAction { KeepWrap, RemoveWrap };
193
193
194
- inline static napi_status Unwrap (napi_env env,
195
- napi_value js_object,
196
- void ** result,
197
- UnwrapAction action) {
194
+ inline napi_status Unwrap (napi_env env,
195
+ napi_value js_object,
196
+ void ** result,
197
+ UnwrapAction action) {
198
198
NAPI_PREAMBLE (env);
199
199
CHECK_ARG (env, js_object);
200
200
if (action == KeepWrap) {
@@ -220,7 +220,7 @@ inline static napi_status Unwrap(napi_env env,
220
220
if (action == RemoveWrap) {
221
221
CHECK (obj->DeletePrivate (context, NAPI_PRIVATE_KEY (context, wrapper))
222
222
.FromJust ());
223
- Reference::Delete ( reference) ;
223
+ delete reference;
224
224
}
225
225
226
226
return GET_RETURN_STATUS (env);
@@ -466,11 +466,20 @@ RefBase::RefBase(napi_env env,
466
466
void * finalize_data,
467
467
void * finalize_hint)
468
468
: Finalizer(env, finalize_callback, finalize_data, finalize_hint),
469
- _refcount (initial_refcount),
470
- _delete_self (delete_self) {
469
+ refcount_ (initial_refcount),
470
+ delete_self_ (delete_self) {
471
471
Link (finalize_callback == nullptr ? &env->reflist : &env->finalizing_reflist );
472
472
}
473
473
474
+ // When a RefBase is being deleted, it may have been queued to call its
475
+ // finalizer.
476
+ RefBase::~RefBase () {
477
+ // Remove from the env's tracked list.
478
+ Unlink ();
479
+ // Try to remove the finalizer from the scheduled second pass callback.
480
+ env_->DequeueFinalizer (this );
481
+ }
482
+
474
483
RefBase* RefBase::New (napi_env env,
475
484
uint32_t initial_refcount,
476
485
bool delete_self,
@@ -485,105 +494,62 @@ RefBase* RefBase::New(napi_env env,
485
494
finalize_hint);
486
495
}
487
496
488
- RefBase::~RefBase () {
489
- Unlink ();
490
- }
491
-
492
497
void * RefBase::Data () {
493
- return _finalize_data;
494
- }
495
-
496
- // Delete is called in 2 ways. Either from the finalizer or
497
- // from one of Unwrap or napi_delete_reference.
498
- //
499
- // When it is called from Unwrap or napi_delete_reference we only
500
- // want to do the delete if the finalizer has already run or
501
- // cannot have been queued to run (ie the reference count is > 0),
502
- // otherwise we may crash when the finalizer does run.
503
- // If the finalizer may have been queued and has not already run
504
- // delay the delete until the finalizer runs by not doing the delete
505
- // and setting _delete_self to true so that the finalizer will
506
- // delete it when it runs.
507
- //
508
- // The second way this is called is from
509
- // the finalizer and _delete_self is set. In this case we
510
- // know we need to do the deletion so just do it.
511
- void RefBase::Delete (RefBase* reference) {
512
- if ((reference->RefCount () != 0 ) || (reference->_delete_self ) ||
513
- (reference->_finalize_ran )) {
514
- delete reference;
515
- } else {
516
- // defer until finalizer runs as
517
- // it may already be queued
518
- reference->_delete_self = true ;
519
- }
498
+ return finalize_data_;
520
499
}
521
500
522
501
uint32_t RefBase::Ref () {
523
- return ++_refcount ;
502
+ return ++refcount_ ;
524
503
}
525
504
526
505
uint32_t RefBase::Unref () {
527
- if (_refcount == 0 ) {
506
+ if (refcount_ == 0 ) {
528
507
return 0 ;
529
508
}
530
- return --_refcount ;
509
+ return --refcount_ ;
531
510
}
532
511
533
512
uint32_t RefBase::RefCount () {
534
- return _refcount;
535
- }
536
-
537
- void RefBase::Finalize (bool is_env_teardown) {
538
- // In addition to being called during environment teardown, this method is
539
- // also the entry point for the garbage collector. During environment
540
- // teardown we have to remove the garbage collector's reference to this
541
- // method so that, if, as part of the user's callback, JS gets executed,
542
- // resulting in a garbage collection pass, this method is not re-entered as
543
- // part of that pass, because that'll cause a double free (as seen in
544
- // https://github.com/nodejs/node/issues/37236).
545
- //
546
- // Since this class does not have access to the V8 persistent reference,
547
- // this method is overridden in the `Reference` class below. Therein the
548
- // weak callback is removed, ensuring that the garbage collector does not
549
- // re-enter this method, and the method chains up to continue the process of
550
- // environment-teardown-induced finalization.
551
-
552
- // During environment teardown we have to convert a strong reference to
553
- // a weak reference to force the deferring behavior if the user's finalizer
554
- // happens to delete this reference so that the code in this function that
555
- // follows the call to the user's finalizer may safely access variables from
556
- // this instance.
557
- if (is_env_teardown && RefCount () > 0 ) _refcount = 0 ;
558
-
559
- if (_finalize_callback != nullptr ) {
560
- // This ensures that we never call the finalizer twice.
561
- napi_finalize fini = _finalize_callback;
562
- _finalize_callback = nullptr ;
563
- _env->CallFinalizer (fini, _finalize_data, _finalize_hint);
564
- }
565
-
566
- // this is safe because if a request to delete the reference
567
- // is made in the finalize_callback it will defer deletion
568
- // to this block and set _delete_self to true
569
- if (_delete_self || is_env_teardown) {
570
- Delete (this );
571
- } else {
572
- _finalize_ran = true ;
513
+ return refcount_;
514
+ }
515
+
516
+ void RefBase::Finalize () {
517
+ napi_finalize finalize_callback = finalize_callback_;
518
+ bool delete_self = delete_self_;
519
+
520
+ // Either the RefBase is going to be deleted in the finalize_callback or not,
521
+ // it should be removed from the tracked list.
522
+ Unlink ();
523
+ // 1. If the finalize_callback is present, it should either delete the
524
+ // RefBase, or set the flag `delete_self`.
525
+ // 2. If the finalizer is not present, the RefBase can be deleted after the
526
+ // call.
527
+ if (finalize_callback != nullptr ) {
528
+ env_->CallFinalizer (finalize_callback, finalize_data_, finalize_hint_);
529
+ // No access to `this` after finalize_callback is called.
530
+ }
531
+
532
+ // If the RefBase is not self-destructive, userland code should delete it.
533
+ // Now delete it if it is self-destructive.
534
+ if (delete_self) {
535
+ delete this ;
573
536
}
574
537
}
575
538
576
539
template <typename ... Args>
577
540
Reference::Reference (napi_env env, v8::Local<v8::Value> value, Args&&... args)
578
541
: RefBase(env, std::forward<Args>(args)...),
579
- _persistent(env->isolate, value),
580
- _secondPassParameter(new SecondPassCallParameterRef(this )),
581
- _secondPassScheduled(false ) {
542
+ persistent_(env->isolate, value) {
582
543
if (RefCount () == 0 ) {
583
544
SetWeak ();
584
545
}
585
546
}
586
547
548
+ Reference::~Reference () {
549
+ // Reset the handle. And no weak callback will be invoked.
550
+ persistent_.Reset ();
551
+ }
552
+
587
553
Reference* Reference::New (napi_env env,
588
554
v8::Local<v8::Value> value,
589
555
uint32_t initial_refcount,
@@ -600,15 +566,6 @@ Reference* Reference::New(napi_env env,
600
566
finalize_hint);
601
567
}
602
568
603
- Reference::~Reference () {
604
- // If the second pass callback is scheduled, it will delete the
605
- // parameter passed to it, otherwise it will never be scheduled
606
- // and we need to delete it here.
607
- if (!_secondPassScheduled) {
608
- delete _secondPassParameter;
609
- }
610
- }
611
-
612
569
uint32_t Reference::Ref () {
613
570
uint32_t refcount = RefBase::Ref ();
614
571
if (refcount == 1 ) {
@@ -627,25 +584,20 @@ uint32_t Reference::Unref() {
627
584
}
628
585
629
586
v8::Local<v8::Value> Reference::Get () {
630
- if (_persistent .IsEmpty ()) {
587
+ if (persistent_ .IsEmpty ()) {
631
588
return v8::Local<v8::Value>();
632
589
} else {
633
- return v8::Local<v8::Value>::New (_env ->isolate , _persistent );
590
+ return v8::Local<v8::Value>::New (env_ ->isolate , persistent_ );
634
591
}
635
592
}
636
593
637
- void Reference::Finalize (bool is_env_teardown) {
638
- // During env teardown, `~napi_env()` alone is responsible for finalizing.
639
- // Thus, we don't want any stray gc passes to trigger a second call to
640
- // `RefBase::Finalize()`. ClearWeak will ensure that even if the
641
- // gc is in progress no Finalization will be run for this Reference
642
- // by the gc.
643
- if (is_env_teardown) {
644
- ClearWeak ();
645
- }
594
+ void Reference::Finalize () {
595
+ // Unconditionally reset the persistent handle so that no weak callback will
596
+ // be invoked again.
597
+ persistent_.Reset ();
646
598
647
599
// Chain up to perform the rest of the finalization.
648
- RefBase::Finalize (is_env_teardown );
600
+ RefBase::Finalize ();
649
601
}
650
602
651
603
// ClearWeak is marking the Reference so that the gc should not
@@ -654,72 +606,25 @@ void Reference::Finalize(bool is_env_teardown) {
654
606
// the secondPassParameter so that even if it has been
655
607
// scheduled no Finalization will be run.
656
608
void Reference::ClearWeak () {
657
- if (!_persistent.IsEmpty ()) {
658
- _persistent.ClearWeak ();
659
- }
660
- if (_secondPassParameter != nullptr ) {
661
- *_secondPassParameter = nullptr ;
609
+ if (!persistent_.IsEmpty ()) {
610
+ persistent_.ClearWeak ();
662
611
}
663
612
}
664
613
665
614
// Mark the reference as weak and eligible for collection
666
615
// by the gc.
667
616
void Reference::SetWeak () {
668
- if (_secondPassParameter == nullptr ) {
669
- // This means that the Reference has already been processed
670
- // by the second pass callback, so its already been Finalized, do
671
- // nothing
672
- return ;
673
- }
674
- _persistent.SetWeak (
675
- _secondPassParameter, FinalizeCallback, v8::WeakCallbackType::kParameter );
676
- *_secondPassParameter = this ;
617
+ persistent_.SetWeak (this , WeakCallback, v8::WeakCallbackType::kParameter );
677
618
}
678
619
679
620
// The N-API finalizer callback may make calls into the engine. V8's heap is
680
621
// not in a consistent state during the weak callback, and therefore it does
681
- // not support calls back into it. However, it provides a mechanism for adding
682
- // a finalizer which may make calls back into the engine by allowing us to
683
- // attach such a second-pass finalizer from the first pass finalizer. Thus,
684
- // we do that here to ensure that the N-API finalizer callback is free to call
685
- // into the engine.
686
- void Reference::FinalizeCallback (
687
- const v8::WeakCallbackInfo<SecondPassCallParameterRef>& data) {
688
- SecondPassCallParameterRef* parameter = data.GetParameter ();
689
- Reference* reference = *parameter;
690
- if (reference == nullptr ) {
691
- return ;
692
- }
693
-
694
- // The reference must be reset during the first pass.
695
- reference->_persistent .Reset ();
696
- // Mark the parameter not delete-able until the second pass callback is
697
- // invoked.
698
- reference->_secondPassScheduled = true ;
699
-
700
- data.SetSecondPassCallback (SecondPassCallback);
701
- }
702
-
703
- // Second pass callbacks are scheduled with platform tasks. At env teardown,
704
- // the tasks may have already be scheduled and we are unable to cancel the
705
- // second pass callback task. We have to make sure that parameter is kept
706
- // alive until the second pass callback is been invoked. In order to do
707
- // this and still allow our code to Finalize/delete the Reference during
708
- // shutdown we have to use a separately allocated parameter instead
709
- // of a parameter within the Reference object itself. This is what
710
- // is stored in _secondPassParameter and it is allocated in the
711
- // constructor for the Reference.
712
- void Reference::SecondPassCallback (
713
- const v8::WeakCallbackInfo<SecondPassCallParameterRef>& data) {
714
- SecondPassCallParameterRef* parameter = data.GetParameter ();
715
- Reference* reference = *parameter;
716
- delete parameter;
717
- if (reference == nullptr ) {
718
- // the reference itself has already been deleted so nothing to do
719
- return ;
720
- }
721
- reference->_secondPassParameter = nullptr ;
722
- reference->Finalize ();
622
+ // not support calls back into it. Enqueue the invocation of the finalizer.
623
+ void Reference::WeakCallback (const v8::WeakCallbackInfo<Reference>& data) {
624
+ Reference* reference = data.GetParameter ();
625
+ // The reference must be reset during the weak callback as the API protocol.
626
+ reference->persistent_ .Reset ();
627
+ reference->env_ ->EnqueueFinalizer (reference);
723
628
}
724
629
725
630
} // end of namespace v8impl
@@ -2515,7 +2420,7 @@ napi_status NAPI_CDECL napi_delete_reference(napi_env env, napi_ref ref) {
2515
2420
CHECK_ENV (env);
2516
2421
CHECK_ARG (env, ref);
2517
2422
2518
- v8impl::Reference::Delete ( reinterpret_cast <v8impl::Reference*>(ref) );
2423
+ delete reinterpret_cast <v8impl::Reference*>(ref);
2519
2424
2520
2425
return napi_clear_last_error (env);
2521
2426
}
@@ -3205,7 +3110,7 @@ napi_status NAPI_CDECL napi_set_instance_data(napi_env env,
3205
3110
if (old_data != nullptr ) {
3206
3111
// Our contract so far has been to not finalize any old data there may be.
3207
3112
// So we simply delete it.
3208
- v8impl::RefBase::Delete ( old_data) ;
3113
+ delete old_data;
3209
3114
}
3210
3115
3211
3116
env->instance_data =
0 commit comments