mirror of https://github.com/python/cpython
gh-88050: Fix asyncio subprocess to kill process cleanly when process is blocked (#32073)
This commit is contained in:
parent
0e72606dd4
commit
7015e13797
|
@ -215,14 +215,10 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
|
||||||
# object. On Python 3.6, it is required to avoid a ResourceWarning.
|
# object. On Python 3.6, it is required to avoid a ResourceWarning.
|
||||||
self._proc.returncode = returncode
|
self._proc.returncode = returncode
|
||||||
self._call(self._protocol.process_exited)
|
self._call(self._protocol.process_exited)
|
||||||
|
for p in self._pipes.values():
|
||||||
|
p.pipe.close()
|
||||||
self._try_finish()
|
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):
|
async def _wait(self):
|
||||||
"""Wait until the process exit and return the process return code.
|
"""Wait until the process exit and return the process return code.
|
||||||
|
|
||||||
|
@ -247,6 +243,11 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
|
||||||
try:
|
try:
|
||||||
self._protocol.connection_lost(exc)
|
self._protocol.connection_lost(exc)
|
||||||
finally:
|
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._loop = None
|
||||||
self._proc = None
|
self._proc = None
|
||||||
self._protocol = None
|
self._protocol = None
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
@ -182,6 +183,30 @@ class SubprocessMixin:
|
||||||
else:
|
else:
|
||||||
self.assertEqual(-signal.SIGKILL, returncode)
|
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):
|
def test_terminate(self):
|
||||||
args = PROGRAM_BLOCKED
|
args = PROGRAM_BLOCKED
|
||||||
proc = self.loop.run_until_complete(
|
proc = self.loop.run_until_complete(
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue