Commit b9a43917 authored by Georg Brandl's avatar Georg Brandl

#10218: return timeout status from Condition.wait, mirroring other primitives' behavior.

parent cbb94213
...@@ -574,6 +574,12 @@ needs to wake up one consumer thread. ...@@ -574,6 +574,12 @@ needs to wake up one consumer thread.
interface is then used to restore the recursion level when the lock is interface is then used to restore the recursion level when the lock is
reacquired. reacquired.
The return value is ``True`` unless a given *timeout* expired, in which
case it is ``False``.
.. versionchanged:: 3.2
Previously, the method always returned ``None``.
.. method:: notify() .. method:: notify()
Wake up a thread waiting on this condition, if any. If the calling thread Wake up a thread waiting on this condition, if any. If the calling thread
......
...@@ -375,13 +375,13 @@ class ConditionTests(BaseTestCase): ...@@ -375,13 +375,13 @@ class ConditionTests(BaseTestCase):
phase_num = 0 phase_num = 0
def f(): def f():
cond.acquire() cond.acquire()
cond.wait() result = cond.wait()
cond.release() cond.release()
results1.append(phase_num) results1.append((result, phase_num))
cond.acquire() cond.acquire()
cond.wait() result = cond.wait()
cond.release() cond.release()
results2.append(phase_num) results2.append((result, phase_num))
b = Bunch(f, N) b = Bunch(f, N)
b.wait_for_started() b.wait_for_started()
_wait() _wait()
...@@ -394,7 +394,7 @@ class ConditionTests(BaseTestCase): ...@@ -394,7 +394,7 @@ class ConditionTests(BaseTestCase):
cond.release() cond.release()
while len(results1) < 3: while len(results1) < 3:
_wait() _wait()
self.assertEqual(results1, [1] * 3) self.assertEqual(results1, [(True, 1)] * 3)
self.assertEqual(results2, []) self.assertEqual(results2, [])
# Notify 5 threads: they might be in their first or second wait # Notify 5 threads: they might be in their first or second wait
cond.acquire() cond.acquire()
...@@ -404,8 +404,8 @@ class ConditionTests(BaseTestCase): ...@@ -404,8 +404,8 @@ class ConditionTests(BaseTestCase):
cond.release() cond.release()
while len(results1) + len(results2) < 8: while len(results1) + len(results2) < 8:
_wait() _wait()
self.assertEqual(results1, [1] * 3 + [2] * 2) self.assertEqual(results1, [(True, 1)] * 3 + [(True, 2)] * 2)
self.assertEqual(results2, [2] * 3) self.assertEqual(results2, [(True, 2)] * 3)
# Notify all threads: they are all in their second wait # Notify all threads: they are all in their second wait
cond.acquire() cond.acquire()
cond.notify_all() cond.notify_all()
...@@ -414,8 +414,8 @@ class ConditionTests(BaseTestCase): ...@@ -414,8 +414,8 @@ class ConditionTests(BaseTestCase):
cond.release() cond.release()
while len(results2) < 5: while len(results2) < 5:
_wait() _wait()
self.assertEqual(results1, [1] * 3 + [2] * 2) self.assertEqual(results1, [(True, 1)] * 3 + [(True,2)] * 2)
self.assertEqual(results2, [2] * 3 + [3] * 2) self.assertEqual(results2, [(True, 2)] * 3 + [(True, 3)] * 2)
b.wait_for_finished() b.wait_for_finished()
def test_notify(self): def test_notify(self):
...@@ -431,14 +431,20 @@ class ConditionTests(BaseTestCase): ...@@ -431,14 +431,20 @@ class ConditionTests(BaseTestCase):
def f(): def f():
cond.acquire() cond.acquire()
t1 = time.time() t1 = time.time()
cond.wait(0.5) result = cond.wait(0.5)
t2 = time.time() t2 = time.time()
cond.release() cond.release()
results.append(t2 - t1) results.append((t2 - t1, result))
Bunch(f, N).wait_for_finished() Bunch(f, N).wait_for_finished()
self.assertEqual(len(results), 5) self.assertEqual(len(results), N)
for dt in results: for dt, result in results:
self.assertTimeout(dt, 0.5) self.assertTimeout(dt, 0.5)
# Note that conceptually (that"s the condition variable protocol)
# a wait() may succeed even if no one notifies us and before any
# timeout occurs. Spurious wakeups can occur.
# This makes it hard to verify the result value.
# In practice, this implementation has no spurious wakeups.
self.assertFalse(result)
class BaseSemaphoreTests(BaseTestCase): class BaseSemaphoreTests(BaseTestCase):
......
...@@ -232,6 +232,7 @@ class _Condition(_Verbose): ...@@ -232,6 +232,7 @@ class _Condition(_Verbose):
try: # restore state no matter what (e.g., KeyboardInterrupt) try: # restore state no matter what (e.g., KeyboardInterrupt)
if timeout is None: if timeout is None:
waiter.acquire() waiter.acquire()
gotit = True
if __debug__: if __debug__:
self._note("%s.wait(): got it", self) self._note("%s.wait(): got it", self)
else: else:
...@@ -249,6 +250,7 @@ class _Condition(_Verbose): ...@@ -249,6 +250,7 @@ class _Condition(_Verbose):
else: else:
if __debug__: if __debug__:
self._note("%s.wait(%s): got it", self, timeout) self._note("%s.wait(%s): got it", self, timeout)
return gotit
finally: finally:
self._acquire_restore(saved_state) self._acquire_restore(saved_state)
......
...@@ -51,6 +51,8 @@ Core and Builtins ...@@ -51,6 +51,8 @@ Core and Builtins
Library Library
------- -------
- Issue #10218: Return timeout status from ``Condition.wait`` in threading.
- Issue #7351: Add ``zipfile.BadZipFile`` spelling of the exception name - Issue #7351: Add ``zipfile.BadZipFile`` spelling of the exception name
and deprecate the old name ``zipfile.BadZipfile``. and deprecate the old name ``zipfile.BadZipfile``.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment