@@ -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,11 +466,20 @@ 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+ // 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+
474483RefBase* RefBase::New (napi_env env,
475484 uint32_t initial_refcount,
476485 bool delete_self,
@@ -485,105 +494,62 @@ RefBase* RefBase::New(napi_env env,
485494 finalize_hint);
486495}
487496
488- RefBase::~RefBase () {
489- Unlink ();
490- }
491-
492497void * 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_;
520499}
521500
522501uint32_t RefBase::Ref () {
523- return ++_refcount ;
502+ return ++refcount_ ;
524503}
525504
526505uint32_t RefBase::Unref () {
527- if (_refcount == 0 ) {
506+ if (refcount_ == 0 ) {
528507 return 0 ;
529508 }
530- return --_refcount ;
509+ return --refcount_ ;
531510}
532511
533512uint32_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 ;
573536 }
574537}
575538
576539template <typename ... Args>
577540Reference::Reference (napi_env env, v8::Local<v8::Value> value, Args&&... args)
578541 : RefBase(env, std::forward<Args>(args)...),
579- _persistent(env->isolate, value),
580- _secondPassParameter(new SecondPassCallParameterRef(this )),
581- _secondPassScheduled(false ) {
542+ persistent_(env->isolate, value) {
582543 if (RefCount () == 0 ) {
583544 SetWeak ();
584545 }
585546}
586547
548+ Reference::~Reference () {
549+ // Reset the handle. And no weak callback will be invoked.
550+ persistent_.Reset ();
551+ }
552+
587553Reference* Reference::New (napi_env env,
588554 v8::Local<v8::Value> value,
589555 uint32_t initial_refcount,
@@ -600,15 +566,6 @@ Reference* Reference::New(napi_env env,
600566 finalize_hint);
601567}
602568
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-
612569uint32_t Reference::Ref () {
613570 uint32_t refcount = RefBase::Ref ();
614571 if (refcount == 1 ) {
@@ -627,25 +584,20 @@ uint32_t Reference::Unref() {
627584}
628585
629586v8::Local<v8::Value> Reference::Get () {
630- if (_persistent .IsEmpty ()) {
587+ if (persistent_ .IsEmpty ()) {
631588 return v8::Local<v8::Value>();
632589 } else {
633- return v8::Local<v8::Value>::New (_env ->isolate , _persistent );
590+ return v8::Local<v8::Value>::New (env_ ->isolate , persistent_ );
634591 }
635592}
636593
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 ();
646598
647599 // Chain up to perform the rest of the finalization.
648- RefBase::Finalize (is_env_teardown );
600+ RefBase::Finalize ();
649601}
650602
651603// ClearWeak is marking the Reference so that the gc should not
@@ -654,72 +606,25 @@ void Reference::Finalize(bool is_env_teardown) {
654606// the secondPassParameter so that even if it has been
655607// scheduled no Finalization will be run.
656608void Reference::ClearWeak () {
657- if (!_persistent.IsEmpty ()) {
658- _persistent.ClearWeak ();
659- }
660- if (_secondPassParameter != nullptr ) {
661- *_secondPassParameter = nullptr ;
609+ if (!persistent_.IsEmpty ()) {
610+ persistent_.ClearWeak ();
662611 }
663612}
664613
665614// Mark the reference as weak and eligible for collection
666615// by the gc.
667616void 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 );
677618}
678619
679620// The N-API finalizer callback may make calls into the engine. V8's heap is
680621// 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);
723628}
724629
725630} // end of namespace v8impl
@@ -2515,7 +2420,7 @@ napi_status NAPI_CDECL napi_delete_reference(napi_env env, napi_ref ref) {
25152420 CHECK_ENV (env);
25162421 CHECK_ARG (env, ref);
25172422
2518- v8impl::Reference::Delete ( reinterpret_cast <v8impl::Reference*>(ref) );
2423+ delete reinterpret_cast <v8impl::Reference*>(ref);
25192424
25202425 return napi_clear_last_error (env);
25212426}
@@ -3205,7 +3110,7 @@ napi_status NAPI_CDECL napi_set_instance_data(napi_env env,
32053110 if (old_data != nullptr ) {
32063111 // Our contract so far has been to not finalize any old data there may be.
32073112 // So we simply delete it.
3208- v8impl::RefBase::Delete ( old_data) ;
3113+ delete old_data;
32093114 }
32103115
32113116 env->instance_data =
0 commit comments