Skip to content
This repository was archived by the owner on Nov 23, 2017. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions asyncio/locks.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ def acquire(self):
yield from fut
self._locked = True
return True
except futures.CancelledError:
if not self._locked:
self._wake_up_first()
raise
finally:
self._waiters.remove(fut)

Expand All @@ -192,14 +196,17 @@ def release(self):
"""
if self._locked:
self._locked = False
# Wake up the first waiter who isn't cancelled.
for fut in self._waiters:
if not fut.done():
fut.set_result(True)
break
self._wake_up_first()
else:
raise RuntimeError('Lock is not acquired.')

def _wake_up_first(self):
"""Wake up the first waiter who isn't cancelled."""
for fut in self._waiters:
if not fut.done():
fut.set_result(True)
break


class Event:
"""Asynchronous equivalent to threading.Event.
Expand Down
23 changes: 23 additions & 0 deletions tests/test_locks.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,29 @@ def lockit(name, blocker):
self.assertTrue(tb.cancelled())
self.assertTrue(tc.done())

def test_finished_waiter_cancelled(self):
lock = asyncio.Lock(loop=self.loop)

ta = asyncio.Task(lock.acquire(), loop=self.loop)
test_utils.run_briefly(self.loop)
self.assertTrue(lock.locked())

tb = asyncio.Task(lock.acquire(), loop=self.loop)
test_utils.run_briefly(self.loop)
self.assertEqual(len(lock._waiters), 1)

# Create a second waiter, wake up the first, and cancel it.
# Without the fix, the second was not woken up.
tc = asyncio.Task(lock.acquire(), loop=self.loop)
lock.release()
tb.cancel()
test_utils.run_briefly(self.loop)

self.assertTrue(lock.locked())
self.assertTrue(ta.done())
self.assertTrue(tb.cancelled())
self.assertTrue(tc.done())

def test_release_not_acquired(self):
lock = asyncio.Lock(loop=self.loop)

Expand Down