From d20c40ca8a8f92b9f515ccbfbf25d3cb98c9b8f9 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 11 Jan 2023 14:51:48 -0800 Subject: [PATCH 1/6] Fix server hang caused by os.stat() on named pipe (Windows) --- Lib/asyncio/windows_events.py | 5 +++ Lib/test/test_asyncio/test_windows_events.py | 32 ++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py index 4dad436fb4187f..c9a5fb841cb134 100644 --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -366,6 +366,10 @@ def loop_accept_pipe(f=None): return f = self._proactor.accept_pipe(pipe) + except BrokenPipeError: + if pipe and pipe.fileno() != -1: + pipe.close() + self.call_soon(loop_accept_pipe) except OSError as exc: if pipe and pipe.fileno() != -1: self.call_exception_handler({ @@ -377,6 +381,7 @@ def loop_accept_pipe(f=None): elif self._debug: logger.warning("Accept pipe failed on pipe %r", pipe, exc_info=True) + self.call_soon(loop_accept_pipe) except exceptions.CancelledError: if pipe: pipe.close() diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 5033acc052486b..e334e27ff3844c 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -250,6 +250,38 @@ def test_address_argument_type_error(self): proactor.sendto(sock, b'abc', addr=bad_address) sock.close() + def test_client_pipe_stat(self): + res = self.loop.run_until_complete(self._test_client_pipe_stat()) + self.assertEqual(res, 'done') + + async def _test_client_pipe_stat(self): + ADDRESS = r'\\.\pipe\test_client_pipe_stat-%s' % os.getpid() + + with self.assertRaises(FileNotFoundError): + os.stat(ADDRESS) + + [server] = await self.loop.start_serving_pipe( + asyncio.Protocol, ADDRESS) + self.assertIsInstance(server, windows_events.PipeServer) + + errors = [] + self.loop.set_exception_handler(lambda *args: errors.append(args)) + + async def stat_it(): + os.stat(ADDRESS) + + for i in range(5): + await self.loop.create_task(stat_it()) + + self.assertEqual(len(errors), 0, errors) + + server.close() + + with self.assertRaises(FileNotFoundError): + os.stat(ADDRESS) + + return "done" + class WinPolicyTests(test_utils.TestCase): From 4eddc01e841b2f5d6069deab0dc6c35e21cb9312 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 12 Jan 2023 01:18:17 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst diff --git a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst new file mode 100644 index 00000000000000..7c3e51e48cbbf7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst @@ -0,0 +1 @@ +Fix a Windows asyncio bug with named pipes where a client doing `os.stat()` on the pipe would cause an error in the server that disabled serving future requests. From 68a32821f0a7849e7b790d6493a050575705a204 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 11 Jan 2023 17:24:39 -0800 Subject: [PATCH 3/6] Fix backticks --- .../next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst index 7c3e51e48cbbf7..9308f77ccff0a6 100644 --- a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst +++ b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst @@ -1 +1 @@ -Fix a Windows asyncio bug with named pipes where a client doing `os.stat()` on the pipe would cause an error in the server that disabled serving future requests. +Fix a Windows asyncio bug with named pipes where a client doing ``os.stat()`` on the pipe would cause an error in the server that disabled serving future requests. From 0fde2aa54114651d6afd1226f2a7924273157372 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Thu, 12 Jan 2023 21:35:00 +0530 Subject: [PATCH 4/6] Update Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst --- .../next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst index 9308f77ccff0a6..97b95d18d1e426 100644 --- a/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst +++ b/Misc/NEWS.d/next/Library/2023-01-12-01-18-13.gh-issue-100573.KDskqo.rst @@ -1 +1 @@ -Fix a Windows asyncio bug with named pipes where a client doing ``os.stat()`` on the pipe would cause an error in the server that disabled serving future requests. +Fix a Windows :mod:`asyncio` bug with named pipes where a client doing ``os.stat()`` on the pipe would cause an error in the server that disabled serving future requests. From f4a9a9ae68d4dd9afab098523cf78166615adc7f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 13 Jan 2023 11:31:52 -0800 Subject: [PATCH 5/6] Fix test using Eryk Sun's suggestion --- Lib/test/test_asyncio/test_windows_events.py | 25 +++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index e334e27ff3844c..aa08f69c58dd43 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -257,28 +257,35 @@ def test_client_pipe_stat(self): async def _test_client_pipe_stat(self): ADDRESS = r'\\.\pipe\test_client_pipe_stat-%s' % os.getpid() + async def probe(): + # See https://github.com/python/cpython/pull/100959#discussion_r1068533658 + h = _overlapped.ConnectPipe(ADDRESS) + try: + _winapi.CloseHandle(_overlapped.ConnectPipe(ADDRESS)) + except OSError as e: + if e.winerror != _overlapped.ERROR_PIPE_BUSY: + raise + finally: + _winapi.CloseHandle(h) + with self.assertRaises(FileNotFoundError): - os.stat(ADDRESS) + await probe() - [server] = await self.loop.start_serving_pipe( - asyncio.Protocol, ADDRESS) + [server] = await self.loop.start_serving_pipe(asyncio.Protocol, ADDRESS) self.assertIsInstance(server, windows_events.PipeServer) errors = [] - self.loop.set_exception_handler(lambda *args: errors.append(args)) - - async def stat_it(): - os.stat(ADDRESS) + self.loop.set_exception_handler(lambda _, data: errors.append(data)) for i in range(5): - await self.loop.create_task(stat_it()) + await self.loop.create_task(probe()) self.assertEqual(len(errors), 0, errors) server.close() with self.assertRaises(FileNotFoundError): - os.stat(ADDRESS) + await probe() return "done" From 6bcd7dfd88d77007611f8ec582b24ec654d514af Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 13 Jan 2023 12:12:50 -0800 Subject: [PATCH 6/6] Add comment linking to issue --- Lib/test/test_asyncio/test_windows_events.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index aa08f69c58dd43..a36119a8004f9d 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -255,6 +255,7 @@ def test_client_pipe_stat(self): self.assertEqual(res, 'done') async def _test_client_pipe_stat(self): + # Regression test for https://github.com/python/cpython/issues/100573 ADDRESS = r'\\.\pipe\test_client_pipe_stat-%s' % os.getpid() async def probe():