In PR #77 (Issue #63), try_wait polling was replaced with child.wait().await inside a lock on Arc<Mutex<Child>>.
This caused two critical regressions in ProcessManager that broke Stage 3 (chartread):
Child Mutex Deadlock: c.wait().await held &mut self on the Mutex<Child> guard for the entire lifetime of the process. Interactive commands (send_stdin and kill) attempted to acquire the same mutex, blocking Calibrate / Retry / Skip / Cancel actions until the child process had already exited.
Premature Stdin Closure: Calling child.wait() in Tokio explicitly drops child.stdin to avoid parent/child deadlocks. Because the wait task spawned immediately after spawn(), stdin was closed before any user interaction could occur.
### Problem Description
In PR #77 (Issue #63), `try_wait` polling was replaced with `child.wait().await` inside a lock on `Arc<Mutex<Child>>`.
This caused two critical regressions in `ProcessManager` that broke Stage 3 (`chartread`):
1. **Child Mutex Deadlock**: `c.wait().await` held `&mut self` on the `Mutex<Child>` guard for the entire lifetime of the process. Interactive commands (`send_stdin` and `kill`) attempted to acquire the same mutex, blocking Calibrate / Retry / Skip / Cancel actions until the child process had already exited.
2. **Premature Stdin Closure**: Calling `child.wait()` in Tokio explicitly drops `child.stdin` to avoid parent/child deadlocks. Because the wait task spawned immediately after `spawn()`, `stdin` was closed before any user interaction could occur.
### Resolution
Implemented in PR #83 and released in `v0.2.1`:
- `take()` standard streams (`stdin`, `stdout`, `stderr`) immediately upon process spawn.
- Decoupled `ChildStdin` into an independent `stdins` map (`HashMap<String, Arc<Mutex<ChildStdin>>>`).
- Used `tokio::select!` between `child.wait()` and a dedicated oneshot cancellation `kill_rx` channel.
- `send_stdin` accesses only the `ChildStdin` handle without blocking against `child.wait()`.
- `kill` drops stdin immediately and triggers `child.start_kill()`.
### Reference
- Resolution PR: #83
- Release: `v0.2.1`
gronod
added this to the Milestone 7: v0.2 ship milestone 2026-08-25 13:55:11 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem Description
In PR #77 (Issue #63),
try_waitpolling was replaced withchild.wait().awaitinside a lock onArc<Mutex<Child>>.This caused two critical regressions in
ProcessManagerthat broke Stage 3 (chartread):c.wait().awaitheld&mut selfon theMutex<Child>guard for the entire lifetime of the process. Interactive commands (send_stdinandkill) attempted to acquire the same mutex, blocking Calibrate / Retry / Skip / Cancel actions until the child process had already exited.child.wait()in Tokio explicitly dropschild.stdinto avoid parent/child deadlocks. Because the wait task spawned immediately afterspawn(),stdinwas closed before any user interaction could occur.Resolution
Implemented in PR #83 and released in
v0.2.1:take()standard streams (stdin,stdout,stderr) immediately upon process spawn.ChildStdininto an independentstdinsmap (HashMap<String, Arc<Mutex<ChildStdin>>>).tokio::select!betweenchild.wait()and a dedicated oneshot cancellationkill_rxchannel.send_stdinaccesses only theChildStdinhandle without blocking againstchild.wait().killdrops stdin immediately and triggerschild.start_kill().Reference
v0.2.1Resolved by PR #83 and included in release
v0.2.1. ProcessManager standard input handling has been decoupled from child process waiting tasks.