Files
hyperframes/packages/engine/src/utils/processTracker.ts
T
Miguel Ángel 84edce908a fix: address code review feedback on process cleanup
- Blocker: arm 3s force-exit timer BEFORE awaiting cleanup, not
  inside .finally(). Prevents hang if drainBrowserPool() blocks on
  dead Chrome.
- Reorder cleanup: killTrackedProcesses() (sync, fast) runs first,
  then async browser drain. Ffmpeg dies immediately instead of
  surviving if the hard timer fires early.
- SIGKILL escalation: processTracker now SIGTERMs all tracked
  processes, then SIGKILLs survivors after 500ms grace period.
- Scope pgrep to current user (pgrep -u $(id -u)) so orphan
  detection doesn't touch other users' Chrome on shared machines.
- Add process.on('exit') handler for crash paths (unhandled
  exceptions/rejections that bypass signal handlers).
- Document Windows no-op behavior on killProcessTree handlers.
2026-05-22 23:53:46 -04:00

42 lines
925 B
TypeScript

import type { ChildProcess } from "node:child_process";
const tracked = new Set<ChildProcess>();
export function trackChildProcess(proc: ChildProcess): void {
tracked.add(proc);
const remove = () => tracked.delete(proc);
proc.once("exit", remove);
proc.once("error", remove);
}
/**
* SIGTERM all tracked child processes, then SIGKILL any that survive
* after a short grace period.
*/
export function killTrackedProcesses(): void {
const alive: ChildProcess[] = [];
for (const proc of tracked) {
if (!proc.killed) {
try {
proc.kill("SIGTERM");
alive.push(proc);
} catch {
// Already exited between the check and the kill.
}
}
}
tracked.clear();
if (alive.length === 0) return;
setTimeout(() => {
for (const proc of alive) {
try {
proc.kill("SIGKILL");
} catch {
// Already exited.
}
}
}, 500).unref();
}