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
33 changes: 16 additions & 17 deletions lib/internal/process/finalization.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
// This file is a modified version of the on-exit-leak-free module on npm.

const {
ArrayPrototypeFilter,
ArrayPrototypeIndexOf,
ArrayPrototypePush,
ArrayPrototypeSplice,
SafeFinalizationRegistry,
SafeSet,
SafeWeakRef,
} = primordials;
const { validateObject, kValidateObjectAllowFunction } = require('internal/validators');
Expand All @@ -20,8 +17,8 @@ function createFinalization() {

const refs = {
__proto__: null,
exit: [],
beforeExit: [],
exit: new SafeSet(),
beforeExit: new SafeSet(),
};

const functions = {
Expand All @@ -31,21 +28,21 @@ function createFinalization() {
};

function install(event) {
if (refs[event].length > 0) {
if (refs[event].size > 0) {
return;
}

process.on(event, functions[event]);
}

function uninstall(event) {
if (refs[event].length > 0) {
if (refs[event].size > 0) {
return;
}

process.removeListener(event, functions[event]);

if (refs.exit.length === 0 && refs.beforeExit.length === 0) {
if (refs.exit.size === 0 && refs.beforeExit.size === 0) {
registry = null;
}
}
Expand All @@ -70,14 +67,14 @@ function createFinalization() {
fn(obj, event);
}
}
refs[event] = [];
refs[event].clear();
}

function clear(ref) {
for (const event of ['exit', 'beforeExit']) {
const index = ArrayPrototypeIndexOf(refs[event], ref);
ArrayPrototypeSplice(refs[event], index, index + 1);
uninstall(event);
if (refs[event].delete(ref)) {
uninstall(event);
}
}
}

Expand All @@ -90,7 +87,7 @@ function createFinalization() {
registry ||= new SafeFinalizationRegistry(clear);
registry.register(obj, ref);

ArrayPrototypePush(refs[event], ref);
refs[event].add(ref);
}

/**
Expand Down Expand Up @@ -130,10 +127,12 @@ function createFinalization() {
}
registry.unregister(obj);
for (const event of ['exit', 'beforeExit']) {
refs[event] = ArrayPrototypeFilter(refs[event], (ref) => {
for (const ref of refs[event]) {
const _obj = ref.deref();
return _obj && _obj !== obj;
});
if (!_obj || _obj === obj) {
refs[event].delete(ref);
}
}
uninstall(event);
}
}
Expand Down
34 changes: 34 additions & 0 deletions test/fixtures/process/finalization-cleanup.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { deepStrictEqual } from 'assert'
import { setImmediate } from 'timers/promises'

const keptAlive = []
const finalized = []

function onFinalize(obj) {
finalized.push(obj.name)
}

{
const first = { name: 'first' }
let collected = { name: 'collected' }
const third = { name: 'third' }

keptAlive.push(first, third)

process.finalization.register(first, onFinalize)
process.finalization.register(collected, onFinalize)
process.finalization.register(third, onFinalize)

collected = null
}

// Give V8 a few chances to collect `collected` and run the
// FinalizationRegistry cleanup before process exit.
for (let i = 0; i < 10; i++) {
gc()
await setImmediate()
}

process.on('exit', function () {
deepStrictEqual(finalized, ['first', 'third'])
})
1 change: 1 addition & 0 deletions test/parallel/test-process-finalization.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import assert from 'assert';
const files = [
'close.mjs',
'before-exit.mjs',
'finalization-cleanup.mjs',
'gc-not-close.mjs',
'unregister.mjs',
'different-registry-per-thread.mjs',
Expand Down
Loading