Repository navigation
Fatal error when using N-API async code from process exit hooks #591
Description
Activity
Hi @rolftimmermans ,
I don't think you can perform asynchronous cleanup on the
exitevent, see:➜ ~ node -e 'process.on("exit", ()=>{console.log("exit"); setTimeout( ()=>{ console.log("timeout!") }, 10000); });' exitThat makes sense. I just prefer to avoid the crash. 🙂
Yeaaah that makes sense 😄 Well, I think this is an underlying napi issue. I would imagine the
napi_create_async_workshould return an error if the env is cleaned up, but I don't know the internals too much. Thoughts @gabrielschulhof ?It might be bigger than only
napi_create_async_work, I posted this just as an example. FWIW I first ran into this behaviour with various N-API methods insideuv_poll_t/uv_poll_startcallbacks within the on-exit handler. (I use libuv directly here because there is no N-API equivalent.) Some appear to work (creating objects?) and others don't (resolving promises, setting object properties...?), but the resulting fatal error is the same as above.Thinking about this more I'd be really happy if
node-addon-apicould detect this situation and throw a sensible JS exception.Reacted by Kevin EadyI took a quick look and in
node_main_instance.ccwere the main loop is and EmitExit is called to trigger the "exit" event I don't see code that sets a bit etc that we could check to see that the process is existing.EmitExit does as boolean property
_exitingon the process object but checking that from C code would not make sense.Even if we added something that could be easily checked in the C N-API methods (or by node-addon-api through a new method) I'm not sure the performance cost doing that check would be acceptable to provide a better error in this case.
Is it possible to check such a condition after an error occurs? It's just that the crash that happens now is very unclear. For a consumer of an N-API library it's not easy to figure out why this is happening. I don't expect this to work, just a clearer (maaaybe, if possible, even a custom) error message...
There would have to be some sort of crash handler which would be able to figure out
- code was running as a result of the exit hook
- async code had caused the crash
I don't have any good ideas of how to do that easily.
This issue is stale because it has been open many days with no activity. It will be closed soon unless the stale label is removed or a comment is made.
I'm also seeing this from native code that's calling back into Javascript via a
ThreadSafeFunction. It's really hard to synchronize cleanup of these callbacks with the env context going away during shutdown. Having an API for querying the liveliness ofenvwould really help here (env->can_call_into_js()being exposed to native code).
When calling N-API based async code (in particular written with
none-addon-api) from a process exit hookprocess.on("exit")it leads to a fatal error.This is very easy to reproduce. For example, take the
asyncworker.jstests and put the last block within aprocess.on("exit")callback. Like thisRunning as
node --expose-gc test/asyncworker.jsthe process is aborted and I see the following stack trace. Tested with Node.js 12.12.0 on macOS 10.14.I would expect:
I am running into this issue with zeromq.js, where I can't control users calling into the native code from an exit hook and this leads to similar fatal errors. Maybe there could be a way to check if the process/thread is exiting from within the native code?