diff options
| author | Guido van Rossum <guido@python.org> | 2023-10-28 18:04:29 (GMT) |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-28 18:04:29 (GMT) |
| commit | 26553695592ad399f735d4dbaf32fd871d0bb1e1 (patch) | |
| tree | 7f777a94e61f9cdc97a961f235019034f0411b3c /Lib/test/test_asyncio/test_server.py | |
| parent | 9d4a1a480b65196c3aabbcd2d165d1fb86d0c8e5 (diff) | |
| download | cpython-26553695592ad399f735d4dbaf32fd871d0bb1e1.zip cpython-26553695592ad399f735d4dbaf32fd871d0bb1e1.tar.gz cpython-26553695592ad399f735d4dbaf32fd871d0bb1e1.tar.bz2 | |
gh-79033: Try to fix asyncio.Server.wait_closed() again (GH-111336)
* Try to fix asyncio.Server.wait_closed() again
I identified the condition that `wait_closed()` is intended
to wait for: the server is closed *and* there are no more
active connections.
When this condition first becomes true, `_wakeup()` is called
(either from `close()` or from `_detach()`) and it sets `_waiters`
to `None`. So we just check for `self._waiters is None`; if it's
not `None`, we know we have to wait, and do so.
A problem was that the new test introduced in 3.12 explicitly
tested that `wait_closed()` returns immediately when the server
is *not* closed but there are currently no active connections.
This was a mistake (probably a misunderstanding of the intended
semantics). I've fixed the test, and added a separate test that
checks exactly for this scenario.
I also fixed an oddity where in `_wakeup()` the result of the
waiter was set to the waiter itself. This result is not used
anywhere and I changed this to `None`, to avoid a GC cycle.
* Update Lib/asyncio/base_events.py
---------
Co-authored-by: Carol Willing <carolcode@willingconsulting.com>
Diffstat (limited to 'Lib/test/test_asyncio/test_server.py')
| -rw-r--r-- | Lib/test/test_asyncio/test_server.py | 38 |
1 files changed, 34 insertions, 4 deletions
diff --git a/Lib/test/test_asyncio/test_server.py b/Lib/test/test_asyncio/test_server.py index 06d8b60..7ff3f55 100644 --- a/Lib/test/test_asyncio/test_server.py +++ b/Lib/test/test_asyncio/test_server.py @@ -122,29 +122,59 @@ class SelectorStartServerTests(BaseStartServer, unittest.TestCase): class TestServer2(unittest.IsolatedAsyncioTestCase): - async def test_wait_closed(self): + async def test_wait_closed_basic(self): async def serve(*args): pass srv = await asyncio.start_server(serve, socket_helper.HOSTv4, 0) + self.addCleanup(srv.close) - # active count = 0 + # active count = 0, not closed: should block task1 = asyncio.create_task(srv.wait_closed()) await asyncio.sleep(0) - self.assertTrue(task1.done()) + self.assertFalse(task1.done()) - # active count != 0 + # active count != 0, not closed: should block srv._attach() task2 = asyncio.create_task(srv.wait_closed()) await asyncio.sleep(0) + self.assertFalse(task1.done()) self.assertFalse(task2.done()) srv.close() await asyncio.sleep(0) + # active count != 0, closed: should block + task3 = asyncio.create_task(srv.wait_closed()) + await asyncio.sleep(0) + self.assertFalse(task1.done()) self.assertFalse(task2.done()) + self.assertFalse(task3.done()) srv._detach() + # active count == 0, closed: should unblock + await task1 await task2 + await task3 + await srv.wait_closed() # Return immediately + + async def test_wait_closed_race(self): + # Test a regression in 3.12.0, should be fixed in 3.12.1 + async def serve(*args): + pass + + srv = await asyncio.start_server(serve, socket_helper.HOSTv4, 0) + self.addCleanup(srv.close) + + task = asyncio.create_task(srv.wait_closed()) + await asyncio.sleep(0) + self.assertFalse(task.done()) + srv._attach() + loop = asyncio.get_running_loop() + loop.call_soon(srv.close) + loop.call_soon(srv._detach) + await srv.wait_closed() + + @unittest.skipUnless(hasattr(asyncio, 'ProactorEventLoop'), 'Windows only') |
