@@ -228,17 +228,34 @@ function waitChildClose(child: ChildProcess): Promise<void> {
228228
229229const SHELL_GUARD_DISPOSE_REAP_MS = 2_000 ;
230230
231- async function reapLiveChildren ( liveChildren : Set < ChildProcess > ) : Promise < void > {
231+ function childStillLive ( child : ChildProcess ) : boolean {
232+ return child . exitCode === null && child . signalCode === null ;
233+ }
234+
235+ // Abort SIGKILLs the process group immediately (runGuardedShell onAbort). This
236+ // window is only a backstop for children still tracked at dispose. Leftovers
237+ // after it must fail teardown; do not stretch the process-exit 2s deadline.
238+ export async function reapLiveChildren ( liveChildren : Set < ChildProcess > ) : Promise < void > {
232239 const remaining = [ ...liveChildren ] ;
233240 for ( const child of remaining ) killProcessTree ( child ) ;
234241 if ( remaining . length === 0 ) return ;
235- await Promise . race ( [
236- Promise . all ( remaining . map ( waitChildClose ) ) ,
237- new Promise < void > ( ( resolve ) => {
238- const timer = setTimeout ( resolve , SHELL_GUARD_DISPOSE_REAP_MS ) ;
239- if ( typeof timer . unref === "function" ) timer . unref ( ) ;
240- } ) ,
241- ] ) ;
242+ const closed = Promise . all ( remaining . map ( waitChildClose ) ) ;
243+ let timer : ReturnType < typeof setTimeout > | undefined ;
244+ const timedOut = new Promise < "timeout" > ( ( resolve ) => {
245+ timer = setTimeout ( ( ) => resolve ( "timeout" ) , SHELL_GUARD_DISPOSE_REAP_MS ) ;
246+ } ) ;
247+ try {
248+ const winner = await Promise . race ( [ closed . then ( ( ) => "closed" as const ) , timedOut ] ) ;
249+ if ( winner === "closed" ) return ;
250+ const stillLive = remaining . filter ( childStillLive ) ;
251+ if ( stillLive . length > 0 ) {
252+ throw new Error (
253+ `${ stillLive . length } shell child process${ stillLive . length === 1 ? "" : "es" } still live after ${ SHELL_GUARD_DISPOSE_REAP_MS } ms reap` ,
254+ ) ;
255+ }
256+ } finally {
257+ if ( timer !== undefined ) clearTimeout ( timer ) ;
258+ }
242259}
243260
244261export async function runGuardedShell (
0 commit comments