|
57 | 57 | (out) = v8::type::New((buffer), (byte_offset), (length)); \ |
58 | 58 | } while (0) |
59 | 59 |
|
60 | | -namespace v8impl { |
| 60 | +void napi_env__::InvokeFinalizerFromGC(v8impl::RefTracker* finalizer) { |
| 61 | + if (module_api_version != NAPI_VERSION_EXPERIMENTAL) { |
| 62 | + EnqueueFinalizer(finalizer); |
| 63 | + } else { |
| 64 | + // The experimental code calls finalizers immediately to release native |
| 65 | + // objects as soon as possible, but it suspends use of JS from finalizer. |
| 66 | + // If JS calls are needed, then the finalizer code must call |
| 67 | + // node_api_post_finalizer. |
| 68 | + if (last_error.error_code == napi_ok && last_exception.IsEmpty()) { |
| 69 | + bool saved_suspend_call_into_js = suspend_call_into_js; |
| 70 | + finalizer->Finalize(); |
| 71 | + suspend_call_into_js = saved_suspend_call_into_js; |
| 72 | + } else { |
| 73 | + // The finalizers can be run in the middle of JS or C++ code. |
| 74 | + // That code may be in an error state. In that case use the asynchronous |
| 75 | + // finalizer. |
| 76 | + EnqueueFinalizer(finalizer); |
| 77 | + } |
| 78 | + } |
| 79 | +} |
61 | 80 |
|
| 81 | +namespace v8impl { |
62 | 82 | namespace { |
63 | 83 |
|
64 | 84 | template <typename CCharType, typename StringMaker> |
@@ -604,28 +624,72 @@ void Finalizer::ResetFinalizer() { |
604 | 624 | finalize_hint_ = nullptr; |
605 | 625 | } |
606 | 626 |
|
607 | | -// Wrapper around v8impl::Persistent that implements reference counting. |
608 | | -RefBase::RefBase(napi_env env, |
609 | | - uint32_t initial_refcount, |
610 | | - Ownership ownership, |
611 | | - napi_finalize finalize_callback, |
612 | | - void* finalize_data, |
613 | | - void* finalize_hint) |
| 627 | +TrackedFinalizer::TrackedFinalizer(napi_env env, |
| 628 | + napi_finalize finalize_callback, |
| 629 | + void* finalize_data, |
| 630 | + void* finalize_hint) |
614 | 631 | : Finalizer(env, finalize_callback, finalize_data, finalize_hint), |
615 | | - refcount_(initial_refcount), |
616 | | - ownership_(ownership) { |
| 632 | + RefTracker() { |
617 | 633 | Link(finalize_callback == nullptr ? &env->reflist : &env->finalizing_reflist); |
618 | 634 | } |
619 | 635 |
|
620 | | -// When a RefBase is being deleted, it may have been queued to call its |
| 636 | +TrackedFinalizer* TrackedFinalizer::New(napi_env env, |
| 637 | + napi_finalize finalize_callback, |
| 638 | + void* finalize_data, |
| 639 | + void* finalize_hint) { |
| 640 | + return new TrackedFinalizer( |
| 641 | + env, finalize_callback, finalize_data, finalize_hint); |
| 642 | +} |
| 643 | + |
| 644 | +// When a TrackedFinalizer is being deleted, it may have been queued to call its |
621 | 645 | // finalizer. |
622 | | -RefBase::~RefBase() { |
| 646 | +TrackedFinalizer::~TrackedFinalizer() { |
623 | 647 | // Remove from the env's tracked list. |
624 | 648 | Unlink(); |
625 | 649 | // Try to remove the finalizer from the scheduled second pass callback. |
626 | 650 | env_->DequeueFinalizer(this); |
627 | 651 | } |
628 | 652 |
|
| 653 | +void TrackedFinalizer::Finalize() { |
| 654 | + FinalizeCore(/*deleteMe:*/ true); |
| 655 | +} |
| 656 | + |
| 657 | +void TrackedFinalizer::FinalizeCore(bool deleteMe) { |
| 658 | + // Swap out the field finalize_callback so that it can not be accidentally |
| 659 | + // called more than once. |
| 660 | + napi_finalize finalize_callback = finalize_callback_; |
| 661 | + void* finalize_data = finalize_data_; |
| 662 | + void* finalize_hint = finalize_hint_; |
| 663 | + ResetFinalizer(); |
| 664 | + |
| 665 | + // Either the RefBase is going to be deleted in the finalize_callback or not, |
| 666 | + // it should be removed from the tracked list. |
| 667 | + Unlink(); |
| 668 | + // 1. If the finalize_callback is present, it should either delete the |
| 669 | + // derived RefBase, or set ownership with Ownership::kRuntime. |
| 670 | + // 2. If the finalizer is not present, the derived RefBase can be deleted |
| 671 | + // after the call. |
| 672 | + if (finalize_callback != nullptr) { |
| 673 | + env_->CallFinalizer(finalize_callback, finalize_data, finalize_hint); |
| 674 | + // No access to `this` after finalize_callback is called. |
| 675 | + } |
| 676 | + |
| 677 | + if (deleteMe) { |
| 678 | + delete this; |
| 679 | + } |
| 680 | +} |
| 681 | + |
| 682 | +// Wrapper around v8impl::Persistent that implements reference counting. |
| 683 | +RefBase::RefBase(napi_env env, |
| 684 | + uint32_t initial_refcount, |
| 685 | + Ownership ownership, |
| 686 | + napi_finalize finalize_callback, |
| 687 | + void* finalize_data, |
| 688 | + void* finalize_hint) |
| 689 | + : TrackedFinalizer(env, finalize_callback, finalize_data, finalize_hint), |
| 690 | + refcount_(initial_refcount), |
| 691 | + ownership_(ownership) {} |
| 692 | + |
629 | 693 | RefBase* RefBase::New(napi_env env, |
630 | 694 | uint32_t initial_refcount, |
631 | 695 | Ownership ownership, |
@@ -660,31 +724,9 @@ uint32_t RefBase::RefCount() { |
660 | 724 | } |
661 | 725 |
|
662 | 726 | void RefBase::Finalize() { |
663 | | - Ownership ownership = ownership_; |
664 | | - // Swap out the field finalize_callback so that it can not be accidentally |
665 | | - // called more than once. |
666 | | - napi_finalize finalize_callback = finalize_callback_; |
667 | | - void* finalize_data = finalize_data_; |
668 | | - void* finalize_hint = finalize_hint_; |
669 | | - ResetFinalizer(); |
670 | | - |
671 | | - // Either the RefBase is going to be deleted in the finalize_callback or not, |
672 | | - // it should be removed from the tracked list. |
673 | | - Unlink(); |
674 | | - // 1. If the finalize_callback is present, it should either delete the |
675 | | - // RefBase, or set ownership with Ownership::kRuntime. |
676 | | - // 2. If the finalizer is not present, the RefBase can be deleted after the |
677 | | - // call. |
678 | | - if (finalize_callback != nullptr) { |
679 | | - env_->CallFinalizer(finalize_callback, finalize_data, finalize_hint); |
680 | | - // No access to `this` after finalize_callback is called. |
681 | | - } |
682 | | - |
683 | 727 | // If the RefBase is not Ownership::kRuntime, userland code should delete it. |
684 | | - // Now delete it if it is Ownership::kRuntime. |
685 | | - if (ownership == Ownership::kRuntime) { |
686 | | - delete this; |
687 | | - } |
| 728 | + // Delete it if it is Ownership::kRuntime. |
| 729 | + FinalizeCore(/*deleteMe:*/ ownership_ == Ownership::kRuntime); |
688 | 730 | } |
689 | 731 |
|
690 | 732 | template <typename... Args> |
@@ -779,7 +821,7 @@ void Reference::WeakCallback(const v8::WeakCallbackInfo<Reference>& data) { |
779 | 821 | Reference* reference = data.GetParameter(); |
780 | 822 | // The reference must be reset during the weak callback as the API protocol. |
781 | 823 | reference->persistent_.Reset(); |
782 | | - reference->env_->EnqueueFinalizer(reference); |
| 824 | + reference->env_->InvokeFinalizerFromGC(reference); |
783 | 825 | } |
784 | 826 |
|
785 | 827 | } // end of namespace v8impl |
@@ -3310,6 +3352,20 @@ napi_status NAPI_CDECL napi_add_finalizer(napi_env env, |
3310 | 3352 | return napi_clear_last_error(env); |
3311 | 3353 | } |
3312 | 3354 |
|
| 3355 | +#ifdef NAPI_EXPERIMENTAL |
| 3356 | + |
| 3357 | +napi_status NAPI_CDECL node_api_post_finalizer(napi_env env, |
| 3358 | + napi_finalize finalize_cb, |
| 3359 | + void* finalize_data, |
| 3360 | + void* finalize_hint) { |
| 3361 | + CHECK_ENV(env); |
| 3362 | + env->EnqueueFinalizer(v8impl::TrackedFinalizer::New( |
| 3363 | + env, finalize_cb, finalize_data, finalize_hint)); |
| 3364 | + return napi_clear_last_error(env); |
| 3365 | +} |
| 3366 | + |
| 3367 | +#endif |
| 3368 | + |
3313 | 3369 | napi_status NAPI_CDECL napi_adjust_external_memory(napi_env env, |
3314 | 3370 | int64_t change_in_bytes, |
3315 | 3371 | int64_t* adjusted_value) { |
|
0 commit comments