-
-
Notifications
You must be signed in to change notification settings - Fork 35.9k
node-api: generalize finalizer second pass callback #44141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -218,7 +218,12 @@ inline napi_status Unwrap(napi_env env, | |
| if (action == RemoveWrap) { | ||
| CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) | ||
| .FromJust()); | ||
| delete reference; | ||
| if (reference->ownership() == Ownership::kUserland) { | ||
| // When the wrap is been removed, the finalizer should be reset. | ||
| reference->ResetFinalizer(); | ||
| } else { | ||
| delete reference; | ||
| } | ||
| } | ||
|
|
||
| return GET_RETURN_STATUS(env); | ||
|
|
@@ -245,7 +250,8 @@ class CallbackBundle { | |
| bundle->env = env; | ||
|
|
||
| v8::Local<v8::Value> cbdata = v8::External::New(env->isolate, bundle); | ||
| Reference::New(env, cbdata, 0, true, Delete, bundle, nullptr); | ||
| Reference::New( | ||
| env, cbdata, 0, Ownership::kRuntime, Delete, bundle, nullptr); | ||
| return cbdata; | ||
| } | ||
| napi_env env; // Necessary to invoke C++ NAPI callback | ||
|
|
@@ -429,16 +435,21 @@ inline napi_status Wrap(napi_env env, | |
| // before then, then the finalize callback will never be invoked.) | ||
| // Therefore a finalize callback is required when returning a reference. | ||
| CHECK_ARG(env, finalize_cb); | ||
| reference = v8impl::Reference::New( | ||
| env, obj, 0, false, finalize_cb, native_object, finalize_hint); | ||
| reference = v8impl::Reference::New(env, | ||
| obj, | ||
| 0, | ||
| v8impl::Ownership::kUserland, | ||
| finalize_cb, | ||
| native_object, | ||
| finalize_hint); | ||
| *result = reinterpret_cast<napi_ref>(reference); | ||
| } else { | ||
| // Create a self-deleting reference. | ||
| reference = v8impl::Reference::New( | ||
| env, | ||
| obj, | ||
| 0, | ||
| true, | ||
| v8impl::Ownership::kRuntime, | ||
| finalize_cb, | ||
| native_object, | ||
| finalize_cb == nullptr ? nullptr : finalize_hint); | ||
|
|
@@ -456,16 +467,22 @@ inline napi_status Wrap(napi_env env, | |
|
|
||
| } // end of anonymous namespace | ||
|
|
||
| void Finalizer::ResetFinalizer() { | ||
| finalize_callback_ = nullptr; | ||
| finalize_data_ = nullptr; | ||
| finalize_hint_ = nullptr; | ||
| } | ||
|
|
||
| // Wrapper around v8impl::Persistent that implements reference counting. | ||
| RefBase::RefBase(napi_env env, | ||
| uint32_t initial_refcount, | ||
| bool delete_self, | ||
| Ownership ownership, | ||
| napi_finalize finalize_callback, | ||
| void* finalize_data, | ||
| void* finalize_hint) | ||
| : Finalizer(env, finalize_callback, finalize_data, finalize_hint), | ||
| refcount_(initial_refcount), | ||
| delete_self_(delete_self) { | ||
| ownership_(ownership) { | ||
| Link(finalize_callback == nullptr ? &env->reflist : &env->finalizing_reflist); | ||
| } | ||
|
|
||
|
|
@@ -480,13 +497,13 @@ RefBase::~RefBase() { | |
|
|
||
| RefBase* RefBase::New(napi_env env, | ||
| uint32_t initial_refcount, | ||
| bool delete_self, | ||
| Ownership ownership, | ||
| napi_finalize finalize_callback, | ||
| void* finalize_data, | ||
| void* finalize_hint) { | ||
| return new RefBase(env, | ||
| initial_refcount, | ||
| delete_self, | ||
| ownership, | ||
| finalize_callback, | ||
| finalize_data, | ||
| finalize_hint); | ||
|
|
@@ -512,27 +529,29 @@ uint32_t RefBase::RefCount() { | |
| } | ||
|
|
||
| void RefBase::Finalize() { | ||
| bool delete_self = delete_self_; | ||
| Ownership ownership = ownership_; | ||
| // Swap out the field finalize_callback so that it can not be accidentally | ||
| // called more than once. | ||
| napi_finalize finalize_callback = finalize_callback_; | ||
| finalize_callback_ = nullptr; | ||
| void* finalize_data = finalize_data_; | ||
| void* finalize_hint = finalize_hint_; | ||
| ResetFinalizer(); | ||
|
|
||
| // Either the RefBase is going to be deleted in the finalize_callback or not, | ||
| // it should be removed from the tracked list. | ||
| Unlink(); | ||
| // 1. If the finalize_callback is present, it should either delete the | ||
| // RefBase, or set the flag `delete_self`. | ||
| // RefBase, or set ownership with Ownership::kRuntime. | ||
| // 2. If the finalizer is not present, the RefBase can be deleted after the | ||
| // call. | ||
| if (finalize_callback != nullptr) { | ||
| env_->CallFinalizer(finalize_callback, finalize_data_, finalize_hint_); | ||
| env_->CallFinalizer(finalize_callback, finalize_data, finalize_hint); | ||
| // No access to `this` after finalize_callback is called. | ||
| } | ||
|
|
||
| // If the RefBase is not self-destructive, userland code should delete it. | ||
| // Now delete it if it is self-destructive. | ||
| if (delete_self) { | ||
| // If the RefBase is not Ownership::kRuntime, userland code should delete it. | ||
| // Now delete it if it is Ownership::kRuntime. | ||
| if (ownership == Ownership::kRuntime) { | ||
| delete this; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to get to a situation when we delete the object at the same address twice? What if some other object allocated at the same address after the first delete?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
| } | ||
|
|
@@ -554,14 +573,14 @@ Reference::~Reference() { | |
| Reference* Reference::New(napi_env env, | ||
| v8::Local<v8::Value> value, | ||
| uint32_t initial_refcount, | ||
| bool delete_self, | ||
| Ownership ownership, | ||
| napi_finalize finalize_callback, | ||
| void* finalize_data, | ||
| void* finalize_hint) { | ||
| return new Reference(env, | ||
| value, | ||
| initial_refcount, | ||
| delete_self, | ||
| ownership, | ||
| finalize_callback, | ||
| finalize_data, | ||
| finalize_hint); | ||
|
|
@@ -2298,8 +2317,13 @@ napi_status NAPI_CDECL napi_create_external(napi_env env, | |
| if (finalize_cb) { | ||
| // The Reference object will delete itself after invoking the finalizer | ||
| // callback. | ||
| v8impl::Reference::New( | ||
| env, external_value, 0, true, finalize_cb, data, finalize_hint); | ||
| v8impl::Reference::New(env, | ||
| external_value, | ||
| 0, | ||
| v8impl::Ownership::kRuntime, | ||
| finalize_cb, | ||
| data, | ||
| finalize_hint); | ||
| } | ||
|
|
||
| *result = v8impl::JsValueFromV8LocalValue(external_value); | ||
|
|
@@ -2408,8 +2432,8 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env, | |
| return napi_set_last_error(env, napi_invalid_arg); | ||
| } | ||
|
|
||
| v8impl::Reference* reference = | ||
| v8impl::Reference::New(env, v8_value, initial_refcount, false); | ||
| v8impl::Reference* reference = v8impl::Reference::New( | ||
| env, v8_value, initial_refcount, v8impl::Ownership::kUserland); | ||
|
|
||
| *result = reinterpret_cast<napi_ref>(reference); | ||
| return napi_clear_last_error(env); | ||
|
|
@@ -3116,8 +3140,8 @@ napi_status NAPI_CDECL napi_set_instance_data(napi_env env, | |
| delete old_data; | ||
| } | ||
|
|
||
| env->instance_data = | ||
| v8impl::RefBase::New(env, 0, true, finalize_cb, data, finalize_hint); | ||
| env->instance_data = v8impl::RefBase::New( | ||
| env, 0, v8impl::Ownership::kRuntime, finalize_cb, data, finalize_hint); | ||
|
|
||
| return napi_clear_last_error(env); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -316,6 +316,8 @@ class Finalizer { | |
| void* data() { return finalize_data_; } | ||
| void* hint() { return finalize_hint_; } | ||
|
|
||
| void ResetFinalizer(); | ||
|
|
||
| protected: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we make fields private and use the new accessor methods instead?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really. These field values are modified in the subclasses. Marking them as private can introduce many unrelated changes. I'll do the field clean-up in a follow-up PR. |
||
| napi_env env_; | ||
| napi_finalize finalize_callback_; | ||
|
|
@@ -337,20 +339,30 @@ class TryCatch : public v8::TryCatch { | |
| napi_env _env; | ||
| }; | ||
|
|
||
| // Ownership of a reference. | ||
| enum class Ownership { | ||
| // The reference is owned by the runtime. No userland call is needed to | ||
| // destruct the reference. | ||
| kRuntime, | ||
| // The reference is owned by the userland. User code is responsible to delete | ||
| // the reference with appropriate node-api calls. | ||
| kUserland, | ||
| }; | ||
|
|
||
| // Wrapper around Finalizer that implements reference counting. | ||
| class RefBase : public Finalizer, public RefTracker { | ||
| protected: | ||
| RefBase(napi_env env, | ||
| uint32_t initial_refcount, | ||
| bool delete_self, | ||
| Ownership ownership, | ||
| napi_finalize finalize_callback, | ||
| void* finalize_data, | ||
| void* finalize_hint); | ||
|
|
||
| public: | ||
| static RefBase* New(napi_env env, | ||
| uint32_t initial_refcount, | ||
| bool delete_self, | ||
| Ownership ownership, | ||
| napi_finalize finalize_callback, | ||
| void* finalize_data, | ||
| void* finalize_hint); | ||
|
|
@@ -361,12 +373,14 @@ class RefBase : public Finalizer, public RefTracker { | |
| uint32_t Unref(); | ||
| uint32_t RefCount(); | ||
|
|
||
| Ownership ownership() { return ownership_; } | ||
|
|
||
| protected: | ||
| void Finalize() override; | ||
|
|
||
| private: | ||
| uint32_t refcount_; | ||
| bool delete_self_; | ||
| Ownership ownership_; | ||
| }; | ||
|
|
||
| // Wrapper around v8impl::Persistent. | ||
|
|
@@ -379,7 +393,7 @@ class Reference : public RefBase { | |
| static Reference* New(napi_env env, | ||
| v8::Local<v8::Value> value, | ||
| uint32_t initial_refcount, | ||
| bool delete_self, | ||
| Ownership ownership, | ||
| napi_finalize finalize_callback = nullptr, | ||
| void* finalize_data = nullptr, | ||
| void* finalize_hint = nullptr); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // Flags: --expose-gc | ||
|
|
||
| 'use strict'; | ||
| const common = require('../../common'); | ||
| const addon = require(`./build/${common.buildType}/6_object_wrap`); | ||
|
|
||
| (function scope() { | ||
| addon.objectWrapDanglingReference({}); | ||
| })(); | ||
|
|
||
| common.gcUntil('object-wrap-ref', () => { | ||
| return addon.objectWrapDanglingReferenceTest(); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.