-
-
Notifications
You must be signed in to change notification settings - Fork 34.1k
node-api: run finalizers directly from GC #42651
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
Closed
Closed
Changes from 1 commit
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
f144c01
node-api: run finalizers directly from GC
vmoroz 929a0ef
fix disabling JS in finalizers
vmoroz e5e2a60
add unit test for pure finalizers
vmoroz 41e61e1
add test to crash on GC state access and update docs
vmoroz f199c72
add CheckGCAccess calls where code may access GC
vmoroz 2edf5af
fix code and test for Linux
vmoroz 971b894
fix JS linter error to use assert.strictEqual
vmoroz 5ef58d3
Address PR feedback
vmoroz 03d16c7
remove Call_while_GC.txt that accidentally added
vmoroz 25bfcaf
address PR feedback
vmoroz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
add unit test for pure finalizers
- Loading branch information
commit e5e2a60fa810f74c6046e6093dbb60449d774cf1
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| { | ||
| "targets": [ | ||
| { | ||
| "target_name": "test_finalizer", | ||
| "defines": [ "NAPI_EXPERIMENTAL" ], | ||
| "sources": [ | ||
| "../entry_point.c", | ||
| "test_finalizer.c" | ||
| ] | ||
| } | ||
| ] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| 'use strict'; | ||
| // Flags: --expose-gc | ||
|
|
||
| const common = require('../../common'); | ||
| const test_finalizer = require(`./build/${common.buildType}/test_finalizer`); | ||
| const assert = require('assert'); | ||
|
|
||
| function addFinalizer() { | ||
| // Define obj in a function context to let it GC-collected. | ||
| const obj = {}; | ||
| test_finalizer.addFinalizer(obj); | ||
| } | ||
|
|
||
| addFinalizer(); | ||
|
|
||
| for (let i = 0; i < 1000; ++i) { | ||
| global.gc(); | ||
| if (test_finalizer.getFinalizerCallCount() === 1) { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| assert(test_finalizer.getFinalizerCallCount() === 1); | ||
|
|
||
| async function runAsyncTests() { | ||
| let js_is_called = false; | ||
vmoroz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| (() => { | ||
| const obj = {}; | ||
| test_finalizer.addFinalizerWithJS(obj, () => { js_is_called = true; }); | ||
| })(); | ||
| await common.gcUntil('test JS finalizer', | ||
| () => (test_finalizer.getFinalizerCallCount() === 2)); | ||
| assert(js_is_called); | ||
| } | ||
| runAsyncTests(); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| #include <js_native_api.h> | ||
| #include <stdint.h> | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #include "../common.h" | ||
|
|
||
| typedef struct { | ||
| int32_t finalize_count; | ||
| napi_ref js_func; | ||
| } FinalizerData; | ||
|
|
||
| static void finalizerOnlyCallback(napi_env env, | ||
| void* finalize_data, | ||
| void* finalize_hint) { | ||
| FinalizerData* data = (FinalizerData*)finalize_data; | ||
| ++data->finalize_count; | ||
| } | ||
|
|
||
| static void finalizerCallingJSCallback(napi_env env, | ||
| void* finalize_data, | ||
| void* finalize_hint) { | ||
| napi_value js_func, undefined; | ||
| FinalizerData* data = (FinalizerData*)finalize_data; | ||
| NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, data->js_func, &js_func)); | ||
| NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined)); | ||
| NODE_API_CALL_RETURN_VOID(env, napi_call_function(env, undefined, js_func, 0, NULL, NULL)); | ||
| NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_func)); | ||
| data->js_func = NULL; | ||
| ++data->finalize_count; | ||
| } | ||
|
|
||
| // Schedule async finalizer to run JavaScript-touching code. | ||
| static void finalizerWithJSCallback(napi_env env, | ||
| void* finalize_data, | ||
| void* finalize_hint) { | ||
| FinalizerData* data = (FinalizerData*)finalize_data; | ||
| NODE_API_CALL_RETURN_VOID(env, node_api_post_finalizer( | ||
| env, finalizerCallingJSCallback, finalize_data, finalize_hint)); | ||
| } | ||
|
|
||
| static napi_value addFinalizer(napi_env env, napi_callback_info info) { | ||
| size_t argc = 1; | ||
| napi_value argv[1]; | ||
| FinalizerData* data; | ||
|
|
||
| NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); | ||
| NODE_API_CALL(env, napi_get_instance_data(env, &data)); | ||
| NODE_API_CALL(env, | ||
| napi_add_finalizer( | ||
| env, argv[0], data, finalizerOnlyCallback, NULL, NULL)); | ||
| return NULL; | ||
| } | ||
|
|
||
| // This finalizer is going to call JavaScript from finalizer | ||
| static napi_value addFinalizerWithJS(napi_env env, napi_callback_info info) { | ||
| size_t argc = 2; | ||
| napi_value argv[2] = {0}; | ||
| napi_valuetype arg_type; | ||
| FinalizerData* data; | ||
|
|
||
| NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); | ||
| NODE_API_CALL(env, napi_get_instance_data(env, &data)); | ||
| NODE_API_CALL(env, napi_typeof(env, argv[1], &arg_type)); | ||
| NODE_API_ASSERT( | ||
| env, arg_type == napi_function, "Expected function as the second arg"); | ||
| NODE_API_CALL(env, napi_create_reference(env, argv[1], 1, &data->js_func)); | ||
| NODE_API_CALL(env, | ||
| napi_add_finalizer( | ||
| env, argv[0], data, finalizerWithJSCallback, NULL, NULL)); | ||
| return NULL; | ||
| } | ||
|
|
||
| static napi_value getFinalizerCallCount(napi_env env, napi_callback_info info) { | ||
| size_t argc = 1; | ||
| napi_value argv[1]; | ||
| FinalizerData* data; | ||
| napi_value result; | ||
|
|
||
| NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); | ||
| NODE_API_CALL(env, napi_get_instance_data(env, &data)); | ||
| NODE_API_CALL(env, napi_create_int32(env, data->finalize_count, &result)); | ||
| return result; | ||
| } | ||
|
|
||
| static void finalizeData(napi_env env, void* data, void* hint) { | ||
| free(data); | ||
| } | ||
|
|
||
| EXTERN_C_START | ||
| napi_value Init(napi_env env, napi_value exports) { | ||
| FinalizerData* data = (FinalizerData*)malloc(sizeof(FinalizerData)); | ||
| NODE_API_ASSERT(env, data != NULL, "Failed to allocate memory"); | ||
| memset(data, 0, sizeof(FinalizerData)); | ||
| NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, NULL)); | ||
| napi_property_descriptor descriptors[] = { | ||
| DECLARE_NODE_API_PROPERTY("addFinalizer", addFinalizer), | ||
| DECLARE_NODE_API_PROPERTY("addFinalizerWithJS", addFinalizerWithJS), | ||
| DECLARE_NODE_API_PROPERTY("getFinalizerCallCount", | ||
| getFinalizerCallCount)}; | ||
|
|
||
| NODE_API_CALL( | ||
| env, | ||
| napi_define_properties(env, | ||
| exports, | ||
| sizeof(descriptors) / sizeof(*descriptors), | ||
| descriptors)); | ||
|
|
||
| return exports; | ||
| } | ||
| EXTERN_C_END |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.