From 146eedbbfecd69f76acff0d3f65bf688cbbf597f Mon Sep 17 00:00:00 2001 From: Matthias Goergens Date: Mon, 28 Sep 2026 18:26:22 +0800 Subject: [PATCH] gh-158336: Fix race in lock_tests test_set_and_clear test_set_and_clear started N threads calling event.wait() and then slept 50 ms (wait_threads_blocked) before calling event.set() and event.clear(). If a thread only reached wait() after clear(), it saw the flag unset and blocked for LONG_TIMEOUT, and Bunch.__exit__ gave up after SHORT_TIMEOUT. This happened on CI in the TSan free-threading job. Wait until all N threads are in the Condition's waiter list instead. Condition.wait() appends the waiter while holding the lock, and notify_all() releases every waiter in the list, so each of those threads is guaranteed to be woken by set(). On a TSan free-threading debug build, pinned to 2 CPUs shared with 4 busy-looping processes, the test failed in 9 of 40 runs before this change and 0 of 40 after. --- Lib/test/lock_tests.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index 691029a1a54f7f..a13702cbf544c0 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -572,8 +572,13 @@ def f(): N = 5 with Bunch(f, N): - # Threads blocked on event.wait() - wait_threads_blocked(N) + # Wait until all threads are registered as waiters in + # event.wait(). A thread that only reaches wait() after set() + # and clear() would block until the timeout, so a fixed sleep + # is not enough on a busy machine. + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if len(event._cond._waiters) >= N: + break # Threads unblocked event.set()