UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

145 lines (118 loc) 3.32 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/process/finalization.js import { validateObject, kValidateObjectAllowFunction, } from "nstdlib/lib/internal/validators"; import { emitExperimentalWarning } from "nstdlib/lib/internal/util"; // This file is a modified version of the on-exit-leak-free module on npm. function createFinalization() { /** * @type {SafeFinalizationRegistry} */ let registry = null; const refs = { __proto__: null, exit: [], beforeExit: [], }; const functions = { __proto__: null, exit: onExit, beforeExit: onBeforeExit, }; function install(event) { if (refs[event].length > 0) { return; } process.on(event, functions[event]); } function uninstall(event) { if (refs[event].length > 0) { return; } process.removeListener(event, functions[event]); if (refs.exit.length === 0 && refs.beforeExit.length === 0) { registry = null; } } function onExit() { callRefsToFree("exit"); } function onBeforeExit() { callRefsToFree("beforeExit"); } function callRefsToFree(event) { for (const ref of refs[event]) { const obj = ref.deref(); const fn = ref.fn; // This should always happen, however GC is // undeterministic so it might not happen. /* istanbul ignore else */ if (obj !== undefined) { fn(obj, event); } } refs[event] = []; } function clear(ref) { for (const event of ["exit", "beforeExit"]) { const index = Array.prototype.indexOf.call(refs[event], ref); Array.prototype.splice.call(refs[event], index, index + 1); uninstall(event); } } function _register(event, obj, fn) { install(event); const ref = new WeakRef(obj); ref.fn = fn; registry ||= new FinalizationRegistry(clear); registry.register(obj, ref); Array.prototype.push.call(refs[event], ref); } /** * Execute the given function when the process exits, * and clean things up when the object is gc. * @param {any} obj * @param {Function} fn */ function register(obj, fn) { emitExperimentalWarning("process.finalization.register"); validateObject(obj, "obj", kValidateObjectAllowFunction); _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 */ function registerBeforeExit(obj, fn) { emitExperimentalWarning("process.finalization.registerBeforeExit"); validateObject(obj, "obj", kValidateObjectAllowFunction); _register("beforeExit", obj, fn); } /** * Unregister the given object from the onExit or onBeforeExit event. * @param {object} obj */ function unregister(obj) { emitExperimentalWarning("process.finalization.unregister"); if (!registry) { return; } registry.unregister(obj); for (const event of ["exit", "beforeExit"]) { refs[event] = Array.prototype.filter.call(refs[event], (ref) => { const _obj = ref.deref(); return _obj && _obj !== obj; }); uninstall(event); } } return { register, registerBeforeExit, unregister, }; } export { createFinalization };