Skip to content

Commit 3234061

Browse files
[3.14] gh-160326: Raise ValueError for a NaN timeout in Condition.wait() (GH-160327) (#160417)
gh-160326: Raise ValueError for a NaN timeout in Condition.wait() (GH-160327) A NaN timeout was treated as zero, which made the deadline loops built on Condition.wait(), such as Queue.get() and Semaphore.acquire(), spin without ever timing out. (cherry picked from commit 3387b19) Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
1 parent 4a455fe commit 3234061

3 files changed

Lines changed: 21 additions & 3 deletions

File tree

‎Lib/test/lock_tests.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,10 @@ def f():
557557
for r, dt in results2:
558558
self.assertTrue(r)
559559

560+
def test_nan_timeout(self):
561+
evt = self.eventtype()
562+
self.assertRaises(ValueError, evt.wait, float('nan'))
563+
560564
def test_set_and_clear(self):
561565
# gh-57711: check that wait() returns true even when the event is
562566
# cleared before the waiting thread is woken up.
@@ -755,6 +759,14 @@ def f():
755759
# In practice, this implementation has no spurious wakeups.
756760
self.assertFalse(result)
757761

762+
def test_nan_timeout(self):
763+
cond = self.condtype()
764+
nan = float('nan')
765+
with cond:
766+
self.assertRaises(ValueError, cond.wait, nan)
767+
self.assertRaises(ValueError, cond.wait_for, lambda: False, nan)
768+
self.assertEqual(len(cond._waiters), 0)
769+
758770
def test_waitfor(self):
759771
cond = self.condtype()
760772
state = 0

‎Lib/threading.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,10 +369,11 @@ def wait(self, timeout=None):
369369
waiter.acquire()
370370
gotit = True
371371
else:
372-
if timeout > 0:
373-
gotit = waiter.acquire(True, timeout)
374-
else:
372+
if timeout <= 0:
375373
gotit = waiter.acquire(False)
374+
else:
375+
# A NaN timeout is rejected by acquire().
376+
gotit = waiter.acquire(True, timeout)
376377
return gotit
377378
finally:
378379
self._acquire_restore(saved_state)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Raise :exc:`ValueError` for a NaN *timeout* in
2+
:meth:`threading.Condition.wait` and the blocking calls built on it, such as
3+
:meth:`threading.Event.wait`, :meth:`threading.Semaphore.acquire` and
4+
:meth:`queue.Queue.get`, instead of returning at once or looping without ever
5+
timing out.

0 commit comments

Comments
 (0)