48 lines
2.7 KiB
Markdown
48 lines
2.7 KiB
Markdown
# Your timeout killed the process, not the process group — Iannucci Solutions
|
|||
|
|
|
||
|
|
## Metadata
|
||
|
|
|
||
|
|
| Field | Value |
|
||
|
|
|---|---|
|
||
|
|
| Institute | Molodetz Institute — Research & Retrieval Division |
|
||
|
|
| Subject | async-pty-process-testing |
|
||
|
|
| Document type | Source Dossier |
|
||
|
|
| Tier | 1 |
|
||
|
|
| Source title | Your timeout killed the process, not the process group |
|
||
|
|
| Source URL | https://www.iannuccisolutions.com/blog/timeout-does-not-kill-child-processes.html |
|
||
|
|
| Source publisher / author | Iannucci Solutions (blog) |
|
||
|
|
| Source type | blog post |
|
||
|
|
| Source date (published) | Unknown — not stated |
|
||
|
|
| Date researched | 2026-10-09 |
|
||
|
|
| Researched by | tai (agent), model: Claude Sonnet 5, on behalf of retoor |
|
||
|
|
| Research method | web search synthesis |
|
||
|
|
| Confidence | High |
|
||
|
|
| Status | Active |
|
||
|
|
|
||
|
|
## Summary
|
||
|
|
|
||
|
|
A blog post documenting a real incident: a script's timeout mechanism killed only the top-level process it had launched, while a worker process that script had itself spawned kept running in the background. The root cause and fix described are directly applicable to any subprocess-timeout design.
|
||
|
|
|
||
|
|
## Key findings
|
||
|
|
|
||
|
|
- `proc.kill()`/`proc.terminate()` signal only the direct child process — if that child spawns further children (a shell command, a subagent, a worker), killing the direct child does not propagate to them.
|
||
|
|
- The documented fix: start the child in its own session/process group (e.g. `start_new_session=True` in Python), which makes the child's PID equal to its process group ID (PGID), then signal the whole group (`os.killpg(pgid, sig)`) rather than just the one process.
|
||
|
|
|
||
|
|
## Pros / strengths
|
||
|
|
|
||
|
|
- A concrete, lived incident report, not a theoretical warning — the exact failure mode (worker alive after the "parent" was killed) is precisely the risk a test framework's timeout handling needs to guard against, since `tai.py` itself can spawn subagents and shell commands.
|
||
|
|
|
||
|
|
## Cons / limitations / caveats
|
||
|
|
|
||
|
|
- Describes the synchronous `subprocess` module's behavior; the asyncio adaptation (confirming `start_new_session=True` is forwarded correctly by `asyncio.create_subprocess_exec`) was treated as a reasonable extrapolation, not independently verified against asyncio's own documentation in this specific source.
|
||
|
|
|
||
|
|
## Relevance to tai
|
||
|
|
|
||
|
|
This is the direct, primary justification for `tai_test.py`'s `ManagedProcess` always spawning with `start_new_session=True` and `terminate()` using `os.killpg(self.proc.pid, sig)` rather than `self.proc.terminate()`. Without this, a timed-out test driving `tai.py` through a shell command or a forked subagent could leave that spawned work running indefinitely in the background after the test framework believed it had cleaned up.
|
||
|
|
|
||
|
|
## Changelog
|
||
|
|
|
||
|
|
| Date | Change |
|
||
|
|
|---|---|
|
||
|
|
| 2026-10-09 | Initial dossier created. |
|