Repository: nodejs/node
File: lib/internal/modules/esm/worker.js (JavaScript)

Code before:
 * @param {MessagePort} syncCommPort - The message port used for synchronous communication between the worker and the
 * main thread.
 * @param {(err: Error, origin?: string) => void} errorHandler - The function to use for uncaught exceptions.
 * @returns {Promise<void>} A promise that resolves when the worker thread has been initialized.
 */
async function customizedModuleWorker(lock, syncCommPort, errorHandler) {
  let hooks;
  let initializationError;
  let hasInitializationError = false;

  {
    // If a custom hook is calling `process.exit`, we should wake up the main thread
    // so it can detect the exit event.
    const { exit } = process;
    process.exit = function(code) {
      const exitMsg = wrapMessage('exit', code ?? process.exitCode);
      if (hooks) {
        for (let i = 0; i < allThreadRegisteredHandlerPorts.length; i++) {
          const { port: registeredPort } = allThreadRegisteredHandlerPorts[i];
          registeredPort.postMessage(exitMsg);
        }

        for (const { port, lock: operationLock } of unsettledResponsePorts) {
          port.postMessage(exitMsg);
          // Wake all threads that have pending operations. Is that needed???
          AtomicsAdd(operationLock, WORKER_TO_MAIN_THREAD_NOTIFICATION, 1);
          AtomicsNotify(operationLock, WORKER_TO_MAIN_THREAD_NOTIFICATION);
        }
      }
      syncCommPort.postMessage(exitMsg);
      AtomicsAdd(lock, WORKER_TO_MAIN_THREAD_NOTIFICATION, 1);
      AtomicsNotify(lock, WORKER_TO_MAIN_THREAD_NOTIFICATION);
      return ReflectApply(exit, this, arguments);
    };
  }


  try {
    hooks = await initializeHooks();
  } catch (exception) {
    // If there was an error while parsing and executing a user loader, for example if because a
    // loader contained a syntax error, then we need to send the error to the main thread so it can
    // be thrown and printed.
    hasInitializationError = true;
    initializationError = exception;
  }

  syncCommPort.on('message', handleMessage);

  if (hasInitializationError) {
    syncCommPort.postMessage(wrapMessage('error', initializationError));

Review comment (suggestion):
yes, this is needed to propagate the exit lifecycle properly since it affects all children; at least last I understood node wanted to kill application threads if the loader died. Previous designs around having the ability to "terminate" a loader early never got implemented to my knowledge.

Code after:
 * @param {MessagePort} syncCommPort - The message port used for synchronous communication between the worker and the
 * main thread.
 * @param {(err: Error, origin?: string) => void} errorHandler - The function to use for uncaught exceptions.
 * @returns {Promise<void>} A promise that resolves when the worker thread has been initialized.
 */
async function customizedModuleWorker(lock, syncCommPort, errorHandler) {
  let hooks;
  let initializationError;
  let hasInitializationError = false;

  {
    // If a custom hook is calling `process.exit`, we should wake up the main thread
    // so it can detect the exit event.
    const { exit } = process;
    process.exit = function(code) {
      const exitMsg = wrapMessage('exit', code ?? process.exitCode);
      if (hooks) {
        for (let i = 0; i < allThreadRegisteredHandlerPorts.length; i++) {
          const { port: registeredPort } = allThreadRegisteredHandlerPorts[i];
          registeredPort.postMessage(exitMsg);
        }

        for (const { port, lock: operationLock } of unsettledResponsePorts) {
          port.postMessage(exitMsg);
          // Wake all threads that have pending operations.
          AtomicsAdd(operationLock, WORKER_TO_MAIN_THREAD_NOTIFICATION, 1);
          AtomicsNotify(operationLock, WORKER_TO_MAIN_THREAD_NOTIFICATION);
        }
      }
      syncCommPort.postMessage(exitMsg);
      AtomicsAdd(lock, WORKER_TO_MAIN_THREAD_NOTIFICATION, 1);
      AtomicsNotify(lock, WORKER_TO_MAIN_THREAD_NOTIFICATION);
      return ReflectApply(exit, this, arguments);
    };
  }


  try {
    hooks = await initializeHooks();
  } catch (exception) {
    // If there was an error while parsing and executing a user loader, for example if because a
    // loader contained a syntax error, then we need to send the error to the main thread so it can
    // be thrown and printed.
    hasInitializationError = true;
    initializationError = exception;
  }

  syncCommPort.on('message', handleMessage);

  if (hasInitializationError) {
    syncCommPort.postMessage(wrapMessage('error', initializationError));