mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-06 17:38:35 +00:00
fix(shell): drop gateway-wide waitpid(-1) zombie reaper
A global reaper can race asyncio's child watcher for just-exited create_subprocess_exec children, causing Process returncode 255 instead of the real command status. Keep only owned-PID reaps after wait/communicate /kill paths in shell and exec_session. Addresses review feedback on #4840.
This commit is contained in:
parent
bda0c099ab
commit
a1fbfd9f7b
@ -201,7 +201,6 @@ app = typer.Typer(
|
|||||||
)
|
)
|
||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
_IS_WINDOWS = sys.platform == "win32"
|
|
||||||
EXIT_COMMANDS = {"exit", "quit", "/exit", "/quit", ":q"}
|
EXIT_COMMANDS = {"exit", "quit", "/exit", "/quit", ":q"}
|
||||||
_REASONING_SENTENCE_ENDINGS = (".", "!", "?", "。", "!", "?")
|
_REASONING_SENTENCE_ENDINGS = (".", "!", "?", "。", "!", "?")
|
||||||
_REASONING_FLUSH_CHARS = 60
|
_REASONING_FLUSH_CHARS = 60
|
||||||
@ -1696,29 +1695,6 @@ def _run_gateway(
|
|||||||
else:
|
else:
|
||||||
console.print("[yellow]✗[/yellow] Heartbeat: disabled")
|
console.print("[yellow]✗[/yellow] Heartbeat: disabled")
|
||||||
|
|
||||||
async def _zombie_reaper() -> None:
|
|
||||||
"""Periodically reap zombie child processes.
|
|
||||||
|
|
||||||
asyncio's child-watcher *should* reap all children, but inside
|
|
||||||
Docker containers the pidfd / SIGCHLD mechanism can miss exits.
|
|
||||||
This task runs every 30 s and calls ``os.waitpid(-1, WNOHANG)``
|
|
||||||
in a loop to collect any zombies that slipped through.
|
|
||||||
"""
|
|
||||||
_INTERVAL = 30
|
|
||||||
while True:
|
|
||||||
await asyncio.sleep(_INTERVAL)
|
|
||||||
reaped = 0
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
pid, _ = os.waitpid(-1, os.WNOHANG)
|
|
||||||
if pid == 0:
|
|
||||||
break # no more zombie children
|
|
||||||
reaped += 1
|
|
||||||
except ChildProcessError:
|
|
||||||
break # no child processes at all
|
|
||||||
if reaped:
|
|
||||||
logger.info("Zombie reaper: reaped {} defunct child process(es)", reaped)
|
|
||||||
|
|
||||||
async def _health_server(host: str, health_port: int):
|
async def _health_server(host: str, health_port: int):
|
||||||
"""Lightweight HTTP health endpoint on the gateway port."""
|
"""Lightweight HTTP health endpoint on the gateway port."""
|
||||||
import json as _json
|
import json as _json
|
||||||
@ -1844,11 +1820,6 @@ def _run_gateway(
|
|||||||
name="nanobot-local-triggers",
|
name="nanobot-local-triggers",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
if not _IS_WINDOWS:
|
|
||||||
tasks.append(asyncio.create_task(
|
|
||||||
_zombie_reaper(),
|
|
||||||
name="nanobot-zombie-reaper",
|
|
||||||
))
|
|
||||||
if health_server_enabled:
|
if health_server_enabled:
|
||||||
tasks.append(asyncio.create_task(
|
tasks.append(asyncio.create_task(
|
||||||
_health_server(config.gateway.host, port),
|
_health_server(config.gateway.host, port),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user