Fix --reload port conflict when using explicit port (#3070)

* Fix --reload port conflict by killing entire process group

* Gate start_new_session on Unix (no-op on Windows)
This commit is contained in:
Jeremiah Lowin 2026-02-04 17:35:39 -05:00 committed by GitHub
commit 422384e576
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -3,6 +3,7 @@
import asyncio
import contextlib
import json
import os
import re
import signal
import sys
@ -288,10 +289,34 @@ def _watch_filter(_change: Change, path: str) -> bool:
async def _terminate_process(process: asyncio.subprocess.Process) -> None:
"""Terminate a subprocess immediately."""
"""Terminate a subprocess and all its children.
Sends SIGTERM to the process group first for graceful shutdown,
then falls back to SIGKILL if the process doesn't exit in time.
"""
if process.returncode is not None:
return
process.kill()
pid = process.pid
if sys.platform != "win32":
# Send SIGTERM to the entire process group for graceful shutdown
with contextlib.suppress(ProcessLookupError, OSError):
os.killpg(os.getpgid(pid), signal.SIGTERM)
# Wait briefly for graceful exit
try:
await asyncio.wait_for(process.wait(), timeout=3.0)
return
except asyncio.TimeoutError:
pass
# Force kill the entire process group
with contextlib.suppress(ProcessLookupError, OSError):
os.killpg(os.getpgid(pid), signal.SIGKILL)
else:
process.kill()
await process.wait()
@ -347,6 +372,8 @@ async def run_with_reload(
stdin=None,
stdout=None,
stderr=None,
# Own process group so _terminate_process can kill the whole tree
start_new_session=sys.platform != "win32",
)
# Watch for either: file changes OR process death