]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-88050: Fix asyncio subprocess to kill process cleanly when process is blocked...
authorKumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Wed, 5 Oct 2022 17:15:31 +0000 (22:45 +0530)
committerGitHub <noreply@github.com>
Wed, 5 Oct 2022 17:15:31 +0000 (10:15 -0700)
Lib/asyncio/base_subprocess.py
Lib/test/test_asyncio/test_subprocess.py
Misc/NEWS.d/next/Library/2022-07-08-08-39-35.gh-issue-88050.0aOC_m.rst [new file with mode: 0644]

index 14d505192288142616cb28e865dd53850df2d7d5..c2ca4a2792f666a5e84f91487ea79ba77de7f8ed 100644 (file)
@@ -215,14 +215,10 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
             # object. On Python 3.6, it is required to avoid a ResourceWarning.
             self._proc.returncode = returncode
         self._call(self._protocol.process_exited)
+        for p in self._pipes.values():
+            p.pipe.close()
         self._try_finish()
 
-        # wake up futures waiting for wait()
-        for waiter in self._exit_waiters:
-            if not waiter.cancelled():
-                waiter.set_result(returncode)
-        self._exit_waiters = None
-
     async def _wait(self):
         """Wait until the process exit and return the process return code.
 
@@ -247,6 +243,11 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
         try:
             self._protocol.connection_lost(exc)
         finally:
+            # wake up futures waiting for wait()
+            for waiter in self._exit_waiters:
+                if not waiter.cancelled():
+                    waiter.set_result(self._returncode)
+            self._exit_waiters = None
             self._loop = None
             self._proc = None
             self._protocol = None
index 961c463f8dc11b727a2381d022e8f79116470310..9bc60b9dc2ae2ee256ee03a4f2ffe231263ea502 100644 (file)
@@ -1,4 +1,5 @@
 import os
+import shutil
 import signal
 import sys
 import unittest
@@ -182,6 +183,30 @@ class SubprocessMixin:
         else:
             self.assertEqual(-signal.SIGKILL, returncode)
 
+    def test_kill_issue43884(self):
+        blocking_shell_command = f'{sys.executable} -c "import time; time.sleep(100000000)"'
+        creationflags = 0
+        if sys.platform == 'win32':
+            from subprocess import CREATE_NEW_PROCESS_GROUP
+            # On windows create a new process group so that killing process
+            # kills the process and all its children.
+            creationflags = CREATE_NEW_PROCESS_GROUP
+        proc = self.loop.run_until_complete(
+            asyncio.create_subprocess_shell(blocking_shell_command, stdout=asyncio.subprocess.PIPE,
+            creationflags=creationflags)
+        )
+        self.loop.run_until_complete(asyncio.sleep(1))
+        if sys.platform == 'win32':
+            proc.send_signal(signal.CTRL_BREAK_EVENT)
+        # On windows it is an alias of terminate which sets the return code
+        proc.kill()
+        returncode = self.loop.run_until_complete(proc.wait())
+        if sys.platform == 'win32':
+            self.assertIsInstance(returncode, int)
+            # expect 1 but sometimes get 0
+        else:
+            self.assertEqual(-signal.SIGKILL, returncode)
+
     def test_terminate(self):
         args = PROGRAM_BLOCKED
         proc = self.loop.run_until_complete(
diff --git a/Misc/NEWS.d/next/Library/2022-07-08-08-39-35.gh-issue-88050.0aOC_m.rst b/Misc/NEWS.d/next/Library/2022-07-08-08-39-35.gh-issue-88050.0aOC_m.rst
new file mode 100644 (file)
index 0000000..43c0765
--- /dev/null
@@ -0,0 +1 @@
+Fix :mod:`asyncio` subprocess transport to kill process cleanly when process is blocked and avoid ``RuntimeError`` when loop is closed. Patch by Kumar Aditya.