cmux-imessage: a send never waits on a descendant holding stderr (#33301)
Fixes #33301, a follow-up from the Codex review of #33219: a send could hang past its timeout when a descendant of osascript inherited stderr and kept it open.
What changes
- stderr is drained on its own thread into a shared buffer, and nothing joins that thread.
- On timeout: the send returns as soon as the child is killed and reaped.
- On a normal exit: it keeps what stderr has delivered within a 2-second grace, then returns.
TimedRunner { timeout }is the runner.SystemRunnerkeeps its name and API and delegates to it withSEND_TIMEOUT.- Stop condition: the drain thread ends at end of file, when the last holder of the pipe closes it. It never blocks a send.
Tests
Two commits: the failing tests first (both red on Linux CI, each waiting the full 20 s on the descendant), then the fix. Green: 9 send tests pass in about 2 s, including a 1-second timeout with sleep 20 & wait and an exit that leaves sleep 20 & holding stderr. The 13 reader tests still pass. clippy -D warnings, the panic ratchet and rustfmt are clean.
Agent session: agent session
🤖 Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes #33301: an iMessage send no longer hangs past its timeout when a descendant of the child keeps stderr open.
- stderr is drained on its own thread into a shared buffer and never joined, so a descendant holding the pipe open can't block the send; on timeout the send returns as soon as the child is killed and reaped, and on a normal exit it takes what stderr delivered within a 2-second grace.
- Adds
TimedRunner { timeout };SystemRunnerkeeps its API and delegates withSEND_TIMEOUT. - Adds two tests: a 1-second timeout with
sleep 20 & waitand an exit that leavessleep 20 &holding stderr; both previously waited the full 20 seconds.