import { spawn } from "node:child_process" ;
const DEFAULT_GRACE_MS = 3000 ;
const MAX_GRACE_MS = 60 _000 ;
/**
* Best - effort process - tree termination with graceful shutdown .
* - Windows : use taskkill / T to include descendants . Sends SIGTERM - equivalent
* first ( without / F ) , then force - kills if process survives .
* - Unix : send SIGTERM to process group first , wait grace period , then SIGKILL .
*
* This gives child processes a chance to clean up ( close connections , remove
* temp files , terminate their own children ) before being hard - killed .
*/
export function killProcessTree(pid: number, opts?: { graceMs?: number }): void {
if (!Number.isFinite(pid) || pid <= 0 ) {
return ;
}
const graceMs = normalizeGraceMs(opts?.graceMs);
if (process.platform === "win32" ) {
killProcessTreeWindows(pid, graceMs);
return ;
}
killProcessTreeUnix(pid, graceMs);
}
function normalizeGraceMs(value?: number): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
return DEFAULT_GRACE_MS;
}
return Math.max(0 , Math.min(MAX_GRACE_MS, Math.floor(value)));
}
function isProcessAlive(pid: number): boolean {
try {
process.kill(pid, 0 );
return true ;
} catch {
return false ;
}
}
function killProcessTreeUnix(pid: number, graceMs: number): void {
// Step 1: Try graceful SIGTERM to process group
try {
process.kill(-pid, "SIGTERM" );
} catch {
// Process group doesn't exist or we lack permission - try direct
try {
process.kill(pid, "SIGTERM" );
} catch {
// Already gone
return ;
}
}
// Step 2: Wait grace period, then SIGKILL if still alive
setTimeout(() => {
if (isProcessAlive(-pid)) {
try {
process.kill(-pid, "SIGKILL" );
return ;
} catch {
// Fall through to direct pid kill
}
}
if (!isProcessAlive(pid)) {
return ;
}
try {
process.kill(pid, "SIGKILL" );
} catch {
// Process exited between liveness check and kill
}
}, graceMs).unref(); // Don't block event loop exit
}
function runTaskkill(args: string[]): void {
try {
spawn("taskkill" , args, {
stdio: "ignore" ,
detached: true ,
windowsHide: true ,
});
} catch {
// Ignore taskkill spawn failures
}
}
function killProcessTreeWindows(pid: number, graceMs: number): void {
// Step 1: Try graceful termination (taskkill without /F)
runTaskkill(["/T" , "/PID" , String(pid)]);
// Step 2: Wait grace period, then force kill only if pid still exists.
// This avoids unconditional delayed /F kills after graceful shutdown.
setTimeout(() => {
if (!isProcessAlive(pid)) {
return ;
}
runTaskkill(["/F" , "/T" , "/PID" , String(pid)]);
}, graceMs).unref(); // Don't block event loop exit
}
Messung V0.5 in Prozent C=88 H=100 G=94
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland