Skip to content

test(git-remote): run_bounded clean-exit path lacks a Windows descendant reap (Unix-only cfg) #222

Description

@beardthelion

Follow-up from the PR #192 review (jatmn's F3, deliberately deferred there: our CI is Linux-only, so a Windows fix would ship untested).

The gap

run_bounded in crates/git-remote-gitlawb/tests/real_git_fetch.rs bounds a git process (the "leader") plus its stdout/stderr reader threads by a timeout. git spawns the git-remote-gitlawb helper as a descendant, which can block in a ~300s HTTP request while holding the inherited pipe write-ends.

  • Timeout path (deadline hit): reaps the whole tree on both platforms — #[cfg(unix)] reap_group / #[cfg(not(unix))] reap_tree (taskkill /T), around lines 408-411.
  • Clean-exit path (leader exits before the deadline, completed == true): reaps the group before the reader-thread joins — but only under #[cfg(unix)] (lines 435-442). There is no #[cfg(not(unix))] counterpart.

The code documents the exact hazard at lines 426-434:

A leader that exits on its own does NOT guarantee the pipes are closed: a descendant (the git-remote-gitlawb helper mid-HTTP request) can outlive the leader while still holding the inherited stdout/stderr write-ends, so the reader joins below would block on its ~300s HTTP timeout, past the deadline this function promises.

On Windows, when the leader exits cleanly while the helper is still blocked mid-request, nothing reaps the descendant before out_reader.join() / err_reader.join() (lines 444-445), so those joins block on the descendant-held pipe until the helper's HTTP times out (~300s) — far past the intended 30s bound.

Why it isn't caught today

  • No Windows CI: every pr-checks job is ubuntu-latest, so the Windows path is never exercised.
  • taskkill /T (the timeout-path Windows reap) can't help on the clean-exit path anyway: after the leader has exited there is no leader pid to anchor the process-tree walk.
  • std::process::Child::drop is a no-op (it neither kills nor waits), and it owns the leader, not the descendant — so there is no RAII rescue.

Severity: latent — a real code gap, Windows-only, not currently triggered by the committed tests (the shim responds promptly) and never run on Windows. The #[cfg(unix)] clean-exit reap's own existence shows the authors consider "leader exits while a descendant holds the pipe" a reachable scenario; Windows simply lacks the counterpart.

Suggested direction

Establish descendant ownership at spawn time on Windows — a Job Object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, assigned before the leader starts — so the whole tree dies when run_bounded returns, on both the clean and timeout paths. Pair it with a Windows CI lane so the behavior is actually exercised; without one the fix would ship untested, which is why it was deferred from #192.

Verified by code-path trace against the current head (3ceba33); not executed, since it is Windows-only and unreproducible on Linux.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind:testTest coverage or harnesssev:lowCosmetic, cleanup, or nice-to-havesubsystem:replicationMirror, replica, and cross-node sync

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions