Skip to content

Commit 5746510

Browse files
bharelasvetlov
authored andcommitted
bpo-32841: Fix cancellation in awaiting asyncio.Condition (#5665)
1 parent 3384d38 commit 5746510

File tree

3 files changed

+34
-5
lines changed

3 files changed

+34
-5
lines changed

Lib/asyncio/locks.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,12 +358,16 @@ async def wait(self):
358358

359359
finally:
360360
# Must reacquire lock even if wait is cancelled
361+
cancelled = False
361362
while True:
362363
try:
363364
await self.acquire()
364365
break
365366
except futures.CancelledError:
366-
pass
367+
cancelled = True
368+
369+
if cancelled:
370+
raise futures.CancelledError
367371

368372
async def wait_for(self, predicate):
369373
"""Wait until a predicate becomes true.

Lib/test/test_asyncio/test_locks.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,11 @@ async def lockit():
214214
call_count += 1
215215
await lock.acquire()
216216
lock_count += 1
217-
217+
218218
async def lockandtrigger():
219219
await lock.acquire()
220220
self.loop.call_soon(trigger)
221-
221+
222222
def trigger():
223223
t1.cancel()
224224
lock.release()
@@ -248,8 +248,6 @@ def trigger():
248248
test_utils.run_briefly(self.loop)
249249
self.assertTrue(t3.cancelled())
250250

251-
252-
253251
def test_finished_waiter_cancelled(self):
254252
lock = asyncio.Lock(loop=self.loop)
255253

@@ -576,6 +574,31 @@ def test_wait_cancel_contested(self):
576574

577575
self.assertTrue(cond.locked())
578576

577+
def test_wait_cancel_after_notify(self):
578+
# See bpo-32841
579+
cond = asyncio.Condition(loop=self.loop)
580+
waited = False
581+
582+
async def wait_on_cond():
583+
nonlocal waited
584+
async with cond:
585+
waited = True # Make sure this area was reached
586+
await cond.wait()
587+
588+
waiter = asyncio.ensure_future(wait_on_cond(), loop=self.loop)
589+
test_utils.run_briefly(self.loop) # Start waiting
590+
591+
self.loop.run_until_complete(cond.acquire())
592+
cond.notify()
593+
test_utils.run_briefly(self.loop) # Get to acquire()
594+
waiter.cancel()
595+
test_utils.run_briefly(self.loop) # Activate cancellation
596+
cond.release()
597+
test_utils.run_briefly(self.loop) # Cancellation should occur
598+
599+
self.assertTrue(waiter.cancelled())
600+
self.assertTrue(waited)
601+
579602
def test_wait_unacquired(self):
580603
cond = asyncio.Condition(loop=self.loop)
581604
self.assertRaises(
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed `asyncio.Condition` issue which silently ignored cancellation after
2+
notifying and cancelling a conditional lock. Patch by Bar Harel.

0 commit comments

Comments
 (0)