|
3 | 3 | import json |
4 | 4 | import os |
5 | 5 | import queue |
| 6 | +import signal |
6 | 7 | import subprocess |
7 | 8 | import sys |
8 | 9 | import threading |
|
31 | 32 | # Time to wait until a worker completes: should be immediate |
32 | 33 | JOIN_TIMEOUT = 30.0 # seconds |
33 | 34 |
|
| 35 | +USE_PROCESS_GROUP = (hasattr(os, "setsid") and hasattr(os, "killpg")) |
| 36 | + |
34 | 37 |
|
35 | 38 | def must_stop(result, ns): |
36 | 39 | if result.result == INTERRUPTED: |
@@ -59,12 +62,16 @@ def run_test_in_subprocess(testname, ns): |
59 | 62 | # Running the child from the same working directory as regrtest's original |
60 | 63 | # invocation ensures that TEMPDIR for the child is the same when |
61 | 64 | # sysconfig.is_python_build() is true. See issue 15300. |
| 65 | + kw = {} |
| 66 | + if USE_PROCESS_GROUP: |
| 67 | + kw['start_new_session'] = True |
62 | 68 | return subprocess.Popen(cmd, |
63 | 69 | stdout=subprocess.PIPE, |
64 | 70 | stderr=subprocess.PIPE, |
65 | 71 | universal_newlines=True, |
66 | 72 | close_fds=(os.name != 'nt'), |
67 | | - cwd=support.SAVEDCWD) |
| 73 | + cwd=support.SAVEDCWD, |
| 74 | + **kw) |
68 | 75 |
|
69 | 76 |
|
70 | 77 | def run_tests_worker(ns, test_name): |
@@ -149,16 +156,24 @@ def _kill(self): |
149 | 156 | return |
150 | 157 | self._killed = True |
151 | 158 |
|
152 | | - print(f"Kill {self}", file=sys.stderr, flush=True) |
| 159 | + if USE_PROCESS_GROUP: |
| 160 | + what = f"{self} process group" |
| 161 | + else: |
| 162 | + what = f"{self}" |
| 163 | + |
| 164 | + print(f"Kill {what}", file=sys.stderr, flush=True) |
153 | 165 | try: |
154 | | - popen.kill() |
| 166 | + if USE_PROCESS_GROUP: |
| 167 | + os.killpg(popen.pid, signal.SIGKILL) |
| 168 | + else: |
| 169 | + popen.kill() |
155 | 170 | except ProcessLookupError: |
156 | | - # Process completed, the TestWorkerProcess thread read its exit |
157 | | - # status, but Popen.send_signal() read the returncode just before |
158 | | - # Popen.wait() set returncode. |
| 171 | + # popen.kill(): the process completed, the TestWorkerProcess thread |
| 172 | + # read its exit status, but Popen.send_signal() read the returncode |
| 173 | + # just before Popen.wait() set returncode. |
159 | 174 | pass |
160 | 175 | except OSError as exc: |
161 | | - print_warning(f"Failed to kill {self}: {exc!r}") |
| 176 | + print_warning(f"Failed to kill {what}: {exc!r}") |
162 | 177 |
|
163 | 178 | def stop(self): |
164 | 179 | # Method called from a different thread to stop this thread |
|
0 commit comments