import { fork, ChildProcess } from 'child_process'; import path from 'path'; import { WindowManager } from "./window_manager"; export class WorkerManager { private readonly pathToWorkerDir: string; private windowManager: WindowManager; private workers: ChildProcess[]; private cleanupInProgress: boolean = false; constructor(pathToWorkerDir: string, windowManager: WindowManager) { this.pathToWorkerDir = pathToWorkerDir; this.windowManager = windowManager; this.workers = []; } async startNetworkScannerWorker(udpPort: number, tcpPort: number, okPage: string, errorPage: string, databaseResetPage: string, userConfigPath: string, applicationInfoPath: string): Promise { return this.startForkedWorker('network_scanner_worker.js', { UDP_PORT: udpPort.toString(), TCP_PORT: tcpPort.toString(), OK_PAGE: okPage, ERROR_PAGE: errorPage, DATABASE_RESET_PAGE: databaseResetPage, USER_CONFIG_PATH: userConfigPath, APPLICATION_INFO_PATH: applicationInfoPath }); } async startDirectoriesWatchersWorker(memoryManagerPath: string, applicationInfoPath: string): Promise { return this.startForkedWorker('directories_watcher_worker.js', { MEMORY_MANAGER_PATH: memoryManagerPath, APPLICATION_INFO_PATH: applicationInfoPath }); } async startServersWorker(host: string, udpPort: number, tcpPort: number): Promise { return this.startForkedWorker('servers_worker.js', { HOST: host, USER_UDP_PORT: udpPort.toString(), USER_TCP_PORT: tcpPort.toString() }); } async startResourceCoordinatorWorker(usersConfigPath: string, applicationInfoPath: string, memoryManagerPath: string, queueManagerPath: string, tcpPort: number): Promise { return this.startForkedWorker('resource_coordinator_worker.js', { USERS_CONFIG_PATH: usersConfigPath, APPLICATION_INFO_PATH: applicationInfoPath, MEMORY_MANAGER_PATH: memoryManagerPath, QUEUE_MANAGER_PATH: queueManagerPath, TCP_PORT: tcpPort.toString() }); } async startBackupRetrievalWorker(userConfigPath: string, applicationInfoPath: string, clientPort: number, destinationPath: string): Promise { return this.startForkedWorker('backup_retrieval_worker.js', { USER_CONFIG_PATH: userConfigPath, APPLICATION_INFO_PATH: applicationInfoPath, CLIENT_PORT: clientPort.toString(), DESTINATION_PATH: destinationPath }); } async startAnnouncementWorker(applicationInfoPath: string, clientPort: number, message: string): Promise { return this.startForkedWorker('send_announcement_worker.js', { APPLICATION_INFO_PATH: applicationInfoPath, CLIENT_PORT: clientPort.toString(), MESSAGE: message }); } private async startForkedWorker(scriptName: string, envData: { [key: string]: string }): Promise { return new Promise((resolve, reject) => { const worker = fork(path.join(this.pathToWorkerDir, scriptName), { execArgv: ['--max-old-space-size=4096'], env: { ...process.env, ...envData } }); this.workers.push(worker); worker.on('message', (data: unknown) => { const message = data as { type: string, page?: string, message?: string }; if (message.type === 'changeContent' && message.page) { this.windowManager.changeContent(message.page); } else if (message.type === 'showAlert' && message.message) { this.windowManager.showAlert(message.message); } else { console.log(`${scriptName} message:`, message); } }); worker.on('error', (err) => { console.error(`${scriptName} error:`, err); worker.kill(); this.removeWorker(worker); reject(err); }); worker.on('exit', (code, signal) => { this.removeWorker(worker); if (code === 0) { console.log(`${scriptName} exited successfully`); resolve(); } else if (signal) { console.log(`${scriptName} was killed with signal: ${signal}`); } else { console.error(`${scriptName} exited with code: ${code}`); } }); }); } closeAllWorkers(): void { if (this.cleanupInProgress) return; this.cleanupInProgress = true; console.log('Terminating all running workers...'); this.workers.forEach(worker => worker.kill()); this.workers = []; } private removeWorker(worker: ChildProcess): void { const index = this.workers.indexOf(worker); if (index > -1) { this.workers.splice(index, 1); } } }