UNPKG

federer

Version:

Experiments in asynchronous federated learning and decentralized learning

87 lines 3.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProcessTracker = void 0; const assert = require("assert"); /** * Keeps track of spawned child processes. */ class ProcessTracker { constructor(logger) { this.logger = logger; /** The child processes that have been spawned by the process tracker. */ this.processes = new Set(); /** * Whether the current process tracker is "spent", meaning that it cannot be * used anymore. */ this.spent = false; this.sigintListener = () => this.cleanExit("Received SIGINT"); this.sigtermListener = () => this.cleanExit("Received SIGTERM"); this.uncaughtExceptionListener = (err) => { this.killChildProcesses(); this.logger.error("Coordinator crashed"); this.logger.error(err.message); this.logger.error(err.stack); // Usually, we disallow calls to `console` methods, preferring to use a // logger. But when the process is about to crash, we want the developer to // know about it; as a last ditch effort to get the programmer's attention // about this crash, we also log to the console. This is the only exception // to the rule. // eslint-disable-next-line no-console console.error(err); process.exit(1); }; this.addListeners(); } addListeners() { // Catch CTRL+C signal process.on("SIGINT", this.sigintListener); // Catch kill signal process.on("SIGTERM", this.sigtermListener); // Kill children when coordinator crashes process.on("uncaughtException", this.uncaughtExceptionListener); } /** Send SIGTERM to all child processes. */ killChildProcesses() { this.checkNotSpent(); this.processes.forEach((child) => child.kill()); } /** Promise resolving once all children have exited. */ async allChildrenExited() { await Promise.all([...this.processes.values()].map((child) => child.exited())); } /** * Removes the registered event listeners. After this method has been called, * the ProcessTracker is "spent": it should not be used anymore. */ removeListeners() { this.checkNotSpent(); process.removeListener("SIGINT", this.sigintListener); process.removeListener("SIGTERM", this.sigtermListener); process.removeListener("uncaughtException", this.uncaughtExceptionListener); this.spent = true; } /** * Register a child with the ProcessTracker. * * @param child Process representing the child process to track * @param name Name of the process, used for logging */ register(child, name) { this.processes.add(child); child.events.on("exited", (reason) => { this.logger.error(`Child ${name} exited for reason '${reason}'`); this.processes.delete(child); }); } cleanExit(reason) { this.logger.info(`[Coordinator] Killing child processes and exiting because: '${reason}'`); this.killChildProcesses(); process.exit(0); } checkNotSpent() { assert(!this.spent, "After ProcessTracker.removeListeners() has been called, the ProcessTracker cannot be used anymore"); } } exports.ProcessTracker = ProcessTracker; //# sourceMappingURL=ProcessTracker.js.map