Skip to content
Merged
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 Lib/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
22 changes: 22 additions & 0 deletions Lib/test/test_asyncio/test_locks.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,28 @@ 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())

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

Expand Down