diff options
author | Yury Selivanov <yury@magic.io> | 2016-06-11 12:00:07 -0400 |
---|---|---|
committer | Yury Selivanov <yury@magic.io> | 2016-06-11 12:00:07 -0400 |
commit | 76e3aa4006a7fe203b5e8c52009a14db054f5d81 (patch) | |
tree | d49a9cd6f90ae7873d72b27a23d86993bb4f9c97 | |
parent | bed123f2dca32db59eb448c453c4c002f1cce77b (diff) | |
download | cpython-76e3aa4006a7fe203b5e8c52009a14db054f5d81.tar.gz |
Issue #22970: asyncio: Fix inconsistency cancelling Condition.wait.
Patch by David Coles.
-rw-r--r-- | Lib/asyncio/locks.py | 8 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_locks.py | 25 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
3 files changed, 35 insertions, 1 deletions
diff --git a/Lib/asyncio/locks.py b/Lib/asyncio/locks.py index 842d6210d5..741aaf27c5 100644 --- a/Lib/asyncio/locks.py +++ b/Lib/asyncio/locks.py @@ -329,7 +329,13 @@ class Condition(_ContextManagerMixin): self._waiters.remove(fut) finally: - yield from self.acquire() + # Must reacquire lock even if wait is cancelled + while True: + try: + yield from self.acquire() + break + except futures.CancelledError: + pass @coroutine def wait_for(self, predicate): diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py index cdf5d9d3b6..d3bdc51385 100644 --- a/Lib/test/test_asyncio/test_locks.py +++ b/Lib/test/test_asyncio/test_locks.py @@ -457,6 +457,31 @@ class ConditionTests(test_utils.TestCase): self.assertFalse(cond._waiters) self.assertTrue(cond.locked()) + def test_wait_cancel_contested(self): + cond = asyncio.Condition(loop=self.loop) + + self.loop.run_until_complete(cond.acquire()) + self.assertTrue(cond.locked()) + + wait_task = asyncio.Task(cond.wait(), loop=self.loop) + test_utils.run_briefly(self.loop) + self.assertFalse(cond.locked()) + + # Notify, but contest the lock before cancelling + self.loop.run_until_complete(cond.acquire()) + self.assertTrue(cond.locked()) + cond.notify() + self.loop.call_soon(wait_task.cancel) + self.loop.call_soon(cond.release) + + try: + self.loop.run_until_complete(wait_task) + except asyncio.CancelledError: + # Should not happen, since no cancellation points + pass + + self.assertTrue(cond.locked()) + def test_wait_unacquired(self): cond = asyncio.Condition(loop=self.loop) self.assertRaises( @@ -524,6 +524,9 @@ Library _conn_lost. Patch by Ćukasz Langa. +- Issue #22970: asyncio: Fix inconsistency cancelling Condition.wait. + Patch by David Coles. + IDLE ---- |