Repository navigation
Python: Kill the shell process group on stateless shell timeout - #9483
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Abandoned drains retain pipe descriptors, and zombie-only groups unnecessarily consume the full grace period.
2 open issues
What changed in this PR
Addresses #9482 by improving stateless shell timeout cleanup for background jobs.
Changes:
- Signals POSIX process groups even after the shell exits.
- Bounds output draining after timeout cleanup.
- Adds background-job regression tests and updates termination tests.
| File | Description |
|---|---|
python/packages/tools/tests/test_shell_killtree.py |
Tests process-group signalling after shell exit. |
python/packages/tools/tests/test_local_shell_tool.py |
Adds background-job timeout regressions. |
python/packages/tools/agent_framework_tools/shell/_killtree.py |
Adds group signalling and grace-period polling. |
python/packages/tools/agent_framework_tools/shell/_executor.py |
Limits post-timeout output draining. |
Review effort: Balanced
Eduard van Valkenburg (eavanvalkenburg)
left a comment
There was a problem hiding this comment.
The timeout cleanup now signals the POSIX process group after leader exit, preserves descendant termination, bounds abandoned pipe drains, closes their transports, and avoids zombie-only grace delays. The Linux regressions cover the critical lifecycle cases.
|
digit50 please fix the merge conflicts |
When a stateless LocalShellTool command timed out, background and double-forked jobs were not killed, and the call waited until they released stdout and stderr. The timeout now signals the whole process group, also after the shell has exited, keeps the descendant walk, and bounds the final output drain. A Linux test checks that both kinds of job are killed and that the call returns promptly.
Abandoning the post-timeout drain left both pipe transports open while a detached child held them, so repeated timeouts could leak descriptors; the subprocess transport is now closed when the drain gives up. The process-group wait also treated a group of unreaped zombies as alive and spent the whole grace period on it; it now looks for a member that is not a zombie.
3c1806a to
3f0c5e4
Compare
|
digit50, this pull request is waiting on your response. If there is no further activity within 4 days, it will be closed automatically. (This is an automated message.) |

Motivation & Context
closes #9482
With
LocalShellTool(mode="stateless"), a timed-out command that left a background job holding stdout or stderr did not return until that job exited on its own.kill_process_treereturned early when the shell had already exited (sleep 6 &), and the psutil walk missed double-forked jobs that were reparented away from the shell, so neither was signalled and the unbounded drain inrun_statelesswaited for them.Description & Review Guide
kill_process_treeon POSIX now signals the process group withos.killpg(proc.pid, ...), SIGTERM first and SIGKILL after the grace period, even when the shell has already exited. The shell is started withstart_new_session=True, so its pid is the group id, and that still holds after the shell has been reaped, whenos.getpgid(proc.pid)would fail.timed_out=True, and background and double-forked jobs started by the command are killed._wait_for_exit, which pollsos.killpg(pgid, 0)because a process group has no handle to await.Tests:
test_stateless_timeout_kills_background_jobs_holding_pipes(Linux only) runssleep 5 & echo $! > <pidfile>and(sleep 5 & echo $! > <pidfile>); sleep 30withtimeout=0.5and checks that each call returns within two seconds withtimed_out=Trueand that the recorded background pid is gone. Both cases took 5.0 s and failed before the change.test_kill_via_psutil_signals_process_group_after_shell_exitedchecks that the group is signalled for a shell that has already exited.os.killpg, and the stdlib test expects the group id to be the pid.Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix); a workflow keeps the label and title prefix in sync automatically.