2727using  System . Net . Http ; 
2828using  System . Threading . Tasks ; 
2929using  OpenQA . Selenium . Internal . Logging ; 
30+ using  System . Threading ; 
31+ using  OpenQA . Selenium . Internal ; 
3032
3133namespace  OpenQA . Selenium ; 
3234
@@ -170,40 +172,47 @@ public int ProcessId
170172    /// </summary> 
171173    protected  virtual  bool  HasShutdown  =>  true ; 
172174
175+     static   TaskFactory  _tf  =  new  TaskFactory ( ) ; 
176+ 
173177    /// <summary> 
174178    /// Gets a value indicating whether the service is responding to HTTP requests. 
175179    /// </summary> 
176-     protected  virtual  bool  IsInitialized 
180+     protected  virtual  async   Task < bool >  IsInitialized ( ) 
177181    { 
178-         get 
179-         { 
180-             bool  isInitialized  =  false ; 
182+         bool  isInitialized  =  false ; 
181183
182-             try 
184+         try 
185+         { 
186+             using  ( var  httpClient  =  new  HttpClient ( ) ) 
183187            { 
184-                 using  ( var  httpClient  =  new  HttpClient ( ) ) 
188+                 //httpClient.DefaultRequestHeaders.ConnectionClose = true; 
189+                 //httpClient.Timeout = TimeSpan.FromSeconds(2); 
190+ 
191+                 Uri  serviceHealthUri  =  new  Uri ( this . ServiceUrl ,  new  Uri ( DriverCommand . Status ,  UriKind . Relative ) ) ; 
192+ 
193+                 _logger . Debug ( $ "Probing HTTP: { serviceHealthUri } ") ; 
194+ 
195+                 _logger . Debug ( "Sending GET" ) ; 
196+ 
197+                 using  ( var  response  =  await  httpClient . GetAsync ( serviceHealthUri ) . ConfigureAwait ( false ) ) 
185198                { 
186-                     httpClient . DefaultRequestHeaders . ConnectionClose  =  true ; 
187-                     httpClient . Timeout  =  TimeSpan . FromSeconds ( 5 ) ; 
199+                     // Checking the response from the 'status' end point. Note that we are simply checking 
200+                     // that the HTTP status returned is a 200 status, and that the response has the correct 
201+                     // Content-Type header. A more sophisticated check would parse the JSON response and 
202+                     // validate its values. At the moment we do not do this more sophisticated check. 
203+                     isInitialized  =  response . StatusCode  ==  HttpStatusCode . OK  &&  response . Content . Headers . ContentType  is  {  MediaType :  string  mediaType  }  &&  mediaType . StartsWith ( "application/json" ,  StringComparison . OrdinalIgnoreCase ) ; 
188204
189-                     Uri  serviceHealthUri  =  new  Uri ( this . ServiceUrl ,  new  Uri ( DriverCommand . Status ,  UriKind . Relative ) ) ; 
190-                     using  ( var  response  =  Task . Run ( async  ( )  =>  await  httpClient . GetAsync ( serviceHealthUri ) ) . GetAwaiter ( ) . GetResult ( ) ) 
191-                     { 
192-                         // Checking the response from the 'status' end point. Note that we are simply checking 
193-                         // that the HTTP status returned is a 200 status, and that the response has the correct 
194-                         // Content-Type header. A more sophisticated check would parse the JSON response and 
195-                         // validate its values. At the moment we do not do this more sophisticated check. 
196-                         isInitialized  =  response . StatusCode  ==  HttpStatusCode . OK  &&  response . Content . Headers . ContentType  is  {  MediaType :  string  mediaType  }  &&  mediaType . StartsWith ( "application/json" ,  StringComparison . OrdinalIgnoreCase ) ; 
197-                     } 
205+                     _logger . Debug ( $ "Probed HTTP: { isInitialized } ") ; 
198206                } 
199207            } 
200-             catch  ( Exception  ex )  when  ( ex  is  HttpRequestException  ||  ex  is  TaskCanceledException ) 
201-             { 
202-                 // Do nothing. The exception is expected, meaning driver service is not initialized. 
203-             } 
204- 
205-             return  isInitialized ; 
206208        } 
209+         catch  ( Exception  ex )  when  ( ex  is  HttpRequestException  ||  ex  is  TaskCanceledException ) 
210+         { 
211+             _logger . Warn ( $ "Probing failed: { ex } ") ; 
212+             // Do nothing. The exception is expected, meaning driver service is not initialized. 
213+         } 
214+ 
215+         return  isInitialized ; 
207216    } 
208217
209218    /// <summary> 
@@ -215,11 +224,15 @@ public void Dispose()
215224        GC . SuppressFinalize ( this ) ; 
216225    } 
217226
227+     // Replaced Task-based readers with dedicated threads 
228+     private  Thread ?  _outputThread ; 
229+     private  Thread ?  _errorThread ; 
230+ 
218231    /// <summary> 
219232    /// Starts the DriverService if it is not already running. 
220233    /// </summary> 
221234    [ MemberNotNull ( nameof ( driverServiceProcess ) ) ] 
222-     public  void  Start ( ) 
235+     public  async   Task  Start ( ) 
223236    { 
224237        if  ( this . driverServiceProcess  !=  null ) 
225238        { 
@@ -249,8 +262,8 @@ public void Start()
249262        this . driverServiceProcess . StartInfo . RedirectStandardOutput  =  true ; 
250263        this . driverServiceProcess . StartInfo . RedirectStandardError  =  true ; 
251264
252-         this . driverServiceProcess . OutputDataReceived  +=  this . OnDriverProcessDataReceived ; 
253-         this . driverServiceProcess . ErrorDataReceived  +=  this . OnDriverProcessDataReceived ; 
265+         // this.driverServiceProcess.OutputDataReceived += this.OnDriverProcessDataReceived;
266+         // this.driverServiceProcess.ErrorDataReceived += this.OnDriverProcessDataReceived;
254267
255268        DriverProcessStartingEventArgs  eventArgs  =  new  DriverProcessStartingEventArgs ( this . driverServiceProcess . StartInfo ) ; 
256269        this . OnDriverProcessStarting ( eventArgs ) ; 
@@ -262,10 +275,83 @@ public void Start()
262275
263276        // Important: Start the process and immediately begin reading the output and error streams to avoid IO deadlocks. 
264277        this . driverServiceProcess . Start ( ) ; 
265-         this . driverServiceProcess . BeginOutputReadLine ( ) ; 
266-         this . driverServiceProcess . BeginErrorReadLine ( ) ; 
278+         // this.driverServiceProcess.BeginOutputReadLine();
279+         // this.driverServiceProcess.BeginErrorReadLine();
267280
268-         bool  serviceAvailable  =  this . WaitForServiceInitialization ( ) ; 
281+         if  ( _logger . IsEnabled ( LogEventLevel . Trace ) ) 
282+         { 
283+             // Capture a stable reference to the process to avoid races with Stop() nulling the field 
284+             var  proc  =  this . driverServiceProcess ; 
285+ 
286+             _outputThread  =  new  Thread ( ( )  => 
287+             { 
288+                 try 
289+                 { 
290+                     if  ( proc  ==  null ) 
291+                     { 
292+                         return ; 
293+                     } 
294+ 
295+                     using  ( var  reader  =  proc . StandardOutput ) 
296+                     { 
297+                         string ?  line ; 
298+                         while  ( ( line  =  reader . ReadLine ( ) )  !=  null ) 
299+                         { 
300+                             OnDriverProcessDataReceived ( line ) ; 
301+                         } 
302+                     } 
303+                 } 
304+                 catch  ( ObjectDisposedException ) 
305+                 { 
306+                     // Process or stream disposed during shutdown; ignore 
307+                 } 
308+                 catch  ( IOException ) 
309+                 { 
310+                     // Stream closed; ignore 
311+                 } 
312+             } ) 
313+             { 
314+                 IsBackground  =  true , 
315+                 Name  =  "DriverService-stdout" 
316+             } ; 
317+ 
318+             _errorThread  =  new  Thread ( ( )  => 
319+             { 
320+                 try 
321+                 { 
322+                     if  ( proc  ==  null ) 
323+                     { 
324+                         return ; 
325+                     } 
326+ 
327+                     using  ( var  reader  =  proc . StandardError ) 
328+                     { 
329+                         string ?  line ; 
330+                         while  ( ( line  =  reader . ReadLine ( ) )  !=  null ) 
331+                         { 
332+                             OnDriverProcessDataReceived ( line ) ; 
333+                         } 
334+                     } 
335+                 } 
336+                 catch  ( ObjectDisposedException ) 
337+                 { 
338+                     // Process or stream disposed during shutdown; ignore 
339+                 } 
340+                 catch  ( IOException ) 
341+                 { 
342+                     // Stream closed; ignore 
343+                 } 
344+             } ) 
345+             { 
346+                 IsBackground  =  true , 
347+                 Name  =  "DriverService-stderr" 
348+             } ; 
349+ 
350+             _outputThread . Start ( ) ; 
351+             _errorThread . Start ( ) ; 
352+         } 
353+ 
354+         bool  serviceAvailable  =  await  this . WaitForServiceInitialization ( ) . ConfigureAwait ( false ) ; 
269355
270356        DriverProcessStartedEventArgs  processStartedEventArgs  =  new  DriverProcessStartedEventArgs ( this . driverServiceProcess ) ; 
271357        this . OnDriverProcessStarted ( processStartedEventArgs ) ; 
@@ -301,8 +387,8 @@ protected virtual void Dispose(bool disposing)
301387
302388                if  ( this . driverServiceProcess  is  not null ) 
303389                { 
304-                     this . driverServiceProcess . OutputDataReceived  -=  this . OnDriverProcessDataReceived ; 
305-                     this . driverServiceProcess . ErrorDataReceived  -=  this . OnDriverProcessDataReceived ; 
390+                     // this.driverServiceProcess.OutputDataReceived -= this.OnDriverProcessDataReceived;
391+                     // this.driverServiceProcess.ErrorDataReceived -= this.OnDriverProcessDataReceived;
306392                } 
307393            } 
308394
@@ -343,14 +429,14 @@ protected virtual void OnDriverProcessStarted(DriverProcessStartedEventArgs even
343429    /// </summary> 
344430    /// <param name="sender">The sender of the event.</param> 
345431    /// <param name="args">The data received event arguments.</param> 
346-     protected  virtual  void  OnDriverProcessDataReceived ( object   sender ,   DataReceivedEventArgs   args ) 
432+     protected  virtual  void  OnDriverProcessDataReceived ( string ?   data ) 
347433    { 
348-         if  ( string . IsNullOrEmpty ( args . Data ) ) 
434+         if  ( string . IsNullOrEmpty ( data ) ) 
349435            return ; 
350436
351437        if  ( _logger . IsEnabled ( LogEventLevel . Trace ) ) 
352438        { 
353-             _logger . Trace ( args . Data ) ; 
439+             _logger . Trace ( data ) ; 
354440        } 
355441    } 
356442
@@ -378,7 +464,7 @@ private void Stop()
378464                            // we'll retry. We wait for exit here, since catching the exception 
379465                            // for a failed HTTP request due to a closed socket is particularly 
380466                            // expensive. 
381-                             using  ( var  response  =  Task . Run ( async  ( )  =>  await  httpClient . GetAsync ( shutdownUrl ) ) . GetAwaiter ( ) . GetResult ( ) ) 
467+                             using  ( var  response  =  Task . Run ( async  ( )  =>  await  httpClient . GetAsync ( shutdownUrl ) . ConfigureAwait ( false ) ) . GetAwaiter ( ) . GetResult ( ) ) 
382468                            { 
383469
384470                            } 
@@ -404,8 +490,17 @@ private void Stop()
404490                } 
405491            } 
406492
493+             // Wait for output/error reader threads to finish to avoid IO deadlocks 
494+             _outputThread ? . Join ( ) ; 
495+             _errorThread ? . Join ( ) ; 
496+ 
407497            this . driverServiceProcess . Dispose ( ) ; 
408498            this . driverServiceProcess  =  null ; 
499+ 
500+             if  ( _logger . IsEnabled ( LogEventLevel . Debug ) ) 
501+             { 
502+                 _logger . Debug ( "Driver service is stopped" ) ; 
503+             } 
409504        } 
410505    } 
411506
@@ -415,29 +510,32 @@ private void Stop()
415510    /// </summary> 
416511    /// <returns><see langword="true"/> if the service is properly started and receiving HTTP requests; 
417512    /// otherwise; <see langword="false"/>.</returns> 
418-     private  bool  WaitForServiceInitialization ( ) 
513+     private  async   Task < bool >  WaitForServiceInitialization ( ) 
419514    { 
420515        if  ( _logger . IsEnabled ( LogEventLevel . Debug ) ) 
421516        { 
422517            _logger . Debug ( "Waiting until driver service is initialized" ) ; 
423518        } 
424519
520+         var  sw  =  Stopwatch . StartNew ( ) ; 
521+ 
425522        bool  isInitialized  =  false ; 
426523        DateTime  timeout  =  DateTime . Now . Add ( this . InitializationTimeout ) ; 
427524        while  ( ! isInitialized  &&  DateTime . Now  <  timeout ) 
428525        { 
526+             _logger . Debug ( "LOOP" ) ; 
429527            // If the driver service process has exited, we can exit early. 
430-             if  ( ! this . IsRunning ) 
431-             { 
432-                 break ; 
433-             } 
528+             // if (!this.IsRunning)
529+             // {
530+             //     break;
531+             // }
434532
435-             isInitialized  =  this . IsInitialized ; 
533+             isInitialized  =  await   this . IsInitialized ( ) . ConfigureAwait ( false ) ; 
436534        } 
437535
438536        if  ( _logger . IsEnabled ( LogEventLevel . Debug ) ) 
439537        { 
440-             _logger . Debug ( $ "Driver service initialization status: { isInitialized } ") ; 
538+             _logger . Debug ( $ "Driver service initialization status: { isInitialized }   { sw . Elapsed } ") ; 
441539        } 
442540
443541        return  isInitialized ; 
0 commit comments