v0.9.14: Replace runtime polling loops and blocking shapes with watch/notify
- Dominant language
- Rust
- Stars
- 41k
- Forks
- 3.6k
- Avg merge
- 13h 59m
- Merged PRs (30d)
- 299
Description
From `codewhale-ops/PERF-OPPORTUNITIES-20260915.md` §2 (R3, R4, R7, R8) — mostly **[S]** findings; read the call path before slicing. Each is an independently shippable fix.
## Polling loops that should be watchers/notifications
- [ ] **R3** — `tui/src/tools/workflow/mod.rs:4811–4852`: the completion already arrived on the channel, then `for _ in 0..50 { read-lock; sleep(20ms) }` waits for the manager record to settle — up to 1s added latency per workflow task completion and 50 lock round-trips under fan-out. Carry the terminal status in the completion envelope (it exists in the `completion.payload` path) or subscribe via a per-agent `tokio::sync::watch`.
- [ ] **R7a** — `tui/src/mcp.rs:1534`: per *pending* MCP connection, a 50ms loop doing directory-permission checks + fd-lock open + JSON state load (sync fs at 20Hz per plugin server). One shared fs-notify watcher task.
- [ ] **R7b** — `tui/src/runtime_api.rs:2126`: each fleet SSE viewer polls the event store at 250ms. `tokio::sync::watch`/`broadcast` fed by the append path; poll only as fallback.
- [ ] **R7c** — `tui/src/task_manager.rs:2876`: 5ms retry loop for the store file lock = 200 wakeups/s under contention. Exponential backoff 5→50ms plus an in-process `tokio::sync::Mutex` for same-process contenders.
## Blocking / serialization shapes
- [ ] **R4** — `crates/mcp/src/stdio_client.rs:964–991`: `Drop for Connection` runs `try_wait` + `thread::sleep(10ms)` up to a 500ms deadline on whichever thread drops it (manager reload, pool rebuild stalls an executor thread). `Connection` is also behind `std::sync::Mutex`, so one in-flight request blocks all callers of that server. Shutdown in a reaper task / `spawn_blocking`; longer term replace with `tokio::process` so the wait is event-driven.
- [ ] **R8a** — `tui/src/exec_agent.rs:543`: `rx_event.write().await` guard held across `recv().await` excludes recovery readers until the next event arrives. `Notify` or a dedicated single-owner consumer task. Same shape at `runtime_threads.rs:183` (deadline-bounded there).
- [ ] **R8b** — `app-server/src/lib.rs:620,2403`: sequential `interrupt_stdio_turn` in a loop at teardown — `join_all` them.
Reusable idiom: `tokio::sync::watch` for "latest state" instead of polling; `Notify` for one-shot wakeups. Every 5ms/20ms/50ms/250ms loop above is a watch channel waiting to happen.
Contributor guide
Research direction
Read the listed call paths in tui/src/tools/workflow/mod.rs, tui/src/mcp.rs, tui/src/runtime_api.rs, tui/src/task_manager.rs, crates/mcp/src/stdio_client.rs, tui/src/exec_agent.rs, runtime_threads.rs, and app-server/src/lib.rs before selecting one independently shippable item. Trace each polling or blocking path and its existing channel or append path, then verify the selected change removes the described wait or serialization shape without changing behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, cli, tooling
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100