Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -2062,6 +2062,10 @@ 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.
-->

> Stability: 1.1 - Active Development
Expand All @@ -2071,6 +2075,7 @@ added: v22.5.0
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
Expand Down Expand Up @@ -2167,10 +2172,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.
-->

> Stability: 1.1 - Active Development
Expand All @@ -2181,6 +2201,7 @@ added: v22.5.0
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.
Expand Down
23 changes: 18 additions & 5 deletions lib/internal/process/finalization.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,35 +85,47 @@ 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) {
emitExperimentalWarning('process.finalization.register');
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) {
emitExperimentalWarning('process.finalization.registerBeforeExit');
validateObject(obj, 'obj', kValidateObjectAllowFunction);

_register('beforeExit', obj, fn);
return _register('beforeExit', obj, fn);
}

/**
Expand All @@ -125,16 +137,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 {
Expand Down
29 changes: 29 additions & 0 deletions test/fixtures/process/unregister-function.mjs
Original file line number Diff line number Diff line change
@@ -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')
})
1 change: 1 addition & 0 deletions test/parallel/test-process-finalization.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const files = [
'finalization-cleanup.mjs',
'gc-not-close.mjs',
'unregister.mjs',
'unregister-function.mjs',
'different-registry-per-thread.mjs',
];

Expand Down
Loading