| 469 | |
| 470 | |
| 471 | class RunWorkers: |
| 472 | def __init__(self, num_workers: int, runtests: RunTests, |
| 473 | logger: Logger, results: TestResults) -> None: |
| 474 | self.num_workers = num_workers |
| 475 | self.runtests = runtests |
| 476 | self.log = logger.log |
| 477 | self.display_progress = logger.display_progress |
| 478 | self.results: TestResults = results |
| 479 | self.live_worker_count = 0 |
| 480 | |
| 481 | self.output: queue.Queue[QueueContent] = queue.Queue() |
| 482 | tests_iter = runtests.iter_tests() |
| 483 | self.pending = MultiprocessIterator(tests_iter) |
| 484 | self.timeout = runtests.timeout |
| 485 | if self.timeout is not None: |
| 486 | # Rely on faulthandler to kill a worker process. This timouet is |
| 487 | # when faulthandler fails to kill a worker process. Give a maximum |
| 488 | # of 5 minutes to faulthandler to kill the worker. |
| 489 | self.worker_timeout: float | None = min(self.timeout * 1.5, self.timeout + 5 * 60) |
| 490 | else: |
| 491 | self.worker_timeout = None |
| 492 | self.workers: list[WorkerThread] = [] |
| 493 | |
| 494 | jobs = self.runtests.get_jobs() |
| 495 | if jobs is not None: |
| 496 | # Don't spawn more threads than the number of jobs: |
| 497 | # these worker threads would never get anything to do. |
| 498 | self.num_workers = min(self.num_workers, jobs) |
| 499 | |
| 500 | def start_workers(self) -> None: |
| 501 | self.workers = [WorkerThread(index, self) |
| 502 | for index in range(1, self.num_workers + 1)] |
| 503 | jobs = self.runtests.get_jobs() |
| 504 | if jobs is not None: |
| 505 | tests = count(jobs, 'test') |
| 506 | else: |
| 507 | tests = 'tests' |
| 508 | nworkers = len(self.workers) |
| 509 | processes = plural(nworkers, "process", "processes") |
| 510 | msg = (f"Run {tests} in parallel using " |
| 511 | f"{nworkers} worker {processes}") |
| 512 | if self.timeout and self.worker_timeout is not None: |
| 513 | msg += (" (timeout: %s, worker timeout: %s)" |
| 514 | % (format_duration(self.timeout), |
| 515 | format_duration(self.worker_timeout))) |
| 516 | self.log(msg) |
| 517 | for worker in self.workers: |
| 518 | worker.start() |
| 519 | self.live_worker_count += 1 |
| 520 | |
| 521 | def stop_workers(self) -> None: |
| 522 | start_time = time.monotonic() |
| 523 | for worker in self.workers: |
| 524 | worker.stop() |
| 525 | for worker in self.workers: |
| 526 | worker.wait_stopped(start_time) |
| 527 | |
| 528 | def _get_result(self) -> QueueOutput | None: |
no outgoing calls
no test coverage detected
searching dependent graphs…