bpo-32841: Fix cancellation in awaiting asyncio.Condition (GH-5665) (GH-5683)

(cherry picked from commit 5746510b7a)

Co-authored-by: Bar Harel <bzvi7919@gmail.com>
This commit is contained in:
Miss Islington (bot) 2018-02-14 02:10:18 -08:00 committed by Andrew Svetlov
parent 2be5435203
commit a23eecab9a
3 changed files with 34 additions and 5 deletions

View file

@ -349,12 +349,16 @@ class Condition(_ContextManagerMixin):
finally: finally:
# Must reacquire lock even if wait is cancelled # Must reacquire lock even if wait is cancelled
cancelled = False
while True: while True:
try: try:
yield from self.acquire() yield from self.acquire()
break break
except futures.CancelledError: except futures.CancelledError:
pass cancelled = True
if cancelled:
raise futures.CancelledError
@coroutine @coroutine
def wait_for(self, predicate): def wait_for(self, predicate):

View file

@ -190,11 +190,11 @@ class LockTests(test_utils.TestCase):
call_count += 1 call_count += 1
await lock.acquire() await lock.acquire()
lock_count += 1 lock_count += 1
async def lockandtrigger(): async def lockandtrigger():
await lock.acquire() await lock.acquire()
self.loop.call_soon(trigger) self.loop.call_soon(trigger)
def trigger(): def trigger():
t1.cancel() t1.cancel()
lock.release() lock.release()
@ -224,8 +224,6 @@ class LockTests(test_utils.TestCase):
test_utils.run_briefly(self.loop) test_utils.run_briefly(self.loop)
self.assertTrue(t3.cancelled()) self.assertTrue(t3.cancelled())
def test_finished_waiter_cancelled(self): def test_finished_waiter_cancelled(self):
lock = asyncio.Lock(loop=self.loop) lock = asyncio.Lock(loop=self.loop)
@ -557,6 +555,31 @@ class ConditionTests(test_utils.TestCase):
self.assertTrue(cond.locked()) self.assertTrue(cond.locked())
def test_wait_cancel_after_notify(self):
# See bpo-32841
cond = asyncio.Condition(loop=self.loop)
waited = False
async def wait_on_cond():
nonlocal waited
async with cond:
waited = True # Make sure this area was reached
await cond.wait()
waiter = asyncio.ensure_future(wait_on_cond(), loop=self.loop)
test_utils.run_briefly(self.loop) # Start waiting
self.loop.run_until_complete(cond.acquire())
cond.notify()
test_utils.run_briefly(self.loop) # Get to acquire()
waiter.cancel()
test_utils.run_briefly(self.loop) # Activate cancellation
cond.release()
test_utils.run_briefly(self.loop) # Cancellation should occur
self.assertTrue(waiter.cancelled())
self.assertTrue(waited)
def test_wait_unacquired(self): def test_wait_unacquired(self):
cond = asyncio.Condition(loop=self.loop) cond = asyncio.Condition(loop=self.loop)
self.assertRaises( self.assertRaises(

View file

@ -0,0 +1,2 @@
Fixed `asyncio.Condition` issue which silently ignored cancellation after
notifying and cancelling a conditional lock. Patch by Bar Harel.