@@ -32,6 +32,7 @@ const POST_LAUNCH_CRASH_SWEEP_DELAY_MS = 1000;
3232const RECENT_LAUNCH_WINDOW_MS = 5000 ;
3333const SUSPICION_WINDOW_MS = 3000 ;
3434const CRASH_ARTIFACT_MIN_OCCURRED_AT_TOLERANCE_MS = 2000 ;
35+ const LOG_STREAM_SHUTDOWN_TIMEOUT_MS = 1000 ;
3536
3637type TimedLogLine = {
3738 line : string ;
@@ -473,6 +474,58 @@ const waitForPollInterval = async (signal: AbortSignal) => {
473474 } ) ;
474475} ;
475476
477+ const waitForTaskShutdown = async (
478+ task : Promise < void > | null ,
479+ timeoutMs : number ,
480+ timeoutMessage : string
481+ ) => {
482+ if ( ! task ) {
483+ return ;
484+ }
485+
486+ let timeout : ReturnType < typeof setTimeout > | null = null ;
487+ const timeoutTask = new Promise < 'timeout' > ( ( resolve ) => {
488+ timeout = setTimeout ( ( ) => resolve ( 'timeout' ) , timeoutMs ) ;
489+ } ) ;
490+
491+ try {
492+ const result = await Promise . race ( [
493+ task . then (
494+ ( ) => 'settled' as const ,
495+ ( error ) => {
496+ iosAppMonitorLogger . debug ( 'iOS monitor task stopped with error' , error ) ;
497+ return 'settled' as const ;
498+ }
499+ ) ,
500+ timeoutTask ,
501+ ] ) ;
502+
503+ if ( result === 'timeout' ) {
504+ iosAppMonitorLogger . warn ( timeoutMessage ) ;
505+ }
506+ } finally {
507+ if ( timeout ) {
508+ clearTimeout ( timeout ) ;
509+ }
510+ }
511+ } ;
512+
513+ const stopLogProcess = async ( process : Subprocess | null ) => {
514+ if ( ! process ) {
515+ return ;
516+ }
517+
518+ try {
519+ const childProcess = await process . nodeChildProcess ;
520+
521+ if ( ! childProcess . killed ) {
522+ childProcess . kill ( ) ;
523+ }
524+ } catch {
525+ // Ignore termination failures for background monitors.
526+ }
527+ } ;
528+
476529const createProcessExitDetails = ( {
477530 platform,
478531 processName,
@@ -1063,15 +1116,12 @@ export const createIosSimulatorAppMonitor = ({
10631116 logTask = null ;
10641117 pollTask = null ;
10651118
1066- if ( currentLogProcess ) {
1067- try {
1068- ( await currentLogProcess . nodeChildProcess ) . kill ( ) ;
1069- } catch {
1070- // Ignore termination failures for background monitors.
1071- }
1072- }
1073-
1074- await currentLogTask ;
1119+ await stopLogProcess ( currentLogProcess ) ;
1120+ await waitForTaskShutdown (
1121+ currentLogTask ,
1122+ LOG_STREAM_SHUTDOWN_TIMEOUT_MS ,
1123+ 'iOS simulator log stream did not stop within shutdown timeout; continuing'
1124+ ) ;
10751125 await currentPollTask ;
10761126 } ,
10771127 dispose : async ( ) => {
@@ -1089,15 +1139,12 @@ export const createIosSimulatorAppMonitor = ({
10891139 logTask = null ;
10901140 pollTask = null ;
10911141
1092- if ( currentLogProcess ) {
1093- try {
1094- ( await currentLogProcess . nodeChildProcess ) . kill ( ) ;
1095- } catch {
1096- // Ignore termination failures for background monitors.
1097- }
1098- }
1099-
1100- await currentLogTask ;
1142+ await stopLogProcess ( currentLogProcess ) ;
1143+ await waitForTaskShutdown (
1144+ currentLogTask ,
1145+ LOG_STREAM_SHUTDOWN_TIMEOUT_MS ,
1146+ 'iOS simulator log stream did not stop within dispose timeout; continuing'
1147+ ) ;
11011148 await currentPollTask ;
11021149 } ,
11031150 launchRequested : ( event ) => {
0 commit comments