nodejs/node · #66482
process: return unregister fn from finalization
doc/api/process.md19 + / 0 −
@@ -2063,6 +2063,9 @@ Since it's not possible to build Node.js without libuv, this value is always `tr <!-- YAML added: v22.5.0 changes:+ - version: REPLACEME+ pr-url: https://github.com/nodejs/node/pull/66482+ description: Returns a function that removes the registration. - version: v26.12.0 pr-url: https://github.com/nodejs/node/pull/66436 description: Marked as release candidate.@@ -2075,6 +2078,7 @@ changes: is finalized. * `ref` {Object | Function} The reference to the resource that is being tracked. * `event` {string} The event that triggered the finalization. Defaults to 'exit'.+* Returns: {Function} A function that removes this registration when called. This function registers a callback to be called when the process emits the `exit` event if the `ref` object was not garbage collected. If the object `ref` was garbage collected@@ -2171,11 +2175,25 @@ but if it is not, `dispose` will be called when `process.exit` is called. Be careful and avoid relying on this feature for the disposal of critical resources, as it is not guaranteed that the callback will be called under all circumstances. +The returned function removes only this registration, leaving other+registrations for the same `ref` in place. Calling it more than once has no+effect. It does not hold a strong reference to `ref`.++```js+const unregister = finalization.register(myDisposableObject, onFinalize);++// Later, once the resource has been released manually:+unregister();+```+ ## `process.finalization.registerBeforeExit(ref, callback)` <!-- YAML added: v22.5.0 changes:+ - version: REPLACEME+ pr-url: https://github.com/nodejs/node/pull/66482+ description: Returns a function that removes the registration. - version: v26.12.0 pr-url: https://github.com/nodejs/node/pull/66436 description: Marked as release candidate.@@ -2189,6 +2207,7 @@ changes: is finalized. * `ref` {Object | Function} The reference to the resource that is being tracked. * `event` {string} The event that triggered the finalization. Defaults to 'beforeExit'.+* Returns: {Function} A function that removes this registration when called. This function behaves exactly like the `register`, except that the callback will be called when the process emits the `beforeExit` event if `ref` object was not garbage collected.lib/internal/process/finalization.js18 + / 5 −
@@ -84,33 +84,45 @@ function createFinalization() { ref.fn = fn; registry ||= new SafeFinalizationRegistry(clear);- registry.register(obj, ref);+ registry.register(obj, ref, ref); refs[event].add(ref);++ // The returned function must not capture `obj`, otherwise it would+ // never be garbage collected while the caller holds the function.+ return function unregister() {+ if (!refs[event].delete(ref)) {+ return;+ }+ registry.unregister(ref);+ uninstall(event);+ }; } /** * Execute the given function when the process exits, * and clean things up when the object is gc. * @param {any} obj * @param {Function} fn+ * @returns {Function} A function that removes this registration. */ function register(obj, fn) { validateObject(obj, 'obj', kValidateObjectAllowFunction); - _register('exit', obj, fn);+ return _register('exit', obj, fn); } /** * Execute the given function before the process exits, * and clean things up when the object is gc. * @param {any} obj * @param {Function} fn+ * @returns {Function} A function that removes this registration. */ function registerBeforeExit(obj, fn) { validateObject(obj, 'obj', kValidateObjectAllowFunction); - _register('beforeExit', obj, fn);+ return _register('beforeExit', obj, fn); } /**@@ -121,16 +133,17 @@ function createFinalization() { if (!registry) { return; }- registry.unregister(obj); for (const event of ['exit', 'beforeExit']) { for (const ref of refs[event]) { const _obj = ref.deref(); if (!_obj || _obj === obj) { refs[event].delete(ref);+ registry.unregister(ref); } }- uninstall(event); }+ uninstall('exit');+ uninstall('beforeExit'); } return {test/fixtures/process/unregister-function.mjsadded29 + / 0 −
@@ -0,0 +1,29 @@+import { strictEqual } from 'assert'++const calls = []++function onExit(obj, event) {+ calls.push(`${obj.name}:${event}`)+}++const a = { name: 'a' }+const b = { name: 'b' }++const unregisterA = process.finalization.register(a, onExit)+const unregisterABeforeExit = process.finalization.registerBeforeExit(a, onExit)+process.finalization.register(b, onExit)+const unregisterBAgain = process.finalization.register(b, onExit)++strictEqual(typeof unregisterA, 'function')+strictEqual(typeof unregisterABeforeExit, 'function')++unregisterA()+unregisterA() // twice, this should not throw+unregisterABeforeExit()++// Removing one registration keeps the others for the same object.+unregisterBAgain()++process.on('exit', function () {+ strictEqual(calls.join(','), 'b:exit')+})test/parallel/test-process-finalization.mjs1 + / 0 −
@@ -12,6 +12,7 @@ const files = [ 'finalization-cleanup.mjs', 'gc-not-close.mjs', 'unregister.mjs',+ 'unregister-function.mjs', 'different-registry-per-thread.mjs', ];