@@ -273,46 +273,56 @@ impl ManagedProcess {
273273 /// Returns `true` when profile unload was deferred and a background job drain is needed.
274274 #[ cfg( windows) ]
275275 fn release_windows_spawn_resources_after_exit ( & mut self , exited_pid : Option < u32 > ) -> bool {
276- if let Some ( ref job) = self . job_object {
277- match job. active_process_count ( ) {
278- Ok ( 0 ) => {
279- self . clear_windows_spawn_resources ( ) ;
280- return false ;
281- }
282- Ok ( _) => {
283- if let ( Some ( pid) , Some ( profile) ) = ( exited_pid, self . user_profile . take ( ) ) {
284- if let Err ( e) = job. terminate ( ) {
285- warn ! (
286- "[{}] failed to terminate residual job members before deferred drain (pid {}): {e:#}" ,
287- self . name, pid
288- ) ;
289- }
290- let job = self . job_object . take ( ) ;
291- info ! (
292- "[{}] deferring profile unload until job members exit (pid {})" ,
293- self . name, pid
294- ) ;
295- self . deferred_exit_cleanups . push ( DeferredExitCleanup {
296- pid,
297- user_profile : profile,
298- job_object : job,
299- } ) ;
300- return true ;
301- }
302- warn ! (
303- "[{}] job still has active processes but no profile to defer" ,
304- self . name
305- ) ;
306- }
307- Err ( e) => {
308- warn ! (
309- "[{}] failed to query job active processes: {e:#}" ,
310- self . name
311- ) ;
312- }
276+ let Some ( job) = self . job_object . as_ref ( ) else {
277+ self . clear_windows_spawn_resources ( ) ;
278+ return false ;
279+ } ;
280+ let count = job. active_process_count ( ) ;
281+ if matches ! ( count, Ok ( 0 ) ) {
282+ self . clear_windows_spawn_resources ( ) ;
283+ return false ;
284+ }
285+ if let Err ( e) = count {
286+ warn ! (
287+ "[{}] failed to query job active processes: {e:#}" ,
288+ self . name
289+ ) ;
290+ }
291+ let needs_drain = self . defer_windows_job_drain_after_exit ( exited_pid) ;
292+ if !needs_drain {
293+ self . clear_windows_spawn_resources ( ) ;
294+ }
295+ needs_drain
296+ }
297+
298+ /// Retain profile and job handles for async drain when members may still be running.
299+ #[ cfg( windows) ]
300+ fn defer_windows_job_drain_after_exit ( & mut self , exited_pid : Option < u32 > ) -> bool {
301+ if let ( Some ( pid) , Some ( profile) ) = ( exited_pid, self . user_profile . take ( ) ) {
302+ if let Some ( ref job) = self . job_object
303+ && let Err ( e) = job. terminate ( )
304+ {
305+ warn ! (
306+ "[{}] failed to terminate residual job members before deferred drain (pid {}): {e:#}" ,
307+ self . name, pid
308+ ) ;
313309 }
310+ let job = self . job_object . take ( ) ;
311+ info ! (
312+ "[{}] deferring profile unload until job members exit (pid {pid})" ,
313+ self . name
314+ ) ;
315+ self . deferred_exit_cleanups . push ( DeferredExitCleanup {
316+ pid,
317+ user_profile : profile,
318+ job_object : job,
319+ } ) ;
320+ return true ;
314321 }
315- self . clear_windows_spawn_resources ( ) ;
322+ warn ! (
323+ "[{}] job may still have active members but no profile to defer" ,
324+ self . name
325+ ) ;
316326 false
317327 }
318328
@@ -329,7 +339,7 @@ impl ManagedProcess {
329339 . job_object
330340 . as_ref ( )
331341 . expect ( "deferred job drain entry must have a job" ) ;
332- if ! matches ! ( job. active_process_count ( ) , Ok ( 0 ) ) {
342+ if job. may_have_active_members ( ) {
333343 return false ;
334344 }
335345 let entry = self . deferred_exit_cleanups . remove ( idx) ;
@@ -359,7 +369,7 @@ impl ManagedProcess {
359369 if self . deferred_exit_cleanups [ idx]
360370 . job_object
361371 . as_ref ( )
362- . is_some_and ( |job| job . active_process_count ( ) . is_ok_and ( |count| count > 0 ) )
372+ . is_some_and ( platform :: JobObject :: may_have_active_members )
363373 {
364374 return true ;
365375 }
0 commit comments