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
3 changes: 3 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ Extension Modules
Library
-------

- bpo-27585: Fix waiter cancellation in asyncio.Lock.
Patch by Mathieu Sornay.

- bpo-30418: On Windows, subprocess.Popen.communicate() now also ignore EINVAL
on stdin.write() if the child process is still running but closed the pipe.

Expand Down