@@ -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,33 +466,12 @@ 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
- RefBase* RefBase::New (napi_env env,
475
- uint32_t initial_refcount,
476
- bool delete_self,
477
- napi_finalize finalize_callback,
478
- void * finalize_data,
479
- void * finalize_hint) {
480
- return new RefBase (env,
481
- initial_refcount,
482
- delete_self,
483
- finalize_callback,
484
- finalize_data,
485
- finalize_hint);
486
- }
487
-
488
- RefBase::~RefBase () {
489
- Unlink ();
490
- }
491
-
492
- void * RefBase::Data () {
493
- return _finalize_data;
494
- }
495
-
474
+ // TODO(@legendecas): document this better.
496
475
// Delete is called in 2 ways. Either from the finalizer or
497
476
// from one of Unwrap or napi_delete_reference.
498
477
//
@@ -508,82 +487,82 @@ void* RefBase::Data() {
508
487
// The second way this is called is from
509
488
// the finalizer and _delete_self is set. In this case we
510
489
// 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
- }
490
+ RefBase::~RefBase () {
491
+ Unlink ();
492
+ // Try to remove the finalizer from the scheduled second pass callback.
493
+ env_->DequeueFinalizer (this );
494
+ }
495
+
496
+ RefBase* RefBase::New (napi_env env,
497
+ uint32_t initial_refcount,
498
+ bool delete_self,
499
+ napi_finalize finalize_callback,
500
+ void * finalize_data,
501
+ void * finalize_hint) {
502
+ return new RefBase (env,
503
+ initial_refcount,
504
+ delete_self,
505
+ finalize_callback,
506
+ finalize_data,
507
+ finalize_hint);
508
+ }
509
+
510
+ void * RefBase::Data () {
511
+ return finalize_data_;
520
512
}
521
513
522
514
uint32_t RefBase::Ref () {
523
- return ++_refcount ;
515
+ return ++refcount_ ;
524
516
}
525
517
526
518
uint32_t RefBase::Unref () {
527
- if (_refcount == 0 ) {
519
+ if (refcount_ == 0 ) {
528
520
return 0 ;
529
521
}
530
- return --_refcount ;
522
+ return --refcount_ ;
531
523
}
532
524
533
525
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 ;
526
+ return refcount_;
527
+ }
528
+
529
+ void RefBase::Finalize () {
530
+ napi_finalize finalize_callback = finalize_callback_;
531
+ bool delete_self = delete_self_;
532
+
533
+ // Either RefBase is deleted or not, it should be removed from the tracked
534
+ // list.
535
+ Unlink ();
536
+ // 1. If the finalizer is present, it should either delete the RefBase in the
537
+ // finalizer, or set the flag `delete_self`.
538
+ // 2. If the finalizer is not present, the RefBase can be deleted after the
539
+ // call.
540
+ if (finalize_callback != nullptr ) {
541
+ env_->CallFinalizer (finalize_callback, finalize_data_, finalize_hint_);
542
+ // No access to this after Finalizer is called.
543
+ }
544
+
545
+ // If the RefBase is not self-destructive, it should have been deleted in the
546
+ // finalizer. Now delete it if it is self-destructive.
547
+ if (delete_self) {
548
+ delete this ;
573
549
}
574
550
}
575
551
576
552
template <typename ... Args>
577
553
Reference::Reference (napi_env env, v8::Local<v8::Value> value, Args&&... args)
578
554
: RefBase(env, std::forward<Args>(args)...),
579
- _persistent(env->isolate, value),
580
- _secondPassParameter(new SecondPassCallParameterRef(this )),
581
- _secondPassScheduled(false ) {
555
+ persistent_(env->isolate, value) {
582
556
if (RefCount () == 0 ) {
583
557
SetWeak ();
584
558
}
585
559
}
586
560
561
+ Reference::~Reference () {
562
+ // Reset the handle. And no weak callback will be invoked.
563
+ persistent_.Reset ();
564
+ }
565
+
587
566
Reference* Reference::New (napi_env env,
588
567
v8::Local<v8::Value> value,
589
568
uint32_t initial_refcount,
@@ -600,15 +579,6 @@ Reference* Reference::New(napi_env env,
600
579
finalize_hint);
601
580
}
602
581
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
582
uint32_t Reference::Ref () {
613
583
uint32_t refcount = RefBase::Ref ();
614
584
if (refcount == 1 ) {
@@ -627,25 +597,20 @@ uint32_t Reference::Unref() {
627
597
}
628
598
629
599
v8::Local<v8::Value> Reference::Get () {
630
- if (_persistent .IsEmpty ()) {
600
+ if (persistent_ .IsEmpty ()) {
631
601
return v8::Local<v8::Value>();
632
602
} else {
633
- return v8::Local<v8::Value>::New (_env ->isolate , _persistent );
603
+ return v8::Local<v8::Value>::New (env_ ->isolate , persistent_ );
634
604
}
635
605
}
636
606
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
- }
607
+ void Reference::Finalize () {
608
+ // Unconditionally reset the persistent handle so that no weak callback will
609
+ // be invoked again.
610
+ persistent_.Reset ();
646
611
647
612
// Chain up to perform the rest of the finalization.
648
- RefBase::Finalize (is_env_teardown );
613
+ RefBase::Finalize ();
649
614
}
650
615
651
616
// ClearWeak is marking the Reference so that the gc should not
@@ -654,72 +619,25 @@ void Reference::Finalize(bool is_env_teardown) {
654
619
// the secondPassParameter so that even if it has been
655
620
// scheduled no Finalization will be run.
656
621
void Reference::ClearWeak () {
657
- if (!_persistent.IsEmpty ()) {
658
- _persistent.ClearWeak ();
659
- }
660
- if (_secondPassParameter != nullptr ) {
661
- *_secondPassParameter = nullptr ;
622
+ if (!persistent_.IsEmpty ()) {
623
+ persistent_.ClearWeak ();
662
624
}
663
625
}
664
626
665
627
// Mark the reference as weak and eligible for collection
666
628
// by the gc.
667
629
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 ;
630
+ persistent_.SetWeak (this , WeakCallback, v8::WeakCallbackType::kParameter );
677
631
}
678
632
679
633
// The N-API finalizer callback may make calls into the engine. V8's heap is
680
634
// 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 ();
635
+ // not support calls back into it. Enqueue the invocation of the finalizer.
636
+ void Reference::WeakCallback (const v8::WeakCallbackInfo<Reference>& data) {
637
+ Reference* reference = data.GetParameter ();
638
+ // The reference must be reset during the weak callback as the API protocol.
639
+ reference->persistent_ .Reset ();
640
+ reference->env_ ->EnqueueFinalizer (reference);
723
641
}
724
642
725
643
} // end of namespace v8impl
@@ -2515,7 +2433,7 @@ napi_status NAPI_CDECL napi_delete_reference(napi_env env, napi_ref ref) {
2515
2433
CHECK_ENV (env);
2516
2434
CHECK_ARG (env, ref);
2517
2435
2518
- v8impl::Reference::Delete ( reinterpret_cast <v8impl::Reference*>(ref) );
2436
+ delete reinterpret_cast <v8impl::Reference*>(ref);
2519
2437
2520
2438
return napi_clear_last_error (env);
2521
2439
}
@@ -3205,7 +3123,7 @@ napi_status NAPI_CDECL napi_set_instance_data(napi_env env,
3205
3123
if (old_data != nullptr ) {
3206
3124
// Our contract so far has been to not finalize any old data there may be.
3207
3125
// So we simply delete it.
3208
- v8impl::RefBase::Delete ( old_data) ;
3126
+ delete old_data;
3209
3127
}
3210
3128
3211
3129
env->instance_data =
0 commit comments