From e61090424ebb2e7deca904ad6955277ee17022fe Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 21 Dec 2018 00:19:44 -0800 Subject: [PATCH 1/6] ctrl-c test: remove auxiliary file, use new process group --- .../test_ctrl_c_in_proactor_loop_helper.py | 63 ------------------- Lib/test/test_asyncio/test_windows_events.py | 61 +++++++++++++++--- 2 files changed, 51 insertions(+), 73 deletions(-) delete mode 100644 Lib/test/test_asyncio/test_ctrl_c_in_proactor_loop_helper.py diff --git a/Lib/test/test_asyncio/test_ctrl_c_in_proactor_loop_helper.py b/Lib/test/test_asyncio/test_ctrl_c_in_proactor_loop_helper.py deleted file mode 100644 index 9aeb58aae21280..00000000000000 --- a/Lib/test/test_asyncio/test_ctrl_c_in_proactor_loop_helper.py +++ /dev/null @@ -1,63 +0,0 @@ -import sys - - -def do_in_child_process(): - import asyncio - - asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) - l = asyncio.get_event_loop() - - def step(n): - try: - print(n) - sys.stdout.flush() - l.run_forever() - sys.exit(100) - except KeyboardInterrupt: - # ok - pass - except: - # error - use default exit code - sys.exit(200) - - step(1) - step(2) - sys.exit(255) - - -def do_in_main_process(): - import os - import signal - import subprocess - import time - from test.support.script_helper import spawn_python - - ok = False - - def step(p, expected): - s = p.stdout.readline() - if s != expected: - raise Exception(f"Unexpected line: got {s}, expected '{expected}'") - # ensure that child process gets to run_forever - time.sleep(0.5) - os.kill(p.pid, signal.CTRL_C_EVENT) - - with spawn_python(__file__, "--child") as p: - try: - # ignore ctrl-c in current process - signal.signal(signal.SIGINT, signal.SIG_IGN) - step(p, b"1\r\n") - step(p, b"2\r\n") - exit_code = p.wait(timeout=5) - ok = exit_code = 255 - except Exception as e: - sys.stderr.write(repr(e)) - p.kill() - sys.exit(255 if ok else 1) - - -if __name__ == "__main__": - if len(sys.argv) == 1: - do_in_main_process() - else: - do_in_child_process() diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 05d875ac3c8523..aab0272224c7ef 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -39,19 +39,60 @@ def data_received(self, data): class ProactorLoopCtrlC(test_utils.TestCase): def test_ctrl_c(self): - from .test_ctrl_c_in_proactor_loop_helper import __file__ as f - - # ctrl-c will be sent to all processes that share the same console - # in order to isolate the effect of raising ctrl-c we'll create - # a process with a new console - flags = subprocess.CREATE_NEW_CONSOLE - with spawn_python(f, creationflags=flags) as p: + code = """if 1: + import asyncio + import ctypes + import sys + from ctypes import wintypes + + # child process is created with CREATE_NEW_PROCESS_GROUP flag + # an implicit call to SetConsoleCtrlHandler(NULL,TRUE) will be made + # on behalf of new process which will disable CTRL-C in it + # call SetConsoleCtrlHandler(NULL, False) to restore normal processing + SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler + SetConsoleCtrlHandler.argtypes = (ctypes.POINTER(ctypes.c_voidp), wintypes.BOOL) + SetConsoleCtrlHandler.restype = wintypes.BOOL + SetConsoleCtrlHandler(None, False) + + asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) + l = asyncio.get_event_loop() + + def step(n): try: - exit_code = p.wait(timeout=5) - self.assertEqual(exit_code, 255) + print(n) + sys.stdout.flush() + l.run_forever() + sys.exit(100) + except KeyboardInterrupt: + # ok + pass except: + sys.exit(200) + + step(1) + step(2) + sys.exit(255) + """ + def step(p, expected): + s = p.stdout.readline() + self.assertEqual(s, expected) + # ensure that child process gets to run_forever + time.sleep(0.5) + os.kill(p.pid, signal.CTRL_C_EVENT) + + exit_code = None + flags = subprocess.CREATE_NEW_PROCESS_GROUP + with spawn_python("-c", code, creationflags=flags) as p: + try: + step(p, b"1\r\n") + step(p, b"2\r\n") + exit_code = p.wait(timeout=5) + except Exception as e: p.kill() - raise + p.wait() + stdout, stderr = p.communicate() + self.fail(f"{stdout: stdout}\nstderr:{stderr}") + self.assertEqual(exit_code, 255) class ProactorTests(test_utils.TestCase): From bf7836898521141f6cfd258ca95537c5f9f9389f Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 21 Dec 2018 00:28:52 -0800 Subject: [PATCH 2/6] formatting --- Lib/test/test_asyncio/test_windows_events.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index aab0272224c7ef..879fd3329eed34 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -44,7 +44,7 @@ def test_ctrl_c(self): import ctypes import sys from ctypes import wintypes - + # child process is created with CREATE_NEW_PROCESS_GROUP flag # an implicit call to SetConsoleCtrlHandler(NULL,TRUE) will be made # on behalf of new process which will disable CTRL-C in it @@ -79,7 +79,7 @@ def step(p, expected): # ensure that child process gets to run_forever time.sleep(0.5) os.kill(p.pid, signal.CTRL_C_EVENT) - + exit_code = None flags = subprocess.CREATE_NEW_PROCESS_GROUP with spawn_python("-c", code, creationflags=flags) as p: From 63e31ca3f884652944c0892ed361a2b93a340b4b Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 24 Dec 2018 06:30:16 -0800 Subject: [PATCH 3/6] switch ctrl-c test to use 'raise'. This uncovered an issue with missing unregistration that was also fixed. --- Lib/asyncio/windows_events.py | 5 ++ Lib/test/test_asyncio/test_windows_events.py | 69 +++++--------------- 2 files changed, 23 insertions(+), 51 deletions(-) diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py index 33ffaf97177069..0f3e9f425f68eb 100644 --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -315,7 +315,12 @@ def run_forever(self): super().run_forever() finally: if self._self_reading_future is not None: + ov = self._self_reading_future._ov self._self_reading_future.cancel() + # self_reading_future was just cancelled so it will never be signalled + # Unregister it otherwise IocpProactor.close will wait for it forever + if ov is not None: + self._proactor._unregister(ov) self._self_reading_future = None async def create_pipe_connection(self, protocol_factory, address): diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 879fd3329eed34..ecc2784a289de4 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -4,6 +4,7 @@ import sys import subprocess import time +import threading import unittest from unittest import mock @@ -11,6 +12,7 @@ raise unittest.SkipTest('Windows only') import _overlapped +import _testcapi import _winapi import asyncio @@ -38,61 +40,26 @@ def data_received(self, data): class ProactorLoopCtrlC(test_utils.TestCase): + def test_ctrl_c(self): - code = """if 1: - import asyncio - import ctypes - import sys - from ctypes import wintypes - - # child process is created with CREATE_NEW_PROCESS_GROUP flag - # an implicit call to SetConsoleCtrlHandler(NULL,TRUE) will be made - # on behalf of new process which will disable CTRL-C in it - # call SetConsoleCtrlHandler(NULL, False) to restore normal processing - SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler - SetConsoleCtrlHandler.argtypes = (ctypes.POINTER(ctypes.c_voidp), wintypes.BOOL) - SetConsoleCtrlHandler.restype = wintypes.BOOL - SetConsoleCtrlHandler(None, False) + + def SIGINT_after_delay(): + time.sleep(1) + _testcapi.raise_signal(signal.SIGINT) asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) l = asyncio.get_event_loop() - - def step(n): - try: - print(n) - sys.stdout.flush() - l.run_forever() - sys.exit(100) - except KeyboardInterrupt: - # ok - pass - except: - sys.exit(200) - - step(1) - step(2) - sys.exit(255) - """ - def step(p, expected): - s = p.stdout.readline() - self.assertEqual(s, expected) - # ensure that child process gets to run_forever - time.sleep(0.5) - os.kill(p.pid, signal.CTRL_C_EVENT) - - exit_code = None - flags = subprocess.CREATE_NEW_PROCESS_GROUP - with spawn_python("-c", code, creationflags=flags) as p: - try: - step(p, b"1\r\n") - step(p, b"2\r\n") - exit_code = p.wait(timeout=5) - except Exception as e: - p.kill() - p.wait() - stdout, stderr = p.communicate() - self.fail(f"{stdout: stdout}\nstderr:{stderr}") - self.assertEqual(exit_code, 255) + try: + t = threading.Thread(target=SIGINT_after_delay, daemon=False) + t.start() + l.run_forever() + self.fail("should not fall though 'run_forever'") + except KeyboardInterrupt: + pass + except BaseException as e: + self.fail(repr(e)) + finally: + l.close() class ProactorTests(test_utils.TestCase): From 40b8f9cd643a0caaafbb2cc0549f3f9c52490744 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Sun, 23 Dec 2018 22:31:26 -0800 Subject: [PATCH 4/6] formatting --- Lib/test/test_asyncio/test_windows_events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index ecc2784a289de4..8116784c6d0a79 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -40,7 +40,7 @@ def data_received(self, data): class ProactorLoopCtrlC(test_utils.TestCase): - + def test_ctrl_c(self): def SIGINT_after_delay(): From 0af4b2906ad77138bc8f8950b36f1746ea18aed2 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Sun, 23 Dec 2018 22:44:04 -0800 Subject: [PATCH 5/6] fix typo in message --- Lib/test/test_asyncio/test_windows_events.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 8116784c6d0a79..8df26513da22a7 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -50,10 +50,10 @@ def SIGINT_after_delay(): asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) l = asyncio.get_event_loop() try: - t = threading.Thread(target=SIGINT_after_delay, daemon=False) + t = threading.Thread(target=SIGINT_after_delay) t.start() l.run_forever() - self.fail("should not fall though 'run_forever'") + self.fail("should not fall through 'run_forever'") except KeyboardInterrupt: pass except BaseException as e: From 284fafb6188af51408219aefaf51839ab3e1e8a7 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 24 Dec 2018 11:19:57 -0800 Subject: [PATCH 6/6] Drop redundant except --- Lib/test/test_asyncio/test_windows_events.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 8df26513da22a7..a200a8a80ad9fe 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -56,8 +56,6 @@ def SIGINT_after_delay(): self.fail("should not fall through 'run_forever'") except KeyboardInterrupt: pass - except BaseException as e: - self.fail(repr(e)) finally: l.close()