* Close everything. Used on graceful daemon shutdown (SIGTERM/idle timeout) * and on direct-mode stop. Idempotent.
()
| 263 | * and on direct-mode stop. Idempotent. |
| 264 | */ |
| 265 | stop(): Promise<void> { |
| 266 | if (this.stopPromise) return this.stopPromise; |
| 267 | this.closed = true; |
| 268 | if (!this.cg && !this.initPromise && this.explicitProjects.size === 0 && this.writerLockRoot) { |
| 269 | releaseWriterLock(this.writerLockRoot); |
| 270 | this.writerLockRoot = null; |
| 271 | } |
| 272 | |
| 273 | // Detach + terminate the worker pool first so no tool call routes to a |
| 274 | // worker mid-teardown; outstanding pool calls resolve with graceful guidance. |
| 275 | // Stopping waits for the workers to end — the pool's, and any |
| 276 | // `codegraph_status` change count still measuring: the daemon exits right |
| 277 | // after, and exiting while a worker is still starting up can crash the |
| 278 | // process. |
| 279 | this.toolHandler.setQueryPool(null); |
| 280 | const poolDown = this.queryPool ? this.queryPool.destroy() : Promise.resolve(); |
| 281 | this.queryPool = null; |
| 282 | const measurementsDown = endFreshnessMeasurements(); |
| 283 | const drained = this.toolHandler.closeAll(); |
| 284 | this.stopPromise = Promise.all([drained, poolDown, measurementsDown]).then(async () => { |
| 285 | if (this.initPromise) await this.initPromise; |
| 286 | if (this.defaultLease) { |
| 287 | await this.defaultLease.release(); |
| 288 | this.defaultLease = null; |
| 289 | this.writerLockRoot = null; |
| 290 | } else if (this.cg) { |
| 291 | this.cg.unwatch(); |
| 292 | while (this.cg.isIndexing()) await new Promise((resolve) => setTimeout(resolve, 25)); |
| 293 | this.cg.close(); |
| 294 | } |
| 295 | this.cg = null; |
| 296 | if (this.writerLockRoot) releaseWriterLock(this.writerLockRoot); |
| 297 | this.writerLockRoot = null; |
| 298 | }); |
| 299 | return this.stopPromise; |
| 300 | } |
| 301 | |
| 302 | private releaseExplicitProject(cg: CodeGraph): void | Promise<void> { |
| 303 | const lease = this.explicitProjects.get(cg); |
nothing calls this directly
no test coverage detected