-
Notifications
You must be signed in to change notification settings - Fork 41
Should finalizers run on exit in Node.js? #125
Comments
Also really easy to do from the node side (with a process exit handler) |
I'd argue, we should not run finalizers on exit in general. On the web, they will most likely not be run on page exit. Finalizers are unreliable, and trying to give them guarantees about being called on process exit might lead people to depend on them more than they should. Finalizers shouldn't be used to release external resources. If we leave them uncalled in this case, it's more likely that small tests will run into the issue, leading the bugs to get fixed. |
I agree. Do not run them on exit. |
I don't have a strong opinion one way or the other, just asked if it would be possible since I know there is the option in Java. The thought I'd add is that it would be much easier to add it later if there ends up being a compelling case versus trying to remove it later on. For that reason, I think leaving it out to start is a reasonable way to go. |
Note that doing this would almost certainly make shutdown significantly slower. I don't know much about v8's GC architecture, but would be surprised if it had to always do a full GC if an isolate is discarded. Doing that would be the only way to reliably run finalizers on exit. What's more, you'd have to run multiple GCs until no more finalizers are run, because those might make additional objects unreachable. |
I would like to bring Agents into this discussion. An Agent is defined as having its own thread. With these Agents, the exit of an Agent does not coincide with the exit of the process. So, at least in the case of native data being attached to JavaScript objects, if finalizers are not run on Agent exit, there will potentially be leaks as Agents are fired up and discarded. Now, in the browser, attaching native data to JS objects is always under the control of the browser itself so the maintainers of the browser can decide amongst themselves what mechanism to use for cleaning up the native data associated with an Agent when an Agent exits. In contrast, in Node.js we have an open architecture where native addons can associate native data with JavaScript objects where we have no control over the nature of that data. Thus, we have to provide a standard way of informing native addons that this data needs to be freed. Until the advent of worker threads, this was pretty much beyond the scope of the project, because the native data was always freed along with the whole segment at process exit. I guess the question is this: shall we go ahead and work on our own, Node.js standard way of informing native addons that the Agent (in our case the worker thread's @addaleax, given your comment, please help me out if I've missed anything 🙂 |
I doubt that any such agent-based cleanup should be tied to weakrefs. GC is inherently unreliable/ambiguous. Agent death is clear. Any resources held by an agent should not be handled by code within the agent, because that would imply that the agent isn't dead yet. We should also think about what the failure unit for preemptive termination, and for enabling others to cleanup after such a unit terminates. Erlang has a good model for cleanup following process death. Their process corresponds well to our agent. However, our shared array buffers (SABs) means that our agents are not shared-nothing units, and therefore are not separable re preemptive failure. If we only treat the agent cluster as the unit of preemptive failure, then we should think about agent-cluster death, and should seek to put cleanup logic in other agent clusters. In any case, I think non of this is relevant to weakrefs. |
Couldn't one argue that GC is fairly unambiguous when every value is about to go out of scope?
So, it sounds like the entity that fired up the Agent could be made to notify of the Agent's impending doom before actually initiating such doom, but that's outside the scope of the Agent. |
Except for resurrection bugs, which the weakref design took pains to avoid. const [z,wr] = (() => {
function x() {}
const wr = new WeakRef(x);
function z() { return void x; }
return [z, wr];
})(); Let's say that We can only cleanup an agent death reliably from outside the agent, after the agent itself is already dead. |
I think we have a solid answer to this question ("no"), so closing the issue. |
FWIW I understand that the general pattern here is to use an auxiliary cleanup set. Assuming all holdings are unique, when you register an external resource with a FinalizationGroup, you also add the resource's holding to an agent-global cleanup set. When the FinalizationGroup's callback runs, it cleans up the resource and removes the holding from the cleanup set. When the process exits, you explicitly clean up any remaining holdings in the cleanup set. So, IIUC Node can implement agent-death cleanup itself. |
Since worker threads are complete Node.js environments, including the ability to load native addons, and since those native addons can allocate resources to be freed when objects go out of scope, and since, upon worker thread exit, the engine does not invoke the weak callbacks responsible for freeing resources which still have references, this modification introduces tracking for weak references such that a list of outstanding weak references is maintained. This list is traversed during environment teardown. The callbacks for the remaining weak references are called. This change is also relevant for Node.js embedder scenarios, because in those cases the process also outlives the `node::Environment` and therefore weak callbacks should also be rendered as environment cleanup hooks to ensure proper cleanup after native addons. This changes introduces the means by which this can be accomplished. A benchmark is included which measures the time it takes to execute the weak reference callback for a given number of weak references. Re: tc39/proposal-weakrefs#125 (comment)
Since worker threads are complete Node.js environments, including the ability to load native addons, and since those native addons can allocate resources to be freed when objects go out of scope, and since, upon worker thread exit, the engine does not invoke the weak callbacks responsible for freeing resources which still have references, this modification introduces tracking for weak references such that a list of outstanding weak references is maintained. This list is traversed during environment teardown. The callbacks for the remaining weak references are called. This change is also relevant for Node.js embedder scenarios, because in those cases the process also outlives the `node::Environment` and therefore weak callbacks should also be rendered as environment cleanup hooks to ensure proper cleanup after native addons. This changes introduces the means by which this can be accomplished. A benchmark is included which measures the time it takes to execute the weak reference callback for a given number of weak references. Re: tc39/proposal-weakrefs#125 (comment) PR-URL: #28428 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
Since worker threads are complete Node.js environments, including the ability to load native addons, and since those native addons can allocate resources to be freed when objects go out of scope, and since, upon worker thread exit, the engine does not invoke the weak callbacks responsible for freeing resources which still have references, this modification introduces tracking for weak references such that a list of outstanding weak references is maintained. This list is traversed during environment teardown. The callbacks for the remaining weak references are called. This change is also relevant for Node.js embedder scenarios, because in those cases the process also outlives the `node::Environment` and therefore weak callbacks should also be rendered as environment cleanup hooks to ensure proper cleanup after native addons. This changes introduces the means by which this can be accomplished. A benchmark is included which measures the time it takes to execute the weak reference callback for a given number of weak references. Re: tc39/proposal-weakrefs#125 (comment) PR-URL: nodejs#28428 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
Since worker threads are complete Node.js environments, including the ability to load native addons, and since those native addons can allocate resources to be freed when objects go out of scope, and since, upon worker thread exit, the engine does not invoke the weak callbacks responsible for freeing resources which still have references, this modification introduces tracking for weak references such that a list of outstanding weak references is maintained. This list is traversed during environment teardown. The callbacks for the remaining weak references are called. This change is also relevant for Node.js embedder scenarios, because in those cases the process also outlives the `node::Environment` and therefore weak callbacks should also be rendered as environment cleanup hooks to ensure proper cleanup after native addons. This changes introduces the means by which this can be accomplished. A benchmark is included which measures the time it takes to execute the weak reference callback for a given number of weak references. Re: tc39/proposal-weakrefs#125 (comment) PR-URL: #28428 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
Since worker threads are complete Node.js environments, including the ability to load native addons, and since those native addons can allocate resources to be freed when objects go out of scope, and since, upon worker thread exit, the engine does not invoke the weak callbacks responsible for freeing resources which still have references, this modification introduces tracking for weak references such that a list of outstanding weak references is maintained. This list is traversed during environment teardown. The callbacks for the remaining weak references are called. This change is also relevant for Node.js embedder scenarios, because in those cases the process also outlives the `node::Environment` and therefore weak callbacks should also be rendered as environment cleanup hooks to ensure proper cleanup after native addons. This changes introduces the means by which this can be accomplished. A benchmark is included which measures the time it takes to execute the weak reference callback for a given number of weak references. Re: tc39/proposal-weakrefs#125 (comment) PR-URL: #28428 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
@mhdawson @littledan
Discussion was raised where in Java finalizers run on process exit.
In discussion people said they don't want people to put complicated logic in finalizers - so that might be a consideration.
The text was updated successfully, but these errors were encountered: