@@ -79,8 +79,12 @@ class MultiprocessResult:
79
79
err_msg : str | None = None
80
80
81
81
82
+ class WorkerThreadExited :
83
+ """Indicates that a worker thread has exited"""
84
+
82
85
ExcStr = str
83
86
QueueOutput = tuple [Literal [False ], MultiprocessResult ] | tuple [Literal [True ], ExcStr ]
87
+ QueueContent = QueueOutput | WorkerThreadExited
84
88
85
89
86
90
class ExitThread (Exception ):
@@ -376,8 +380,8 @@ def _runtest(self, test_name: TestName) -> MultiprocessResult:
376
380
def run (self ) -> None :
377
381
fail_fast = self .runtests .fail_fast
378
382
fail_env_changed = self .runtests .fail_env_changed
379
- while not self . _stopped :
380
- try :
383
+ try :
384
+ while not self . _stopped :
381
385
try :
382
386
test_name = next (self .pending )
383
387
except StopIteration :
@@ -396,11 +400,12 @@ def run(self) -> None:
396
400
397
401
if mp_result .result .must_stop (fail_fast , fail_env_changed ):
398
402
break
399
- except ExitThread :
400
- break
401
- except BaseException :
402
- self .output .put ((True , traceback .format_exc ()))
403
- break
403
+ except ExitThread :
404
+ pass
405
+ except BaseException :
406
+ self .output .put ((True , traceback .format_exc ()))
407
+ finally :
408
+ self .output .put (WorkerThreadExited ())
404
409
405
410
def _wait_completed (self ) -> None :
406
411
popen = self ._popen
@@ -458,8 +463,9 @@ def __init__(self, num_workers: int, runtests: RunTests,
458
463
self .log = logger .log
459
464
self .display_progress = logger .display_progress
460
465
self .results : TestResults = results
466
+ self .live_worker_count = 0
461
467
462
- self .output : queue .Queue [QueueOutput ] = queue .Queue ()
468
+ self .output : queue .Queue [QueueContent ] = queue .Queue ()
463
469
tests_iter = runtests .iter_tests ()
464
470
self .pending = MultiprocessIterator (tests_iter )
465
471
self .timeout = runtests .timeout
@@ -497,6 +503,7 @@ def start_workers(self) -> None:
497
503
self .log (msg )
498
504
for worker in self .workers :
499
505
worker .start ()
506
+ self .live_worker_count += 1
500
507
501
508
def stop_workers (self ) -> None :
502
509
start_time = time .monotonic ()
@@ -511,14 +518,18 @@ def _get_result(self) -> QueueOutput | None:
511
518
512
519
# bpo-46205: check the status of workers every iteration to avoid
513
520
# waiting forever on an empty queue.
514
- while any ( worker . is_alive () for worker in self . workers ) :
521
+ while self . live_worker_count > 0 :
515
522
if use_faulthandler :
516
523
faulthandler .dump_traceback_later (MAIN_PROCESS_TIMEOUT ,
517
524
exit = True )
518
525
519
526
# wait for a thread
520
527
try :
521
- return self .output .get (timeout = PROGRESS_UPDATE )
528
+ result = self .output .get (timeout = PROGRESS_UPDATE )
529
+ if isinstance (result , WorkerThreadExited ):
530
+ self .live_worker_count -= 1
531
+ continue
532
+ return result
522
533
except queue .Empty :
523
534
pass
524
535
@@ -528,12 +539,6 @@ def _get_result(self) -> QueueOutput | None:
528
539
if running :
529
540
self .log (running )
530
541
531
- # all worker threads are done: consume pending results
532
- try :
533
- return self .output .get (timeout = 0 )
534
- except queue .Empty :
535
- return None
536
-
537
542
def display_result (self , mp_result : MultiprocessResult ) -> None :
538
543
result = mp_result .result
539
544
pgo = self .runtests .pgo
0 commit comments