Skip to content

Commit 1a78924

Browse files
bpo-45416: Fix use of asyncio.Condition() with explicit Lock objects (GH-28850)
Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 62a6677 commit 1a78924

File tree

3 files changed

+59
-15
lines changed

3 files changed

+59
-15
lines changed

Lib/asyncio/locks.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,6 @@ def __init__(self, lock=None, *, loop=mixins._marker):
230230
super().__init__(loop=loop)
231231
if lock is None:
232232
lock = Lock()
233-
elif lock._loop is not self._get_loop():
234-
raise ValueError("loop argument must agree with lock")
235233

236234
self._lock = lock
237235
# Export the lock's locked(), acquire() and release() methods.

Lib/test/test_asyncio/test_locks.py

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -720,24 +720,68 @@ async def f():
720720
self.loop.run_until_complete(f())
721721

722722
def test_explicit_lock(self):
723-
lock = asyncio.Lock()
724-
cond = asyncio.Condition(lock)
723+
async def f(lock=None, cond=None):
724+
if lock is None:
725+
lock = asyncio.Lock()
726+
if cond is None:
727+
cond = asyncio.Condition(lock)
728+
self.assertIs(cond._lock, lock)
729+
self.assertFalse(lock.locked())
730+
self.assertFalse(cond.locked())
731+
async with cond:
732+
self.assertTrue(lock.locked())
733+
self.assertTrue(cond.locked())
734+
self.assertFalse(lock.locked())
735+
self.assertFalse(cond.locked())
736+
async with lock:
737+
self.assertTrue(lock.locked())
738+
self.assertTrue(cond.locked())
739+
self.assertFalse(lock.locked())
740+
self.assertFalse(cond.locked())
725741

726-
self.assertIs(cond._lock, lock)
727-
self.assertIs(cond._loop, lock._loop)
742+
# All should work in the same way.
743+
self.loop.run_until_complete(f())
744+
self.loop.run_until_complete(f(asyncio.Lock()))
745+
lock = asyncio.Lock()
746+
self.loop.run_until_complete(f(lock, asyncio.Condition(lock)))
728747

729748
def test_ambiguous_loops(self):
730-
loop = self.new_test_loop()
749+
loop = asyncio.new_event_loop()
731750
self.addCleanup(loop.close)
732751

733-
lock = asyncio.Lock()
734-
lock._loop = loop
735-
736-
async def _create_condition():
737-
with self.assertRaises(ValueError):
738-
asyncio.Condition(lock)
739-
740-
self.loop.run_until_complete(_create_condition())
752+
async def wrong_loop_in_lock():
753+
with self.assertRaises(TypeError):
754+
asyncio.Lock(loop=loop) # actively disallowed since 3.10
755+
lock = asyncio.Lock()
756+
lock._loop = loop # use private API for testing
757+
async with lock:
758+
# acquired immediately via the fast-path
759+
# without interaction with any event loop.
760+
cond = asyncio.Condition(lock)
761+
# cond.acquire() will trigger waiting on the lock
762+
# and it will discover the event loop mismatch.
763+
with self.assertRaisesRegex(
764+
RuntimeError,
765+
"is bound to a different event loop",
766+
):
767+
await cond.acquire()
768+
769+
async def wrong_loop_in_cond():
770+
# Same analogy here with the condition's loop.
771+
lock = asyncio.Lock()
772+
async with lock:
773+
with self.assertRaises(TypeError):
774+
asyncio.Condition(lock, loop=loop)
775+
cond = asyncio.Condition(lock)
776+
cond._loop = loop
777+
with self.assertRaisesRegex(
778+
RuntimeError,
779+
"is bound to a different event loop",
780+
):
781+
await cond.wait()
782+
783+
self.loop.run_until_complete(wrong_loop_in_lock())
784+
self.loop.run_until_complete(wrong_loop_in_cond())
741785

742786
def test_timeout_in_block(self):
743787
loop = asyncio.new_event_loop()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix use of :class:`asyncio.Condition` with explicit :class:`asyncio.Lock` objects, which was a regression due to removal of explicit loop arguments.
2+
Patch by Joongi Kim.

0 commit comments

Comments
 (0)