-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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: run Node-API finalizers in GC second pass #42515
Conversation
Review requested:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to it, we are adding new node_api_set_finalizer_error_handler public API to setup an error handler per napi_env instance which is created per each native module. Each native module can handle its finalizer JS errors on its own.
Why? This seems like a bad idea. Addons that need to handle errors should explicitly check for exceptions rather than installing semi-global handlers for that.
This is only half the truth – the significantly bigger issue is that the finalizers may run at any point during other synchronous JavaScript execution if GC happens to kick in. |
FWIW, in most cases, the second pass callbacks are called in the platform idle tasks queue. However, there are cases where the second pass callbacks are invoked synchronously :
I'd agree that allowing arbitrary JavaScript to run during second callbacks is a bad idea. I'm wondering if there are chances that we can call into addons with disallowing JavaScript execution to give addons a chance to release external memory, especially when the program reached the isolate's external memory limit? |
It is a "safety net" for developers who work on a big native addons. When a big team works on an addon, they individually may not know all aspects of the addon. Having an error handler per addon may help the team to weed out any accidental issues that they introduced. Otherwise, an unhandled error will cause Node.js to crash globally, and someone else in production environment must do the extra work to attribute that crash to the specific addon and notify the team. |
i'm also not a fan of this approach. it sounds like your issue is less about finalizer errors and more about review culture on these teams. |
This is the main goal of this PR. Without running the finalizers as a part of the GC we cannot free the native resources on-time and in some scenarios it causes a pseudo memory leak (see nodejs/node-addon-api#1140). Is your concern of running JS code as a part of finalizers as @legendecas described above? |
Agree. Having a better review culture can address some issues. Though, in real code that may have multiple levels of indirections it is practically impossible to find all places that may accidently cause JS errors thrown, unless we completely prohibit touching JS code in the finalizers. Note that our docs, samples, tests, or examples never mention or show how to handle JS errors in finalizers. Also, I do not completely understand why handling unhandled exceptions in the global process-wide level is a good and a practical thing but giving the opportunity to the native addon developers to do the same for their code is a bad practice. |
It sounds like we may need to have two classes of finalizers:
Is it correct? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does the finalizer error handler have to be a napi_value
? Why not a C function pointer?
This PR was discussed today in Node-API meeting. The focus was mostly on whether we must introduce new "pure" finalizers.
|
PR #42651 adds the "pure" finalizers. |
Seeing that unhandled exceptions can be handled today from JS code, I thought that making the finalizer error handler to be a JS function will make it follow the same lines. |
Closing this PR since the idea of having a handler for finalizer errors seems to be unpopular. |
The issue
Currently
Reference
finalizers are run inside ofSetImmediate
.In case if user code creates a lot of native objects in the main script, it could cause a significant memory pressure, even if the objects are properly released. This is because they are "collected" only inside of
SetImmediate
that follows the script run.See issue: nodejs/node-addon-api#1140
In the a74a6e3 commit the processing of finalizers was moved from the GC second pass to the
SetImmediate
because finalizers may throw JavaScript exceptions which can affect behavior of other functions.If the JavaScript exception is thrown inside of
SetImmediate
, then it causes an unhandled exception which can be handled process-wide with help ofprocess.on('uncaughtException', ...)
event handler.The solution
In this PR we are switching back to processing finalizers in the GC second pass which may happen any time after GC calls.
To address the issue of handling JS exceptions from finalizers we do the following in this PR:
SetImmediate
. Thus, we address the previous issue with the finalizer JS errors interrupting other functions, and align the error handling behavior withSetImmediate
.node_api_set_finalizer_error_handler
public API to setup an error handler pernapi_env
instance which is created per each native module. Each native module can handle its finalizer JS errors on its own.Tests
New test_finalizer_exception test is added to js-native-api to verify the new behavior.
All other js-native-api, node-api, and cctests are passing.
Documentation
The n-api.md documentation is updated with the new
node_api_set_finalizer_error_handler
public API.See the n-api.md for the details about the
node_api_set_finalizer_error_handler
function.