Skip to content
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

node-api: generalize finalizer second pass callback #44141

Closed
wants to merge 2 commits into from

Conversation

legendecas
Copy link
Member

@legendecas legendecas commented Aug 5, 2022

Generalize the finalizer's second pass callback to make it cancellable and simplify the code around the second pass callback.

With this change, it is determined that Reference::Finalize/RefBase::Finalize are called once, either from the env's shutdown, or from the env's second pass callback.

All existing node-api js tests should pass without a touch. The js_native_api cctest is no longer applicable with this change, just removing it.

Refs: #44071

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/gyp
  • @nodejs/node-api

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Aug 5, 2022
@legendecas legendecas force-pushed the node-api/second-pass branch from 486add4 to 40562fc Compare August 5, 2022 07:17
@legendecas legendecas added the node-api Issues and PRs related to the Node-API. label Aug 5, 2022
@legendecas legendecas force-pushed the node-api/second-pass branch 2 times, most recently from e9ac1e2 to d71cdd6 Compare August 5, 2022 07:27
@legendecas legendecas marked this pull request as ready for review August 5, 2022 07:41
@legendecas legendecas added the request-ci Add this label to start a Jenkins CI on a PR. label Aug 5, 2022
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 5, 2022
@nodejs-github-bot

This comment was marked as outdated.

@legendecas legendecas force-pushed the node-api/second-pass branch from d71cdd6 to 2a14bf3 Compare August 7, 2022 06:13
@legendecas legendecas added the request-ci Add this label to start a Jenkins CI on a PR. label Aug 7, 2022
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 7, 2022
@nodejs-github-bot
Copy link
Collaborator

nodejs-github-bot commented Aug 7, 2022

@nodejs-github-bot
Copy link
Collaborator

nodejs-github-bot commented Aug 9, 2022

@nodejs-github-bot
Copy link
Collaborator

if (!finalization_scheduled) {
finalization_scheduled = true;
Ref();
node_env()->SetImmediate([this](node::Environment* node_env) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capturing this is not safe: if the lambda is not executed, then the env instance is never released. It would be better to use the EnvRefHolder here which is being deleted in this PR from js_native_api_v8.h. This way the Unref() is always called when the lambda is released.
Also, when we call the Unref() below we must be prepared that it was the last Unref() that causes the env instance destruction. In such case the DrainFinalizerQueue() may be called against deleted object and cause issues.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The callback of Environment::SetImmediate is deemed to be called. So we don't need additional setups for the condition that the callback is not invoked.

while (!pending_finalizers.empty()) {
v8impl::RefTracker* ref_tracker = *pending_finalizers.begin();
pending_finalizers.erase(ref_tracker);
ref_tracker->Finalize();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we handle uncaught JS exceptions? Previously the SetImmediate code was taking care about the exceptions. It may be worth adding a test where we have a few finalizers running and they all throw JS exceptions. The JS code should handle and ignore those exceptions - we must see all the finalizers executed.
Also, we need to test that if JS does not handle the uncaught exceptions, then we crash as usual.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node_napi_env__::CallFinalizer still handles JS exception. Native SetImmediate doesn't handle JS exceptions.

src/node_api.cc Outdated Show resolved Hide resolved
juanarbol pushed a commit that referenced this pull request Oct 11, 2022
Functions declared in anonymous namespaces are not necessarily to be
marked as static.

PR-URL: #44301
Refs: #44141
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Michael Dawson <midawson@redhat.com>
@legendecas legendecas force-pushed the node-api/second-pass branch 3 times, most recently from 9416d37 to c6b1b47 Compare October 14, 2022 07:13
@legendecas
Copy link
Member Author

legendecas commented Oct 14, 2022

@mhdawson thank you for verifying the patch with node-addon-api test sets.

I believe the problem here is what napi_remove_wrap should do when the out-param napi_ref result is set on napi_wrap. The document said that:

napi_remove_wrap:
Retrieves a native instance that was previously wrapped in the JavaScript object js_object using napi_wrap() and removes the wrapping. If a finalizer callback was associated with the wrapping, it will no longer be called when the JavaScript object becomes garbage-collected.
https://nodejs.org/api/n-api.html#napi_remove_wrap

However, I found that on the main branch, when the napi_remove_wrap is called but the napi_delete_reference is not called with the out-param napi_ref result, the finalizer can still be invoked. I've added a test case for it: https://github.com/nodejs/node/pull/44141/files#diff-8037a0113a35b91ac2d997501123d0b8be5b341907e9e350fe4f70082afa361f.

So basically, when the napi_wrap is called with non-null out-paramnapi_ref result, the expected behavior would be:

  1. invoke napi_remove_wrap with the wrapped object to invalidate the wrap, after this point no finalizer should be invoked, and napi_get_reference_value can still get the ref-ed value from the out-param napi_ref result.
  2. invoke napi_delete_reference with the out-param napi_ref result, to delete the user-owned napi_ref.

legendecas and others added 2 commits November 18, 2022 09:33
Generalize the finalizer's second pass callback to make it
cancellable and simplify the code around the second pass callback.

With this change, it is determined that Reference::Finalize or
RefBase::Finalize are called once, either from the env's shutdown,
or from the env's second pass callback.

All existing node-api js tests should pass without a touch. The
js_native_api cctest is no longer applicable with this change,
just removing it.
@legendecas legendecas added the request-ci Add this label to start a Jenkins CI on a PR. label Nov 18, 2022
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Nov 18, 2022
@nodejs-github-bot
Copy link
Collaborator

@legendecas
Copy link
Member Author

@mhdawson updated, PTAL again, thank you :)

Copy link
Member

@mhdawson mhdawson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I ran the node-addon-api tests 10000 times with these changes and they all passed.

legendecas added a commit that referenced this pull request Dec 19, 2022
Generalize the finalizer's second pass callback to make it
cancellable and simplify the code around the second pass callback.

With this change, it is determined that Reference::Finalize or
RefBase::Finalize are called once, either from the env's shutdown,
or from the env's second pass callback.

All existing node-api js tests should pass without a touch. The
js_native_api cctest is no longer applicable with this change,
just removing it.

PR-URL: #44141
Refs: #44071
Reviewed-By: Michael Dawson <midawson@redhat.com>
@legendecas
Copy link
Member Author

Landed in f14fa1b

@legendecas legendecas closed this Dec 19, 2022
@legendecas legendecas deleted the node-api/second-pass branch December 19, 2022 17:45
toyobayashi added a commit to toyobayashi/emnapi that referenced this pull request Dec 26, 2022
targos pushed a commit that referenced this pull request Jan 1, 2023
Generalize the finalizer's second pass callback to make it
cancellable and simplify the code around the second pass callback.

With this change, it is determined that Reference::Finalize or
RefBase::Finalize are called once, either from the env's shutdown,
or from the env's second pass callback.

All existing node-api js tests should pass without a touch. The
js_native_api cctest is no longer applicable with this change,
just removing it.

PR-URL: #44141
Refs: #44071
Reviewed-By: Michael Dawson <midawson@redhat.com>
@RafaelGSS RafaelGSS mentioned this pull request Jan 2, 2023
guangwong pushed a commit to noslate-project/node that referenced this pull request Jan 3, 2023
Functions declared in anonymous namespaces are not necessarily to be
marked as static.

PR-URL: nodejs/node#44301
Refs: nodejs/node#44141
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Michael Dawson <midawson@redhat.com>
guangwong pushed a commit to noslate-project/node that referenced this pull request Jan 3, 2023
Functions declared in anonymous namespaces are not necessarily to be
marked as static.

PR-URL: nodejs/node#44301
Refs: nodejs/node#44141
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Michael Dawson <midawson@redhat.com>
RafaelGSS pushed a commit that referenced this pull request Jan 4, 2023
Generalize the finalizer's second pass callback to make it
cancellable and simplify the code around the second pass callback.

With this change, it is determined that Reference::Finalize or
RefBase::Finalize are called once, either from the env's shutdown,
or from the env's second pass callback.

All existing node-api js tests should pass without a touch. The
js_native_api cctest is no longer applicable with this change,
just removing it.

PR-URL: #44141
Refs: #44071
Reviewed-By: Michael Dawson <midawson@redhat.com>
RafaelGSS pushed a commit that referenced this pull request Jan 5, 2023
Generalize the finalizer's second pass callback to make it
cancellable and simplify the code around the second pass callback.

With this change, it is determined that Reference::Finalize or
RefBase::Finalize are called once, either from the env's shutdown,
or from the env's second pass callback.

All existing node-api js tests should pass without a touch. The
js_native_api cctest is no longer applicable with this change,
just removing it.

PR-URL: #44141
Refs: #44071
Reviewed-By: Michael Dawson <midawson@redhat.com>
juanarbol pushed a commit that referenced this pull request Jan 26, 2023
Generalize the finalizer's second pass callback to make it
cancellable and simplify the code around the second pass callback.

With this change, it is determined that Reference::Finalize or
RefBase::Finalize are called once, either from the env's shutdown,
or from the env's second pass callback.

All existing node-api js tests should pass without a touch. The
js_native_api cctest is no longer applicable with this change,
just removing it.

PR-URL: #44141
Refs: #44071
Reviewed-By: Michael Dawson <midawson@redhat.com>
@juanarbol juanarbol mentioned this pull request Jan 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. node-api Issues and PRs related to the Node-API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants