@@ -6,6 +6,8 @@ import type { ManagedRunStdin, SpawnProcessAdapter } from "../types.js";
66import { toStringEnv } from "./env.js" ;
77
88const FORCE_KILL_WAIT_FALLBACK_MS = 4000 ;
9+ const WINDOWS_CLOSE_STATE_SETTLE_TIMEOUT_MS = 250 ;
10+ const WINDOWS_CLOSE_STATE_POLL_MS = 10 ;
911
1012function resolveCommand ( command : string ) : string {
1113 return resolveWindowsCommandShim ( {
@@ -122,6 +124,8 @@ export async function createChildAdapter(params: {
122124 let rejectWait : ( ( reason ?: unknown ) => void ) | null = null ;
123125 let waitPromise : Promise < { code : number | null ; signal : NodeJS . Signals | null } > | null = null ;
124126 let forceKillWaitFallbackTimer : NodeJS . Timeout | null = null ;
127+ let childExitState : { code : number | null ; signal : NodeJS . Signals | null } | null = null ;
128+ let windowsExitSettleTimer : NodeJS . Timeout | null = null ;
125129
126130 const clearForceKillWaitFallback = ( ) => {
127131 if ( ! forceKillWaitFallbackTimer ) {
@@ -131,11 +135,20 @@ export async function createChildAdapter(params: {
131135 forceKillWaitFallbackTimer = null ;
132136 } ;
133137
138+ const clearWindowsExitSettleTimer = ( ) => {
139+ if ( ! windowsExitSettleTimer ) {
140+ return ;
141+ }
142+ clearTimeout ( windowsExitSettleTimer ) ;
143+ windowsExitSettleTimer = null ;
144+ } ;
145+
134146 const settleWait = ( value : { code : number | null ; signal : NodeJS . Signals | null } ) => {
135147 if ( waitResult || waitError !== undefined ) {
136148 return ;
137149 }
138150 clearForceKillWaitFallback ( ) ;
151+ clearWindowsExitSettleTimer ( ) ;
139152 waitResult = value ;
140153 if ( resolveWait ) {
141154 const resolve = resolveWait ;
@@ -150,6 +163,7 @@ export async function createChildAdapter(params: {
150163 return ;
151164 }
152165 clearForceKillWaitFallback ( ) ;
166+ clearWindowsExitSettleTimer ( ) ;
153167 waitError = error ;
154168 if ( rejectWait ) {
155169 const reject = rejectWait ;
@@ -168,11 +182,60 @@ export async function createChildAdapter(params: {
168182 forceKillWaitFallbackTimer . unref ?.( ) ;
169183 } ;
170184
185+ const resolveObservedExitState = ( fallback : {
186+ code : number | null ;
187+ signal : NodeJS . Signals | null ;
188+ } ) => ( {
189+ code : childExitState ?. code ?? child . exitCode ?? fallback . code ,
190+ signal : childExitState ?. signal ?? child . signalCode ?? fallback . signal ,
191+ } ) ;
192+
193+ const hasObservedExitState = ( ) =>
194+ childExitState != null || child . exitCode != null || child . signalCode != null ;
195+
196+ const scheduleWindowsExitStateSettle = ( fallback : {
197+ code : number | null ;
198+ signal : NodeJS . Signals | null ;
199+ } ) => {
200+ if ( process . platform !== "win32" ) {
201+ return ;
202+ }
203+ clearWindowsExitSettleTimer ( ) ;
204+ windowsExitSettleTimer = setTimeout ( ( ) => {
205+ settleWait ( resolveObservedExitState ( fallback ) ) ;
206+ } , WINDOWS_CLOSE_STATE_SETTLE_TIMEOUT_MS ) ;
207+ } ;
208+
171209 child . once ( "error" , ( error ) => {
172210 rejectPendingWait ( error ) ;
173211 } ) ;
212+ child . once ( "exit" , ( code , signal ) => {
213+ childExitState = { code, signal } ;
214+ scheduleWindowsExitStateSettle ( { code, signal } ) ;
215+ } ) ;
174216 child . once ( "close" , ( code , signal ) => {
175- settleWait ( { code, signal } ) ;
217+ if ( process . platform !== "win32" || hasObservedExitState ( ) || code != null || signal != null ) {
218+ settleWait ( resolveObservedExitState ( { code, signal } ) ) ;
219+ return ;
220+ }
221+
222+ const startedAt = Date . now ( ) ;
223+ const waitForExitState = ( ) => {
224+ if ( waitResult || waitError !== undefined ) {
225+ return ;
226+ }
227+ if ( hasObservedExitState ( ) ) {
228+ settleWait ( resolveObservedExitState ( { code, signal } ) ) ;
229+ return ;
230+ }
231+ if ( Date . now ( ) - startedAt >= WINDOWS_CLOSE_STATE_SETTLE_TIMEOUT_MS ) {
232+ settleWait ( { code, signal } ) ;
233+ return ;
234+ }
235+ setTimeout ( waitForExitState , WINDOWS_CLOSE_STATE_POLL_MS ) ;
236+ } ;
237+
238+ waitForExitState ( ) ;
176239 } ) ;
177240
178241 const wait = async ( ) => {
@@ -229,6 +292,7 @@ export async function createChildAdapter(params: {
229292
230293 const dispose = ( ) => {
231294 clearForceKillWaitFallback ( ) ;
295+ clearWindowsExitSettleTimer ( ) ;
232296 child . removeAllListeners ( ) ;
233297 } ;
234298
0 commit comments