Skip to content

Commit 164dddf

Browse files
bpo-45416: Fix use of asyncio.Condition() with explicit Lock objects (GH-28850)
Co-authored-by: Serhiy Storchaka <[email protected]> (cherry picked from commit 1a78924) Co-authored-by: Joongi Kim <[email protected]>
1 parent 3c27013 commit 164dddf

File tree

3 files changed

+57
-13
lines changed

3 files changed

+57
-13
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: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -724,24 +724,68 @@ async def f():
724724
self.loop.run_until_complete(f())
725725

726726
def test_explicit_lock(self):
727-
lock = asyncio.Lock()
728-
cond = asyncio.Condition(lock)
727+
async def f(lock=None, cond=None):
728+
if lock is None:
729+
lock = asyncio.Lock()
730+
if cond is None:
731+
cond = asyncio.Condition(lock)
732+
self.assertIs(cond._lock, lock)
733+
self.assertFalse(lock.locked())
734+
self.assertFalse(cond.locked())
735+
async with cond:
736+
self.assertTrue(lock.locked())
737+
self.assertTrue(cond.locked())
738+
self.assertFalse(lock.locked())
739+
self.assertFalse(cond.locked())
740+
async with lock:
741+
self.assertTrue(lock.locked())
742+
self.assertTrue(cond.locked())
743+
self.assertFalse(lock.locked())
744+
self.assertFalse(cond.locked())
729745

730-
self.assertIs(cond._lock, lock)
731-
self.assertIs(cond._loop, lock._loop)
746+
# All should work in the same way.
747+
self.loop.run_until_complete(f())
748+
self.loop.run_until_complete(f(asyncio.Lock()))
749+
lock = asyncio.Lock()
750+
self.loop.run_until_complete(f(lock, asyncio.Condition(lock)))
732751

733752
def test_ambiguous_loops(self):
734-
loop = self.new_test_loop()
753+
loop = asyncio.new_event_loop()
735754
self.addCleanup(loop.close)
736755

737-
lock = asyncio.Lock()
738-
lock._loop = loop
756+
async def wrong_loop_in_lock():
757+
with self.assertRaises(TypeError):
758+
asyncio.Lock(loop=loop) # actively disallowed since 3.10
759+
lock = asyncio.Lock()
760+
lock._loop = loop # use private API for testing
761+
async with lock:
762+
# acquired immediately via the fast-path
763+
# without interaction with any event loop.
764+
cond = asyncio.Condition(lock)
765+
# cond.acquire() will trigger waiting on the lock
766+
# and it will discover the event loop mismatch.
767+
with self.assertRaisesRegex(
768+
RuntimeError,
769+
"is bound to a different event loop",
770+
):
771+
await cond.acquire()
739772

740-
async def _create_condition():
741-
with self.assertRaises(ValueError):
742-
asyncio.Condition(lock)
773+
async def wrong_loop_in_cond():
774+
# Same analogy here with the condition's loop.
775+
lock = asyncio.Lock()
776+
async with lock:
777+
with self.assertRaises(TypeError):
778+
asyncio.Condition(lock, loop=loop)
779+
cond = asyncio.Condition(lock)
780+
cond._loop = loop
781+
with self.assertRaisesRegex(
782+
RuntimeError,
783+
"is bound to a different event loop",
784+
):
785+
await cond.wait()
743786

744-
self.loop.run_until_complete(_create_condition())
787+
self.loop.run_until_complete(wrong_loop_in_lock())
788+
self.loop.run_until_complete(wrong_loop_in_cond())
745789

746790
def test_timeout_in_block(self):
747791
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)