| 268 | |
| 269 | |
| 270 | class ExThread(threading.Thread): |
| 271 | def __init__(self, target: Callable[[], None], name: str) -> None: |
| 272 | super().__init__(target=target, name=name) |
| 273 | self.ex: _ExcInfoType | None = None |
| 274 | |
| 275 | def run(self) -> None: |
| 276 | try: |
| 277 | super().run() |
| 278 | except Exception: # pragma: no cover # a worker that raises fails the test; a healthy run never lands here |
| 279 | self.ex = sys.exc_info() |
| 280 | |
| 281 | def join(self, timeout: float | None = None) -> None: |
| 282 | super().join(timeout=timeout) |
| 283 | if self.ex is not None: |
| 284 | raise RuntimeError from self.ex[1] # pragma: no cover # only a worker that raised gets re-raised here |
| 285 | |
| 286 | |
| 287 | # 100 threads x 100 acquisitions is thousands of lock cycles; the 20s default is tight on a loaded Windows runner. |
no outgoing calls