Skip to content

Commit e44160b

Browse files
[3.15] gh-160326: Raise ValueError for a NaN timeout in Condition.wait() (GH-160327) (#160416)
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 45f5b00 commit e44160b

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
@@ -562,6 +562,10 @@ def f():
562562
for r, dt in results2:
563563
self.assertTrue(r)
564564

565+
def test_nan_timeout(self):
566+
evt = self.eventtype()
567+
self.assertRaises(ValueError, evt.wait, float('nan'))
568+
565569
def test_set_and_clear(self):
566570
# gh-57711: check that wait() returns true even when the event is
567571
# cleared before the waiting thread is woken up.
@@ -760,6 +764,14 @@ def f():
760764
# In practice, this implementation has no spurious wakeups.
761765
self.assertFalse(result)
762766

767+
def test_nan_timeout(self):
768+
cond = self.condtype()
769+
nan = float('nan')
770+
with cond:
771+
self.assertRaises(ValueError, cond.wait, nan)
772+
self.assertRaises(ValueError, cond.wait_for, lambda: False, nan)
773+
self.assertEqual(len(cond._waiters), 0)
774+
763775
def test_waitfor(self):
764776
cond = self.condtype()
765777
state = 0

‎Lib/threading.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,10 +363,11 @@ def wait(self, timeout=None):
363363
waiter.acquire()
364364
gotit = True
365365
else:
366-
if timeout > 0:
367-
gotit = waiter.acquire(True, timeout)
368-
else:
366+
if timeout <= 0:
369367
gotit = waiter.acquire(False)
368+
else:
369+
# A NaN timeout is rejected by acquire().
370+
gotit = waiter.acquire(True, timeout)
370371
return gotit
371372
finally:
372373
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)