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

src: add support for nogc types via NogcEnv #1514

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

KevinEady
Copy link
Contributor

@KevinEady KevinEady commented Jun 6, 2024

  • Introduce Napi::NogcEnv class, taking the place of Napi::Env in finalizer callbacks.
  • Introduce void Napi::NogcEnv::AddPostFinalizer() and overloads.
  • Modify tests to support above changes

TODO:

@codecov-commenter
Copy link

codecov-commenter commented Jun 6, 2024

Codecov Report

Attention: Patch coverage is 69.38776% with 15 lines in your changes missing coverage. Please review.

Project coverage is 64.40%. Comparing base (bf49519) to head (6591a36).
Report is 3 commits behind head on main.

Files Patch % Lines
napi-inl.h 70.83% 2 Missing and 12 partials ⚠️
napi.h 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1514      +/-   ##
==========================================
- Coverage   64.69%   64.40%   -0.30%     
==========================================
  Files           3        3              
  Lines        1997     2003       +6     
  Branches      687      693       +6     
==========================================
- Hits         1292     1290       -2     
- Misses        144      146       +2     
- Partials      561      567       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@KevinEady
Copy link
Contributor Author

@vmoroz your comment on removing const ref on std::nullptr_t has been addressed, and @mhdawson your comment on reference deletion has been addressed, both in 2a689b6

matrix:
node-version: [ 18.x, 20.x, 21.x, 22.x ]
api_version:
Copy link
Member

Choose a reason for hiding this comment

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

Should we add checks for the standard/experimental to ci-win.yml too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good thought... I pretty much copied Gabriel's CI file. I'll comment on his PR to verify, since mine would end up being overwritten when i rebase to master after his is merged.

@mhdawson
Copy link
Member

Agreed we should do a release to get out the previous fixe that @gabrielschulhof made and then put this out in a SemVer major.

@KevinEady KevinEady force-pushed the add-nogc-types branch 2 times, most recently from 7926d7a to bcdde7b Compare June 15, 2024 18:57
@KevinEady
Copy link
Contributor Author

Hi team,

Using some SFINAE magic, I was able to determine at compile time how to associate the finalizer -- either directly or with node_api_post_finalizer. This would address #1367 as implemented.

This means no changes were needed to any existing tests, and I created a new test in finalizer_order to ensure finalizers are being called correctly.

PTAL! @gabrielschulhof @vmoroz @legendecas @mhdawson

Copy link
Contributor

@gabrielschulhof gabrielschulhof left a comment

Choose a reason for hiding this comment

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

Looks good!

I think the Gc vs. NoGc nomenclature is a bit confusing. Unfortunately, we established this in core. I think maybe we should go with DuringGc and OutsideGc. For env, we'd have DuringGcEnv and Env, since Env is already established, but for the finalizer type names I think we can go with DuringGc and OutsideGc for clarity.

if (isExperimental) {
assert.strictEqual(binding.finalizer_order.Test.isNogcFinalizerCalled, true, 'Expected nogc finalizer to be called [before ticking]');
assert.strictEqual(binding.finalizer_order.Test.isGcFinalizerCalled, false, 'Expected gc finalizer to not be called [before ticking]');
assert.strictEqual(isCallbackCalled, false, 'Expected callback not be called [before ticking]');
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
assert.strictEqual(isCallbackCalled, false, 'Expected callback not be called [before ticking]');
assert.strictEqual(isCallbackCalled, false, 'Expected callback to not be called [before ticking]');

napi.h Outdated
@@ -2415,6 +2448,7 @@ class ObjectWrap : public InstanceWrap<T>, public Reference<Object> {
napi_property_attributes attributes = napi_default);
static Napi::Value OnCalledAsFunction(const Napi::CallbackInfo& callbackInfo);
virtual void Finalize(Napi::Env env);
virtual void Finalize(NogcEnv env);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
virtual void Finalize(NogcEnv env);
virtual void Finalize(Napi::NogcEnv env);

@gabrielschulhof
Copy link
Contributor

I tested this with Napi::External and it works great!

const { makeSync, makeAsync } = require('bindings')('test_sync_fini')

const doAsync = process.argv[2] === 'async'

;(function test(iter = 0) {
  for (let i = 0; i < 1000000; i++) {
    const id = `${iter}:${i}`
    if (doAsync) {
      makeAsync(id)
    } else {
      makeSync(id)
    }
  }
  setTimeout(test, 0, iter + 1)
})()

Here's a video of the output. Guess which column is async 🙂

Screen.Recording.2024-06-24.at.8.46.57.AM.mov

@gabrielschulhof
Copy link
Contributor

We should rename AddPostFinalizer to just PostFinalizer.

@KevinEady
Copy link
Contributor Author

@gabrielschulhof ,

For env, we'd have DuringGcEnv and Env, since Env is already established, ...

One could argue that being a Node-API wrapper, the naming in this library should be as consistent with Node-API as possible. The (almost) 1-to-1 mapping helps link up with other documentation (eg. referencing the Node-API docs), or translating between Node-API C code and node-addon-api C++ code.

I do agree though, the name DuringGcEnv better describes the restriction ("during GC") on how the environment can be used, but not sure if we should stray from the Node-API names.


... but for the finalizer type names I think we can go with DuringGc and OutsideGc for clarity.

By "finalizer type names" do you mean changing the Finalizer in places eg.:

template <typename Finalizer>
static External New(napi_env env, T* data, Finalizer finalizeCallback);

If so, I don't think we should change this name since the finalizer passed can accept either a DuringGcEnv or Env. A Finalizer is a callback ran either during the GC cycle (if using DuringGcEnv) or on a future tick after the GC cycle completes (if using Env). This means the concept of "finalization" is dependent on your callback signature, and would be documented as such.


We should rename AddPostFinalizer to just PostFinalizer.

Makes sense, esp. considering what I said previously.


While on the subject of documentation: we don't have any specific entry for "finalization", as a finalizer callback is always documented on each individual object or function, eg:

  • Napi::External:
    `Napi::External` objects can be created with an optional Finalizer function and optional Hint value. The Finalizer function, if specified, is called when your `Napi::External` object is released by Node's garbage collector. It gives your code the opportunity to free any dynamically created data. If you specify a Hint value, it is passed to your Finalizer function.
  • Napi::Buffer::New:
    - `[in] finalizeCallback`: The function to be called when the `Napi::Buffer` is
    destroyed. It must implement `operator()`, accept an Napi::Env, a `T*` (which is the
    external data pointer), and return `void`.
  • and others

I propose we create a new top-level documentation page for finalization describing the above, and can have each use of finalizers in the documentation link to this page.

napi.h Outdated
private:
napi_env _env;
class NogcEnv {
protected:
Copy link
Contributor Author

@KevinEady KevinEady Jul 4, 2024

Choose a reason for hiding this comment

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

Should we make this private and have Env be a friend class? With it being protected, the default finalizers (as well as _env) would be exposed in any user-defined classes that extend NogcEnv.

EDIT: I went with this private-friend approach, making this comment out-dated. Can discuss in meeting.

rename NoGcEnv to BasicEnv, AddPostFinalizer to PostFinalizer, NoGc/GcFinalizer to Sync/AsyncFinalizer
@KevinEady
Copy link
Contributor Author

I've added some WIP documentation on the top-level finalization page: https://github.com/nodejs/node-addon-api/blob/6591a3609c2b94f22956b72736e06b86586556cf/doc/finalization.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants