No result after the full hard-kill window — the worker really is hung.
(w: ParsePoolWorker, job: ParseJob, totalMs: number)
| 396 | |
| 397 | /** No result after the full hard-kill window — the worker really is hung. */ |
| 398 | private onHardTimeout(w: ParsePoolWorker, job: ParseJob, totalMs: number): void { |
| 399 | if (job.settled || !this.workers.has(w)) return; |
| 400 | this.log(`TIMEOUT: ${job.task.filePath} got no result after ${totalMs}ms — killing worker`); |
| 401 | // Kill the (WASM-wedged) worker and reject this parse. A timeout isn't a |
| 402 | // crash — don't charge the budget — but the worker is gone, so spawn a |
| 403 | // replacement to keep capacity. The rejection message contains "timed out" |
| 404 | // so the orchestrator's retry pass re-attempts the file. |
| 405 | this.removeWorker(w); |
| 406 | this.inflight.delete(w); |
| 407 | try { void w.terminate(); } catch { /* already gone */ } |
| 408 | this.settle(job, undefined, new Error(`Parse timed out after ${totalMs}ms`)); |
| 409 | if (this.healthy) this.spawnOne(); |
| 410 | this.drain(); |
| 411 | } |
| 412 | |
| 413 | private drain(): void { |
| 414 | // Grow toward maxSize while queued work outstrips workers that are idle OR |