UNPKG

batch-cluster

Version:
137 lines 7.75 kB
"use strict"; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _ProcessTerminator_instances, _ProcessTerminator_logger, _ProcessTerminator_waitForTaskCompletion, _ProcessTerminator_removeErrorListeners, _ProcessTerminator_sendExitCommand, _ProcessTerminator_destroyStreams, _ProcessTerminator_handleGracefulShutdown, _ProcessTerminator_forceKillIfRunning, _ProcessTerminator_awaitNotRunning; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProcessTerminator = void 0; const Async_1 = require("./Async"); const Pids_1 = require("./Pids"); const Stream_1 = require("./Stream"); const String_1 = require("./String"); const Timeout_1 = require("./Timeout"); /** * Utility class for managing process termination lifecycle */ class ProcessTerminator { constructor(opts) { _ProcessTerminator_instances.add(this); this.opts = opts; _ProcessTerminator_logger.set(this, void 0); __classPrivateFieldSet(this, _ProcessTerminator_logger, opts.logger, "f"); } /** * Terminates a child process gracefully or forcefully * * @param proc The child process to terminate * @param processName Name for logging purposes * @param pid Process ID * @param lastTask Current task being processed * @param startupTaskId ID of the startup task * @param gracefully Whether to wait for current task completion * @param reason Reason for termination * @param isExited Whether the process has already exited * @param isRunning Function to check if process is still running * @returns Promise that resolves when termination is complete */ async terminate(proc, processName, lastTask, startupTaskId, gracefully, isExited, isRunning) { var _a; // Wait for current task to complete if graceful termination requested await __classPrivateFieldGet(this, _ProcessTerminator_instances, "m", _ProcessTerminator_waitForTaskCompletion).call(this, lastTask, startupTaskId, gracefully); // Remove error listeners to prevent EPIPE errors during termination __classPrivateFieldGet(this, _ProcessTerminator_instances, "m", _ProcessTerminator_removeErrorListeners).call(this, proc); // Send exit command to process __classPrivateFieldGet(this, _ProcessTerminator_instances, "m", _ProcessTerminator_sendExitCommand).call(this, proc); // Destroy streams __classPrivateFieldGet(this, _ProcessTerminator_instances, "m", _ProcessTerminator_destroyStreams).call(this, proc); // Handle graceful shutdown with timeouts await __classPrivateFieldGet(this, _ProcessTerminator_instances, "m", _ProcessTerminator_handleGracefulShutdown).call(this, proc, gracefully, isExited, isRunning); // Force kill if still running __classPrivateFieldGet(this, _ProcessTerminator_instances, "m", _ProcessTerminator_forceKillIfRunning).call(this, proc, processName, isRunning); // Final cleanup try { (_a = proc.disconnect) === null || _a === void 0 ? void 0 : _a.call(proc); } catch { // Ignore disconnect errors } // Note: Caller should emit childEnd event with proper BatchProcess instance } } exports.ProcessTerminator = ProcessTerminator; _ProcessTerminator_logger = new WeakMap(), _ProcessTerminator_instances = new WeakSet(), _ProcessTerminator_waitForTaskCompletion = async function _ProcessTerminator_waitForTaskCompletion(lastTask, startupTaskId, gracefully) { // Don't wait for startup tasks or if no task is running if (lastTask == null || lastTask.taskId === startupTaskId) { return; } try { // Wait for the process to complete and streams to flush await (0, Timeout_1.thenOrTimeout)(lastTask.promise, gracefully ? 2000 : 250); } catch { // Ignore errors during task completion wait } // Reject task if still pending if (lastTask.pending) { lastTask.reject(new Error(`Process terminated before task completed (${JSON.stringify({ gracefully, lastTask, })})`)); } }, _ProcessTerminator_removeErrorListeners = function _ProcessTerminator_removeErrorListeners(proc) { // Remove error listeners to prevent EPIPE errors during termination // See https://github.com/nodejs/node/issues/26828 for (const stream of [proc, proc.stdin, proc.stdout, proc.stderr]) { stream === null || stream === void 0 ? void 0 : stream.removeAllListeners("error"); } }, _ProcessTerminator_sendExitCommand = function _ProcessTerminator_sendExitCommand(proc) { var _a; if (((_a = proc.stdin) === null || _a === void 0 ? void 0 : _a.writable) !== true) { return; } const exitCmd = this.opts.exitCommand == null ? null : (0, String_1.ensureSuffix)(this.opts.exitCommand, "\n"); try { proc.stdin.end(exitCmd); } catch { // Ignore errors when sending exit command } }, _ProcessTerminator_destroyStreams = function _ProcessTerminator_destroyStreams(proc) { // Destroy all streams to ensure cleanup (0, Stream_1.destroy)(proc.stdin); (0, Stream_1.destroy)(proc.stdout); (0, Stream_1.destroy)(proc.stderr); }, _ProcessTerminator_handleGracefulShutdown = async function _ProcessTerminator_handleGracefulShutdown(proc, gracefully, isExited, isRunning) { if (!this.opts.cleanupChildProcs || !gracefully || this.opts.endGracefulWaitTimeMillis <= 0 || isExited) { return; } // Wait for the exit command to take effect await __classPrivateFieldGet(this, _ProcessTerminator_instances, "m", _ProcessTerminator_awaitNotRunning).call(this, this.opts.endGracefulWaitTimeMillis / 2, isRunning); // If still running, send kill signal if (isRunning() && proc.pid != null) { proc.kill(); } // Wait for the signal handler to work await __classPrivateFieldGet(this, _ProcessTerminator_instances, "m", _ProcessTerminator_awaitNotRunning).call(this, this.opts.endGracefulWaitTimeMillis / 2, isRunning); }, _ProcessTerminator_forceKillIfRunning = function _ProcessTerminator_forceKillIfRunning(proc, processName, isRunning) { if (this.opts.cleanupChildProcs && proc.pid != null && isRunning()) { __classPrivateFieldGet(this, _ProcessTerminator_logger, "f").call(this).warn(`${processName}.terminate(): force-killing still-running child.`); (0, Pids_1.kill)(proc.pid, true); } }, _ProcessTerminator_awaitNotRunning = async function _ProcessTerminator_awaitNotRunning(timeout, isRunning) { await (0, Async_1.until)(() => !isRunning(), timeout); }; //# sourceMappingURL=ProcessTerminator.js.map