|
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