@@ -314,16 +314,29 @@ class RefBase : protected Finalizer, RefTracker {
314314};
315315
316316class Reference : public RefBase {
317+ class PersistentWeakParameter {
318+ public:
319+ explicit PersistentWeakParameter (Reference* reference)
320+ : _reference(reference) {}
321+
322+ inline Reference* reference () { return _reference; }
323+
324+ inline void Reset (Reference* reference = nullptr ) {
325+ _reference = reference;
326+ }
327+
328+ private:
329+ Reference* _reference;
330+ };
331+
317332 protected:
318333 template <typename ... Args>
319- Reference (napi_env env,
320- v8::Local<v8::Value> value,
321- Args&&... args)
334+ Reference (napi_env env, v8::Local<v8::Value> value, Args&&... args)
322335 : RefBase(env, std::forward<Args>(args)...),
323- _persistent (env->isolate, value) {
336+ _persistent (env->isolate, value),
337+ _parameter(new PersistentWeakParameter(this )) {
324338 if (RefCount () == 0 ) {
325- _persistent.SetWeak (
326- this , FinalizeCallback, v8::WeakCallbackType::kParameter );
339+ SetWeak ();
327340 }
328341 }
329342
@@ -344,10 +357,18 @@ class Reference : public RefBase {
344357 finalize_hint);
345358 }
346359
360+ virtual ~Reference () {
361+ // If the second pass callback is scheduled, they are responsible for
362+ // deleting the weak parameter.
363+ if (_parameter != nullptr ) {
364+ delete _parameter;
365+ }
366+ }
367+
347368 inline uint32_t Ref () {
348369 uint32_t refcount = RefBase::Ref ();
349370 if (refcount == 1 ) {
350- _persistent. ClearWeak ();
371+ ClearWeak ();
351372 }
352373 return refcount;
353374 }
@@ -356,8 +377,7 @@ class Reference : public RefBase {
356377 uint32_t old_refcount = RefCount ();
357378 uint32_t refcount = RefBase::Unref ();
358379 if (old_refcount == 1 && refcount == 0 ) {
359- _persistent.SetWeak (
360- this , FinalizeCallback, v8::WeakCallbackType::kParameter );
380+ SetWeak ();
361381 }
362382 return refcount;
363383 }
@@ -374,38 +394,82 @@ class Reference : public RefBase {
374394 inline void Finalize (bool is_env_teardown = false ) override {
375395 // During env teardown, `~napi_env()` alone is responsible for finalizing.
376396 // 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 ();
397+ // `RefBase::Finalize()`, so let's clear the weakness here.
398+ if (is_env_teardown) {
399+ ClearWeak ();
381400 }
382401
383402 // Chain up to perform the rest of the finalization.
384403 RefBase::Finalize (is_env_teardown);
385404 }
386405
387406 private:
407+ // The persistent may have been reset in the first pass weak callback.
408+ // However a second pass callback may have been scheduled already, we
409+ // must ensure that the weak parameter no longer link to this reference
410+ // so that the second pass callback is not able to calling into the
411+ // `Reference::Finalize()`.
412+ inline void ClearWeak () {
413+ if (!_persistent.IsEmpty ()) {
414+ _persistent.ClearWeak ();
415+ }
416+ if (_parameter != nullptr ) {
417+ _parameter->Reset ();
418+ }
419+ }
420+
421+ // Setup the weakness callback and parameter.
422+ inline void SetWeak () {
423+ // If second pass callback has been scheduled, suppress the set weak.
424+ if (_parameter == nullptr ) {
425+ return ;
426+ }
427+ _persistent.SetWeak (
428+ _parameter, FinalizeCallback, v8::WeakCallbackType::kParameter );
429+ _parameter->Reset (this );
430+ }
431+
388432 // The N-API finalizer callback may make calls into the engine. V8's heap is
389433 // not in a consistent state during the weak callback, and therefore it does
390434 // not support calls back into it. However, it provides a mechanism for adding
391435 // a finalizer which may make calls back into the engine by allowing us to
392436 // attach such a second-pass finalizer from the first pass finalizer. Thus,
393437 // we do that here to ensure that the N-API finalizer callback is free to call
394438 // into the engine.
395- static void FinalizeCallback (const v8::WeakCallbackInfo<Reference>& data) {
396- Reference* reference = data.GetParameter ();
439+ static void FinalizeCallback (
440+ const v8::WeakCallbackInfo<PersistentWeakParameter>& data) {
441+ PersistentWeakParameter* parameter = data.GetParameter ();
442+ Reference* reference = parameter->reference ();
443+ if (reference == nullptr ) {
444+ return ;
445+ }
397446
398447 // The reference must be reset during the first pass.
399448 reference->_persistent .Reset ();
449+ // Mark the parameter not delete-able until the second pass callback is
450+ // invoked.
451+ reference->_parameter = nullptr ;
400452
401453 data.SetSecondPassCallback (SecondPassCallback);
402454 }
403455
404- static void SecondPassCallback (const v8::WeakCallbackInfo<Reference>& data) {
405- data.GetParameter ()->Finalize ();
456+ // Second pass callbacks are scheduled with platform tasks. At env teardown,
457+ // the tasks may have already be scheduled and we are unable to cancel the
458+ // second pass callback task. So we have to make sure that parameter is kept
459+ // alive until the second pass callback is been invoked.
460+ static void SecondPassCallback (
461+ const v8::WeakCallbackInfo<PersistentWeakParameter>& data) {
462+ PersistentWeakParameter* parameter = data.GetParameter ();
463+ Reference* reference = parameter->reference ();
464+ delete parameter;
465+ if (reference == nullptr ) {
466+ return ;
467+ }
468+ reference->Finalize ();
406469 }
407470
408471 v8impl::Persistent<v8::Value> _persistent;
472+ PersistentWeakParameter* _parameter;
409473};
410474
411475enum UnwrapAction {
0 commit comments