Skip to content
This repository was archived by the owner on Nov 23, 2017. It is now read-only.

Commit 27218fa

Browse files
committed
Fix inconsistency cancelling Condition.wait
Patch by David Coles.
1 parent 66b4ad7 commit 27218fa

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-1
lines changed

asyncio/locks.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,13 @@ def wait(self):
329329
self._waiters.remove(fut)
330330

331331
finally:
332-
yield from self.acquire()
332+
# Must reacquire lock even if wait is cancelled
333+
while True:
334+
try:
335+
yield from self.acquire()
336+
break
337+
except futures.CancelledError:
338+
pass
333339

334340
@coroutine
335341
def wait_for(self, predicate):

tests/test_locks.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,31 @@ def test_wait_cancel(self):
457457
self.assertFalse(cond._waiters)
458458
self.assertTrue(cond.locked())
459459

460+
def test_wait_cancel_contested(self):
461+
cond = asyncio.Condition(loop=self.loop)
462+
463+
self.loop.run_until_complete(cond.acquire())
464+
self.assertTrue(cond.locked())
465+
466+
wait_task = asyncio.Task(cond.wait(), loop=self.loop)
467+
test_utils.run_briefly(self.loop)
468+
self.assertFalse(cond.locked())
469+
470+
# Notify, but contest the lock before cancelling
471+
self.loop.run_until_complete(cond.acquire())
472+
self.assertTrue(cond.locked())
473+
cond.notify()
474+
self.loop.call_soon(wait_task.cancel)
475+
self.loop.call_soon(cond.release)
476+
477+
try:
478+
self.loop.run_until_complete(wait_task)
479+
except asyncio.CancelledError:
480+
# Should not happen, since no cancellation points
481+
pass
482+
483+
self.assertTrue(cond.locked())
484+
460485
def test_wait_unacquired(self):
461486
cond = asyncio.Condition(loop=self.loop)
462487
self.assertRaises(

0 commit comments

Comments
 (0)