Skip to content

fix(proxy): stop blocking on orphaned stdio pipes after the child exits#2830

Open
isacgalvao wants to merge 1 commit into
rtk-ai:developfrom
isacgalvao:fix/proxy-orphan-pipe-hang
Open

fix(proxy): stop blocking on orphaned stdio pipes after the child exits#2830
isacgalvao wants to merge 1 commit into
rtk-ai:developfrom
isacgalvao:fix/proxy-orphan-pipe-hang

Conversation

@isacgalvao

Copy link
Copy Markdown

Summary

Fixes #2829

Test plan

  • cargo fmt --all && cargo clippy --all-targets && cargo test (Windows 11, stable)
  • New test proxy_exits_when_child_dies_despite_orphaned_pipe fails on develop (hangs until the 15s deadline) and passes with this change
  • Manual before/after: rtk proxy cmd /c "start /b waitfor ZZZ /t 20 & exit /b 0" — before: returns after ~20.2s (when the orphan dies; forever with a real daemon); after: returns in ~2s with exit code 0 and identical output
  • Manual sanity: rtk proxy git status, rtk proxy cmd /c "echo hi & exit /b 3" — output and exit codes unchanged, no added latency on the normal EOF path

The proxy path joined its stdout/stderr reader threads unconditionally.
Those threads only stop at pipe EOF, which never arrives when a
descendant of the child (build daemon, background job) inherits the
write end and outlives it - rtk kept running forever after the wrapped
command had exited and its output was fully streamed.

Once child.wait() returns, give the readers a 2s grace period to drain
buffered output, then detach any reader still blocked on an orphaned
pipe; process::exit() in main() reaps it. Captures are shared through
Arc<Mutex<...>> so tracking still records what was streamed on detach.

Fixes rtk-ai#2829
Copilot AI review requested due to automatic review settings July 5, 2026 02:37
@CLAassistant

CLAassistant commented Jul 5, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a long-standing hang in rtk proxy where the proxy process could block indefinitely after the wrapped command exited if any descendant process kept the inherited stdout/stderr pipe write-ends open (common with daemons/watchers/background jobs). The fix makes proxy completion driven by the direct child’s exit, with a bounded grace period for post-exit pipe draining, and adds a regression test for the orphaned-pipe scenario.

Changes:

  • Update the proxy streaming implementation to join stdout/stderr reader threads with a 2s grace period after child.wait(), then detach if still blocked on orphaned pipes.
  • Move captured stdout/stderr into shared buffers so tracking can still consume what was streamed before detaching.
  • Add an integration test that reproduces the orphaned-pipe hang and asserts rtk proxy returns promptly with the child’s exit code (with a unix-only streamed-output assertion).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/main.rs Implements bounded post-exit joining/detaching of stdout/stderr reader threads and shared capture buffers to prevent hangs on orphaned pipes.
tests/proxy_orphan_pipe_test.rs Adds an integration regression test that leaves an orphan process holding pipes open and verifies rtk proxy exits promptly and forwards the exit code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rtk proxy never returns after the child exits when a descendant keeps the stdout/stderr pipes open (Windows repro, same mechanism as #2320)

3 participants