From 17bafba563e39684a90e4e3ce14bc2a90886f008 Mon Sep 17 00:00:00 2001 From: Noah Stapp Date: Tue, 27 Oct 2026 09:56:34 -0400 Subject: [PATCH] PYTHON-6194 Fix SpecRunnerThread lost wakeup race --- test/asynchronous/utils_spec_runner.py | 27 ++++++++++++++------------ test/utils_spec_runner.py | 27 ++++++++++++++------------ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/test/asynchronous/utils_spec_runner.py b/test/asynchronous/utils_spec_runner.py index e93bab269b..d36c010749 100644 --- a/test/asynchronous/utils_spec_runner.py +++ b/test/asynchronous/utils_spec_runner.py @@ -37,27 +37,30 @@ def __init__(self, name): self.ops = [] async def schedule(self, work): - self.ops.append(work) async with self.cond: + self.ops.append(work) self.cond.notify() async def stop(self): - self.stopped = True async with self.cond: + self.stopped = True self.cond.notify() async def run(self): - while not self.stopped or self.ops: - if not self.ops: - async with self.cond: + while True: + # Check ops and stopped while holding the lock so a notify() from + # schedule() or stop() can't be missed before the wait starts. + async with self.cond: + while not self.ops and not self.stopped: await _async_cond_wait(self.cond, 10) - if self.ops: - try: - work = self.ops.pop(0) - await work() - except Exception as exc: - self.exc = exc - await self.stop() + if not self.ops: + break + work = self.ops.pop(0) + try: + await work() + except Exception as exc: + self.exc = exc + await self.stop() class AsyncSpecTestCreator: diff --git a/test/utils_spec_runner.py b/test/utils_spec_runner.py index 5c63cd08c4..fbc8f25550 100644 --- a/test/utils_spec_runner.py +++ b/test/utils_spec_runner.py @@ -37,27 +37,30 @@ def __init__(self, name): self.ops = [] def schedule(self, work): - self.ops.append(work) with self.cond: + self.ops.append(work) self.cond.notify() def stop(self): - self.stopped = True with self.cond: + self.stopped = True self.cond.notify() def run(self): - while not self.stopped or self.ops: - if not self.ops: - with self.cond: + while True: + # Check ops and stopped while holding the lock so a notify() from + # schedule() or stop() can't be missed before the wait starts. + with self.cond: + while not self.ops and not self.stopped: _cond_wait(self.cond, 10) - if self.ops: - try: - work = self.ops.pop(0) - work() - except Exception as exc: - self.exc = exc - self.stop() + if not self.ops: + break + work = self.ops.pop(0) + try: + work() + except Exception as exc: + self.exc = exc + self.stop() class SpecTestCreator: