@@ -191,10 +191,10 @@ inline static napi_status ConcludeDeferred(napi_env env,
191191
192192enum UnwrapAction { KeepWrap, RemoveWrap };
193193
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) {
198198 NAPI_PREAMBLE (env);
199199 CHECK_ARG (env, js_object);
200200 if (action == KeepWrap) {
@@ -220,7 +220,7 @@ inline static napi_status Unwrap(napi_env env,
220220 if (action == RemoveWrap) {
221221 CHECK (obj->DeletePrivate (context, NAPI_PRIVATE_KEY (context, wrapper))
222222 .FromJust ());
223- Reference::Delete ( reference) ;
223+ delete reference;
224224 }
225225
226226 return GET_RETURN_STATUS (env);
@@ -466,33 +466,12 @@ RefBase::RefBase(napi_env env,
466466 void * finalize_data,
467467 void * finalize_hint)
468468 : 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) {
471471 Link (finalize_callback == nullptr ? &env->reflist : &env->finalizing_reflist );
472472}
473473
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.
496475// Delete is called in 2 ways. Either from the finalizer or
497476// from one of Unwrap or napi_delete_reference.
498477//
@@ -508,82 +487,82 @@ void* RefBase::Data() {
508487// The second way this is called is from
509488// the finalizer and _delete_self is set. In this case we
510489// 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_;
520512}
521513
522514uint32_t RefBase::Ref () {
523- return ++_refcount ;
515+ return ++refcount_ ;
524516}
525517
526518uint32_t RefBase::Unref () {
527- if (_refcount == 0 ) {
519+ if (refcount_ == 0 ) {
528520 return 0 ;
529521 }
530- return --_refcount ;
522+ return --refcount_ ;
531523}
532524
533525uint32_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 ;
573549 }
574550}
575551
576552template <typename ... Args>
577553Reference::Reference (napi_env env, v8::Local<v8::Value> value, Args&&... args)
578554 : RefBase(env, std::forward<Args>(args)...),
579- _persistent(env->isolate, value),
580- _secondPassParameter(new SecondPassCallParameterRef(this )),
581- _secondPassScheduled(false ) {
555+ persistent_(env->isolate, value) {
582556 if (RefCount () == 0 ) {
583557 SetWeak ();
584558 }
585559}
586560
561+ Reference::~Reference () {
562+ // Reset the handle. And no weak callback will be invoked.
563+ persistent_.Reset ();
564+ }
565+
587566Reference* Reference::New (napi_env env,
588567 v8::Local<v8::Value> value,
589568 uint32_t initial_refcount,
@@ -600,15 +579,6 @@ Reference* Reference::New(napi_env env,
600579 finalize_hint);
601580}
602581
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-
612582uint32_t Reference::Ref () {
613583 uint32_t refcount = RefBase::Ref ();
614584 if (refcount == 1 ) {
@@ -627,25 +597,20 @@ uint32_t Reference::Unref() {
627597}
628598
629599v8::Local<v8::Value> Reference::Get () {
630- if (_persistent .IsEmpty ()) {
600+ if (persistent_ .IsEmpty ()) {
631601 return v8::Local<v8::Value>();
632602 } else {
633- return v8::Local<v8::Value>::New (_env ->isolate , _persistent );
603+ return v8::Local<v8::Value>::New (env_ ->isolate , persistent_ );
634604 }
635605}
636606
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 ();
646611
647612 // Chain up to perform the rest of the finalization.
648- RefBase::Finalize (is_env_teardown );
613+ RefBase::Finalize ();
649614}
650615
651616// ClearWeak is marking the Reference so that the gc should not
@@ -654,72 +619,25 @@ void Reference::Finalize(bool is_env_teardown) {
654619// the secondPassParameter so that even if it has been
655620// scheduled no Finalization will be run.
656621void Reference::ClearWeak () {
657- if (!_persistent.IsEmpty ()) {
658- _persistent.ClearWeak ();
659- }
660- if (_secondPassParameter != nullptr ) {
661- *_secondPassParameter = nullptr ;
622+ if (!persistent_.IsEmpty ()) {
623+ persistent_.ClearWeak ();
662624 }
663625}
664626
665627// Mark the reference as weak and eligible for collection
666628// by the gc.
667629void 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 );
677631}
678632
679633// The N-API finalizer callback may make calls into the engine. V8's heap is
680634// 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);
723641}
724642
725643} // end of namespace v8impl
@@ -2515,7 +2433,7 @@ napi_status NAPI_CDECL napi_delete_reference(napi_env env, napi_ref ref) {
25152433 CHECK_ENV (env);
25162434 CHECK_ARG (env, ref);
25172435
2518- v8impl::Reference::Delete ( reinterpret_cast <v8impl::Reference*>(ref) );
2436+ delete reinterpret_cast <v8impl::Reference*>(ref);
25192437
25202438 return napi_clear_last_error (env);
25212439}
@@ -3205,7 +3123,7 @@ napi_status NAPI_CDECL napi_set_instance_data(napi_env env,
32053123 if (old_data != nullptr ) {
32063124 // Our contract so far has been to not finalize any old data there may be.
32073125 // So we simply delete it.
3208- v8impl::RefBase::Delete ( old_data) ;
3126+ delete old_data;
32093127 }
32103128
32113129 env->instance_data =
0 commit comments