Repository navigation
Drain subprocess pipes while probing interpreters and standardize probe deadlines #530
Description
Tracking plan: #528
Priority: P1. Evidence: pipe backpressure reproduced; other runner gaps confirmed by source inspection.
Problem and evidence
The interpreter probe pipes stdout/stderr, polls for child exit, and only then calls wait_with_output. A child that fills a pipe cannot exit until PET drains it, so a healthy interpreter is killed by the timeout.
A temporary Python startup fixture writing 128 KiB to stderr exited successfully in 217 ms when its output was drained normally. Resolving the same interpreter through PET took 15,111 ms and returned a timeout error. These are Windows debug-build diagnostic measurements, not release budgets.
Sources: interpreter probe, Conda subprocess, Poetry subprocess.
The interpreter deadline currently starts after process creation. Conda/Poetry blocking output calls do not share equivalent timeout/cleanup semantics. Do not claim that a deadline bounds a synchronous OS process-creation call unless the implementation actually does so.
Scope
Introduce the smallest reusable process runner needed for interpreter and manager probes: concurrent pipe draining, bounded retained output, explicit exit/parse/I/O errors, deadlines, and child cleanup. Preserve the file-first discovery policy and do not add new spawn fallbacks. Define behavior for descendants retaining pipe handles and for capture-limit exhaustion; do not silently truncate valid protocol data into success.
The non-UTF-8 panic already belongs to #525 / #526. Reuse that fix rather than duplicating it, and retain its regression tests while changing the runner.
Acceptance criteria
- A healthy fixture emitting more than pipe capacity on stdout and/or stderr completes without waiting for the 15-second timeout.
- Hanging child, malformed output, nonzero exit, invalid UTF-8, and output-limit cases return explicit outcomes without panic or abandoned RPCs.
- Timeout/cleanup behavior has Windows and Unix tests, including an inherited-pipe case; tests clean up every process they create.
- Conda and Poetry probe call sites use the established deadline/error contract, with no unbounded output wait hidden behind an RPC.
- Captured memory stays within a documented limit while pipes continue to be drained or the child is terminated deliberately.
- Quiet successful interpreter probes preserve existing environment output and do not regress the relevant release benchmark budgets.
Dependencies
Coordinate implementation after #525 / #526 because both touch interpreter-output handling. Bounded request scheduling and sharing failed in-flight probes are separate follow-up work in #539. Reuse this runner from the #533 workload harness; the complete plan is #528.
Activity
karthiknadig commentedon Sep 23, 2026
After #526 merged, I audited the preserved subprocess prototype before reusing it. It is not ready to ship as a complete runner: Windows resumes a suspended child by enumerating system threads; cleanup can apply two separate grace periods and replace a primary failure; Unix descendant tests conflate terminated processes with reaped grandchildren. Stable Rust 1.98 still does not expose CommandExt::spawn_with_attributes or ChildExt::main_thread_handle, so replacing the Windows launch boundary is not a trivial API switch. The old dirty worktree remains unchanged. I will first isolate the reproduced interpreter pipe-backpressure/capture problem with explicit immediate-child ownership and no blocking pipe readers. Full process-tree supervision, manager deadlines and active-shutdown ownership remain tracked here rather than pretending that a partial fix completes #530.
fix: drain interpreter probe pipes while running (#554)
karthiknadig commentedon Sep 24, 2026
Draft #555 now routes every Conda/Poetry probe through the shared bounded runner, completing the manager-deadline slice after #554. Arguments/cwd and existing path text semantics are preserved; real noisy/hanging fixture and parser/failure tests pass on Windows/Linux. The broader issue remains open: direct-child ownership is explicitly documented, not silently promoted into a guarantee about descendant trees or active-server shutdown. That ownership boundary is the next design task before #529 can safely complete.
karthiknadig commentedon Sep 24, 2026
Ownership design follow-up: checked existing stable-Rust alternatives rather than copying the old prototype. Both process-wrap (src/windows.rs) and command-group resume suspended children by system ToolHelp thread enumeration; switching dependencies does not remove that tradeoff. The native rust-subprocess wrapper also discards PROCESS_INFORMATION.hThread and exposes no job-list launch hook. Stable std does not expose the required raw startup attribute/primary-thread handle APIs. A race-free Windows tree supervisor therefore needs either an explicitly reviewed enumeration strategy or a separately audited native launch boundary preserving quoting, batch launch, cwd/environment and handle inheritance. No speculative backend or dependency was added. #555 remains the independently verified manager-deadline slice; full tree ownership and active shutdown remain open.
fix: bound Conda and Poetry subprocess probes (#555)
fix: own subprocess probe descendants (microsoft#556)