Skip to content

Commit 831306e

Browse files
kpumukcodex
andcommitted
Fix Python process-pool test server lifecycle
Client: py Co-Authored-By: OpenAI Codex (GPT-5.6) <codex@openai.com>
1 parent eac5f4f commit 831306e

2 files changed

Lines changed: 48 additions & 35 deletions

File tree

test/py/RunClientServer.py

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
FRAMED = ["TNonblockingServer"]
4848
SKIP_ZLIB = ['TNonblockingServer', 'THttpServer']
4949
SKIP_SSL = ['THttpServer']
50-
EXTRA_DELAY = dict(TProcessPoolServer=5.5)
5150

5251
PROTOS = [
5352
'accel',
@@ -78,6 +77,49 @@ def relfile(fname):
7877
return os.path.join(SCRIPT_DIR, fname)
7978

8079

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+
81123
def setup_pypath(libdir, gendir):
82124
dirs = [libdir, gendir]
83125
env = copy.deepcopy(os.environ)
@@ -182,22 +224,7 @@ def ensureServerAlive():
182224
ensureServerAlive()
183225
except Exception as exc:
184226
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)
201228
try:
202229
os.unlink(port_file)
203230
except OSError:

test/py/TestServer.py

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -413,28 +413,14 @@ def main(options):
413413
if server_type == "TNonblockingServer":
414414
server = TNonblockingServer.TNonblockingServer(processor, transport, inputProtocolFactory=pfactory)
415415
elif server_type == "TProcessPoolServer":
416-
import signal
417416
from thrift.server import TProcessPoolServer
418417
server = TProcessPoolServer.TProcessPoolServer(processor, transport, tfactory, pfactory)
419418
server.setNumWorkers(5)
420419

421-
def set_alarm():
422-
def clean_shutdown(signum, frame):
423-
for worker in server.workers:
424-
if options.verbose > 0:
425-
logging.info('Terminating worker: %s' % worker)
426-
worker.terminate()
427-
if options.verbose > 0:
428-
logging.info('Shutting down server')
429-
# server.stop() would deadlock here: it calls Condition.notify()
430-
# reentrantly on the same thread that's blocked in serve()'s
431-
# Condition.wait(), which can never post the wake ack notify()
432-
# is waiting for (THRIFT-6082). Workers are already terminated,
433-
# so just exit directly.
434-
os._exit(0)
435-
signal.signal(signal.SIGALRM, clean_shutdown)
436-
signal.alarm(4)
437-
set_alarm()
420+
def stop_process_pool(signum, frame):
421+
raise SystemExit
422+
423+
signal.signal(signal.SIGTERM, stop_process_pool)
438424
else:
439425
# look up server class dynamically to instantiate server
440426
ServerClass = getattr(TServer, server_type)

0 commit comments

Comments
 (0)