| 1 | import { Worker } from 'node:worker_threads' |
| 2 | import { Stats } from 'node:fs' |
| 3 | import { PendingPromise, pendingPromise } from './cross' |
| 4 | import { quitting } from './first' |
| 5 | |
| 6 | // all stat requests for the same worker are serialized, potentially introducing extra latency |
| 7 | |
| 8 | const pool = new Map<string, (path: string) => Promise<Stats>>() |
| 9 | |
| 10 | export function getStatWorker(key: string) { |
| 11 | if (quitting) |
| 12 | return () => Promise.reject('quitting') |
| 13 | const existing = pool.get(key) |
| 14 | if (existing) |
| 15 | return existing |
| 16 | const worker = new Worker(__dirname + '/statWorker.js') |
| 17 | worker.unref() |
| 18 | const requests = new Map<string, PendingPromise<Stats>>() |
| 19 | worker.on('message', (msg: any) => { // request finished, good or bad |
| 20 | const k = msg.path |
| 21 | requests.get(k)?.resolve(msg.error ? Promise.reject(Error(msg.error)) |
| 22 | : Object.setPrototypeOf(msg.result, Stats.prototype) ) |
| 23 | requests.delete(k) |
| 24 | }) |
| 25 | worker.on('error', (err) => { // worker failure |
| 26 | for (const p of requests.values()) |
| 27 | p.reject(err) |
| 28 | requests.clear() |
| 29 | worker.terminate().catch(() => {}) |
| 30 | pool.delete(key) |
| 31 | }) |
| 32 | pool.set(key, query) |
| 33 | return query |
| 34 | |
| 35 | function query(path: string) { |
| 36 | const was = requests.get(path) |
| 37 | if (was) |
| 38 | return was |
| 39 | const ret = pendingPromise<Stats>() |
| 40 | requests.set(path, ret) |
| 41 | worker.postMessage(path) |
| 42 | return ret |
| 43 | } |
| 44 | } |