diff options
author | Joongi Kim <joongi@lablup.com> | 2021-10-10 16:01:41 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-10 16:01:41 (GMT) |
commit | 1a7892414e654aa5c99efa31db767baba7f4a424 (patch) | |
tree | 50de7bd25b262b2fe44f1028bd8da17dcb9d03c6 /Lib | |
parent | 62a667784ba7b84611ebd50fa8a1a464cde32235 (diff) | |
download | cpython-1a7892414e654aa5c99efa31db767baba7f4a424.zip cpython-1a7892414e654aa5c99efa31db767baba7f4a424.tar.gz cpython-1a7892414e654aa5c99efa31db767baba7f4a424.tar.bz2 |
bpo-45416: Fix use of asyncio.Condition() with explicit Lock objects (GH-28850)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/asyncio/locks.py | 2 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_locks.py | 70 |
2 files changed, 57 insertions, 15 deletions
diff --git a/Lib/asyncio/locks.py b/Lib/asyncio/locks.py index a7453fb..4fef64e 100644 --- a/Lib/asyncio/locks.py +++ b/Lib/asyncio/locks.py @@ -230,8 +230,6 @@ class Condition(_ContextManagerMixin, mixins._LoopBoundMixin): super().__init__(loop=loop) if lock is None: lock = Lock() - elif lock._loop is not self._get_loop(): - raise ValueError("loop argument must agree with lock") self._lock = lock # Export the lock's locked(), acquire() and release() methods. diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py index 441adee..b2492c1 100644 --- a/Lib/test/test_asyncio/test_locks.py +++ b/Lib/test/test_asyncio/test_locks.py @@ -720,24 +720,68 @@ class ConditionTests(test_utils.TestCase): self.loop.run_until_complete(f()) def test_explicit_lock(self): - lock = asyncio.Lock() - cond = asyncio.Condition(lock) + async def f(lock=None, cond=None): + if lock is None: + lock = asyncio.Lock() + if cond is None: + cond = asyncio.Condition(lock) + self.assertIs(cond._lock, lock) + self.assertFalse(lock.locked()) + self.assertFalse(cond.locked()) + async with cond: + self.assertTrue(lock.locked()) + self.assertTrue(cond.locked()) + self.assertFalse(lock.locked()) + self.assertFalse(cond.locked()) + async with lock: + self.assertTrue(lock.locked()) + self.assertTrue(cond.locked()) + self.assertFalse(lock.locked()) + self.assertFalse(cond.locked()) - self.assertIs(cond._lock, lock) - self.assertIs(cond._loop, lock._loop) + # All should work in the same way. + self.loop.run_until_complete(f()) + self.loop.run_until_complete(f(asyncio.Lock())) + lock = asyncio.Lock() + self.loop.run_until_complete(f(lock, asyncio.Condition(lock))) def test_ambiguous_loops(self): - loop = self.new_test_loop() + loop = asyncio.new_event_loop() self.addCleanup(loop.close) - lock = asyncio.Lock() - lock._loop = loop - - async def _create_condition(): - with self.assertRaises(ValueError): - asyncio.Condition(lock) - - self.loop.run_until_complete(_create_condition()) + async def wrong_loop_in_lock(): + with self.assertRaises(TypeError): + asyncio.Lock(loop=loop) # actively disallowed since 3.10 + lock = asyncio.Lock() + lock._loop = loop # use private API for testing + async with lock: + # acquired immediately via the fast-path + # without interaction with any event loop. + cond = asyncio.Condition(lock) + # cond.acquire() will trigger waiting on the lock + # and it will discover the event loop mismatch. + with self.assertRaisesRegex( + RuntimeError, + "is bound to a different event loop", + ): + await cond.acquire() + + async def wrong_loop_in_cond(): + # Same analogy here with the condition's loop. + lock = asyncio.Lock() + async with lock: + with self.assertRaises(TypeError): + asyncio.Condition(lock, loop=loop) + cond = asyncio.Condition(lock) + cond._loop = loop + with self.assertRaisesRegex( + RuntimeError, + "is bound to a different event loop", + ): + await cond.wait() + + self.loop.run_until_complete(wrong_loop_in_lock()) + self.loop.run_until_complete(wrong_loop_in_cond()) def test_timeout_in_block(self): loop = asyncio.new_event_loop() |