
      // This should always happen, however GC is
      // indeterministic 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 = ArrayPrototypeIndexOf(refs[event], ref);
      ArrayPrototypeSplice(refs[event], index, index + 1);
      uninstall(event);
    }
  }

  function _register(event, obj, fn) {
    install(event);

    const ref = new SafeWeakRef(obj);
    ref.fn = fn;

    registry ||= new SafeFinalizationRegistry(clear);
    registry.register(obj, ref);

    ArrayPrototypePush(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] = ArrayPrototypeFilter(refs[event], (ref) => {
        const _obj = ref.deref();
        return _obj && _obj !== obj;
      });
      uninstall(event);
    }
  }

  return {
    register,
    registerBeforeExit,
    unregister,
  };
}

module.exports = {
  createFinalization,
};
