Graceful shutdown: close all sessions, the engine, and clean up the lock.
(reason: string = 'stop')
| 316 | |
| 317 | /** Graceful shutdown: close all sessions, the engine, and clean up the lock. */ |
| 318 | async stop(reason: string = 'stop'): Promise<void> { |
| 319 | if (this.stopping) return; |
| 320 | this.stopping = true; |
| 321 | if (this.idleTimer) { |
| 322 | clearTimeout(this.idleTimer); |
| 323 | this.idleTimer = null; |
| 324 | } |
| 325 | if (this.maxIdleTimer) { |
| 326 | clearInterval(this.maxIdleTimer); |
| 327 | this.maxIdleTimer = null; |
| 328 | } |
| 329 | if (this.clientSweepTimer) { |
| 330 | clearInterval(this.clientSweepTimer); |
| 331 | this.clientSweepTimer = null; |
| 332 | } |
| 333 | process.stderr.write(`[CodeGraph daemon] Shutting down (${reason}; clients=${this.clients.size}).\n`); |
| 334 | for (const session of [...this.clients]) { |
| 335 | try { session.stop(); } catch { /* best-effort */ } |
| 336 | } |
| 337 | this.clients.clear(); |
| 338 | if (this.server) { |
| 339 | await new Promise<void>((resolve) => this.server!.close(() => resolve())); |
| 340 | this.server = null; |
| 341 | } |
| 342 | this.engine.stop(); |
| 343 | this.cleanupLockfile(); |
| 344 | deregisterDaemon(this.projectRoot); |
| 345 | if (process.platform !== 'win32') { |
| 346 | try { fs.unlinkSync(this.socketPath); } catch { /* may already be gone */ } |
| 347 | } |
| 348 | // POSIX exits here; Windows drains first (engine.stop() above began closing |
| 349 | // the file watcher, and exiting mid-teardown aborts the process). See |
| 350 | // finalizeDaemonExit / DAEMON_SHUTDOWN_BACKSTOP_MS. |
| 351 | finalizeDaemonExit(process.platform, (code) => process.exit(code)); |
| 352 | } |
| 353 | |
| 354 | private handleConnection(socket: net.Socket): void { |
| 355 | // Hello first so the proxy can verify versions before piping any |
no test coverage detected