|
47 | 47 | FRAMED = ["TNonblockingServer"] |
48 | 48 | SKIP_ZLIB = ['TNonblockingServer', 'THttpServer'] |
49 | 49 | SKIP_SSL = ['THttpServer'] |
50 | | -EXTRA_DELAY = dict(TProcessPoolServer=5.5) |
51 | 50 |
|
52 | 51 | PROTOS = [ |
53 | 52 | 'accel', |
@@ -78,6 +77,49 @@ def relfile(fname): |
78 | 77 | return os.path.join(SCRIPT_DIR, fname) |
79 | 78 |
|
80 | 79 |
|
| 80 | +def terminate_process_group(process, timeout=5): |
| 81 | + """Terminate a test server, killing it if graceful shutdown times out.""" |
| 82 | + if platform.system() == 'Windows': |
| 83 | + if process.poll() is not None: |
| 84 | + return |
| 85 | + process.terminate() |
| 86 | + try: |
| 87 | + process.wait(timeout=timeout) |
| 88 | + except subprocess.TimeoutExpired: |
| 89 | + process.kill() |
| 90 | + process.wait() |
| 91 | + return |
| 92 | + |
| 93 | + process_group = process.pid |
| 94 | + try: |
| 95 | + os.killpg(process_group, signal.SIGTERM) |
| 96 | + except ProcessLookupError: |
| 97 | + process.wait() |
| 98 | + return |
| 99 | + |
| 100 | + deadline = time.monotonic() + timeout |
| 101 | + while time.monotonic() < deadline: |
| 102 | + process.poll() |
| 103 | + try: |
| 104 | + os.killpg(process_group, 0) |
| 105 | + except ProcessLookupError: |
| 106 | + process.wait() |
| 107 | + return |
| 108 | + except PermissionError: |
| 109 | + # macOS can report EPERM briefly while group members are exiting. |
| 110 | + pass |
| 111 | + time.sleep(0.01) |
| 112 | + |
| 113 | + try: |
| 114 | + os.killpg(process_group, signal.SIGKILL) |
| 115 | + except ProcessLookupError: |
| 116 | + pass |
| 117 | + except PermissionError: |
| 118 | + if process.poll() is None: |
| 119 | + process.kill() |
| 120 | + process.wait() |
| 121 | + |
| 122 | + |
81 | 123 | def setup_pypath(libdir, gendir): |
82 | 124 | dirs = [libdir, gendir] |
83 | 125 | env = copy.deepcopy(os.environ) |
@@ -182,22 +224,7 @@ def ensureServerAlive(): |
182 | 224 | ensureServerAlive() |
183 | 225 | except Exception as exc: |
184 | 226 | cleanup_exc = exc |
185 | | - extra_sleep = EXTRA_DELAY.get(server_class, 0) |
186 | | - if extra_sleep > 0 and verbose > 0: |
187 | | - print('Giving %s (proto=%s,zlib=%s,ssl=%s) an extra %d seconds for child' |
188 | | - 'processes to terminate via alarm' |
189 | | - % (server_class, proto, use_zlib, use_ssl, extra_sleep)) |
190 | | - time.sleep(extra_sleep) |
191 | | - sig = signal.SIGKILL if platform.system() != 'Windows' else signal.SIGABRT |
192 | | - try: |
193 | | - if platform.system() == 'Windows': |
194 | | - os.kill(serverproc.pid, sig) |
195 | | - else: |
196 | | - # POSIX: kill the whole process group to reap forked children. |
197 | | - os.killpg(serverproc.pid, sig) |
198 | | - except OSError: |
199 | | - pass |
200 | | - serverproc.wait() |
| 227 | + terminate_process_group(serverproc) |
201 | 228 | try: |
202 | 229 | os.unlink(port_file) |
203 | 230 | except OSError: |
|
0 commit comments