Description
In Napi-6 mode, InstanceData registers a drop_queue threadsafe function, which ensures that leaked resources correctly get disposed. However, that TSFN itself is being reported by Jest, why-is-node-running and others (simply "Jest" thereafter) as leaked resources, since that function is never destroyed.
How to reproduce
InstanceData is lazily initialized. The issue can be observed by running any Jest test that forces initialization of InstanceData, notably by creating a Root object or calling cx.channel(). For example:
In rust:
pub fn test_inner_data_drop_queue_leak(mut cx: FunctionContext) -> JsResult<JsObject> {
let some_object = cx.empty_object().root(&mut cx).into_inner(&mut cx);
Ok(some_object)
}
In JavaScript:
test('httpWorkflow with mock activity', () => {
(native as any).testInnerDataDropQueueLeak();
});
Then running using:
npx jest --detectOpenHandles
Results in this Jest warning message:
Jest has detected the following 1 open handle potentially keeping Jest from exiting:
● neon threadsafe function
Discussion and Proposed solution
This is technically a false-positive, since the TSFN is .unref() so that it will not keep the process alive. Unfortunately, Node's async hook callbacks API doesn't provide an official way to know that an async resource has been unref(). There is therefore no way for Jest to know that the TSFN can safely be ignored, by relying on Node's API alone.
Instead, Jest looks at the async resource for the presence of a hasRef() function. If that function exists on the async resource object, then Jest will call that function to determine if the resource is still active. why-is-node-running also support that function, and Node itself has added this function on some of its own resources specifically for the purpose of helping Jest and the like properly detect leaks.
It is therefore suggested that a hasRef() function should be added to the async resource associated TSFNs. That function would simply return either true or false, depending on either the TSFN is referenced() or unref().
Description
In Napi-6 mode,
InstanceDataregisters adrop_queuethreadsafe function, which ensures that leaked resources correctly get disposed. However, that TSFN itself is being reported by Jest,why-is-node-runningand others (simply "Jest" thereafter) as leaked resources, since that function is never destroyed.How to reproduce
InstanceDatais lazily initialized. The issue can be observed by running any Jest test that forces initialization ofInstanceData, notably by creating aRootobject or callingcx.channel(). For example:In rust:
In JavaScript:
Then running using:
Results in this Jest warning message:
Discussion and Proposed solution
This is technically a false-positive, since the TSFN is
.unref()so that it will not keep the process alive. Unfortunately, Node's async hook callbacks API doesn't provide an official way to know that an async resource has beenunref(). There is therefore no way for Jest to know that the TSFN can safely be ignored, by relying on Node's API alone.Instead, Jest looks at the async resource for the presence of a
hasRef()function. If that function exists on the async resource object, then Jest will call that function to determine if the resource is still active. why-is-node-running also support that function, and Node itself has added this function on some of its own resources specifically for the purpose of helping Jest and the like properly detect leaks.It is therefore suggested that a
hasRef()function should be added to the async resource associated TSFNs. That function would simply return either true or false, depending on either the TSFN isreferenced()orunref().