()
| 296 | } |
| 297 | |
| 298 | private drain(): void { |
| 299 | // Grow toward maxSize while queued work outstrips workers that are idle OR |
| 300 | // already on their way up — throttled so we never cold-start the whole pool |
| 301 | // at once. |
| 302 | while ( |
| 303 | this.queue.length > this.idle.length + this.pending.size && |
| 304 | this.workers.size < this.maxSize && |
| 305 | this.pending.size < MAX_CONCURRENT_SPAWN && |
| 306 | !this.destroyed && |
| 307 | this.healthy |
| 308 | ) { |
| 309 | this.spawnOne(); |
| 310 | } |
| 311 | // Dispatch queued jobs to idle workers. |
| 312 | while (this.idle.length && this.queue.length) { |
| 313 | let job: ParseJob | undefined; |
| 314 | while (this.queue.length && (job = this.queue.shift()) && job.settled) job = undefined; |
| 315 | if (!job || job.settled) break; |
| 316 | const w = this.idle.pop()!; |
| 317 | this.dispatch(w, job); |
| 318 | } |
| 319 | // Hang-prevention: if there's queued work but nothing can ever run it (no |
| 320 | // idle workers, none spawning, none alive), fail it instead of hanging |
| 321 | // forever. Reached only when the crash budget is exhausted or after destroy. |
| 322 | if (this.queue.length && this.idle.length === 0 && this.pending.size === 0 && this.workers.size === 0) { |
| 323 | const reason = this.destroyed ? 'parse pool destroyed' : 'parse pool exhausted its worker crash budget'; |
| 324 | for (const job of this.queue.splice(0)) this.settle(job, undefined, new Error(reason)); |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | private settle(job: ParseJob, result?: ExtractionResult, err?: Error): void { |
| 329 | if (job.settled) return; |
no test coverage detected