No result after the full hard-kill window — the worker really is hung.
(w: ParsePoolWorker, job: ParseJob, totalMs: number)
| 377 | |
| 378 | /** No result after the full hard-kill window — the worker really is hung. */ |
| 379 | private onHardTimeout(w: ParsePoolWorker, job: ParseJob, totalMs: number): void { |
| 380 | if (job.settled || !this.workers.has(w)) return; |
| 381 | this.log(`TIMEOUT: ${job.task.filePath} got no result after ${totalMs}ms — killing worker`); |
| 382 | // Kill the (WASM-wedged) worker and reject this parse. A timeout isn't a |
| 383 | // crash — don't charge the budget — but the worker is gone, so spawn a |
| 384 | // replacement to keep capacity. The rejection message contains "timed out" |
| 385 | // so the orchestrator's retry pass re-attempts the file. |
| 386 | this.removeWorker(w); |
| 387 | this.inflight.delete(w); |
| 388 | try { void w.terminate(); } catch { /* already gone */ } |
| 389 | this.settle(job, undefined, new Error(`Parse timed out after ${totalMs}ms`)); |
| 390 | if (this.healthy) this.spawnOne(); |
| 391 | this.drain(); |
| 392 | } |
| 393 | |
| 394 | private drain(): void { |
| 395 | // Grow toward maxSize while queued work outstrips workers that are idle OR |