cmux-tui: a daemon start adopts its terminal hosts in parallel (perf100, cx-p00l3.1)
perf100 tui lane, bead cx-p00l3.1 (P1): after an update, the new daemon adopts every terminal host one at a time before it answers. On Lawrence's laptop, 113 terminals stayed blank for 8 to 20 s. On mac26 with 112 terminals, the daemon took 8.4 s to start, and the first terminal frame came at 14.7 s.
Change (mux/terminal_adoption.rs)
- The startup pass still makes every registry decision serially and in record order: exit sidecars, tombstones, incarnation checks, the template claim, the legacy import, orphan recovery, and the Adopting transition.
- It then collects the live hosts and runs their handshakes at once on up to 16 scoped threads. A handshake connects and replays the host's snapshot into the mirror.
- The results commit serially in record order, through the same success and failure paths as before. Lifecycle and topology decisions therefore do not change.
- Output order does not change either. Each host replays its own snapshot, and later output follows on that host's stream.
- The batch sets a Kitty budget owner floor while it runs, so each side-by-side reservation gets its final share at once. Without the floor, a reservation could wait 500 ms and then start with graphics disabled. A drop guard ends the floor, also when the commit loop returns early.
- A debug-only seam,
CMUX_TUI_TEST_ADOPT_HANDSHAKE_DELAY_MS, slows each handshake for the test.
Evidence
tests/terminal_host_recovery/parallel_handoff.rs runs on rbx. It uses 16 cat terminals, SIGKILLs the daemon, and restarts it with every handshake slowed to 400 ms.
- Red at b7bf0b6274e3:
answered in 6.57 s (serial: 6.4 s). - Green at dbeaf2d6b76b:
answered in 0.63 s. - After the handoff, every terminal is running on its old host pid, keeps its screen, and new input lands after the old screen.
Real handoff with no seam, on rbx (Linux, release build). The script is handoff_bench.py. Setup: 100 terminals with 20,000 lines of scrollback each. The old daemon is SIGKILLed, and the new binary starts on the same state.
| Run | Base 3c73ce6e | After | Ratio |
|---|---|---|---|
| spawn to first answer, run 1 | 3.80 s | 1.09 s | 3.5x |
| spawn to first answer, run 2 | 3.87 s | 1.31 s | 3.0x |
| spawn to all 100 running | 3.81 / 3.88 s | 1.09 / 1.32 s | 3.0-3.5x |
The GUI measurement with scripts/cmux-next/quit-launch-bound.py --handoff (#22919) runs on cmux-lawrence-2, before and after. The results will follow as a comment.
Not in this PR (named, decision for the coordinator)
- The daemon still answers only after the whole adoption pass. The coordinator asked for the next step: answer at once, attach visible terminals first, and adopt the rest in the background. That needs the app to accept tabs in the
adoptingstate at launch, which the async retry path already publishes (PendingTerminal::Adopting). It also needs an order of visible terminals first. - The handoff case is not yet added to the CI guard.
quit-launch-bound.pylives in #22919, which is still open. I will add the case there, or in a follow-up once it lands.
Observed / Not run
- Ran on rbx:
- clippy: 2 base errors (
agent_reports.rs:458,event_shape_tests.rs:148), none from this change. terminal_host_recovery: 204 pass, 4 fail. The same 4 fail on base 3c73ce6e (sized_attachx3,a_respawned_terminal_resumes_its_agent_session).- Kitty tests: 34/34 pass.
- clippy: 2 base errors (
- Review: the subagent findings and their resolution are in a PR comment.
Changelog
- Fixed: after an update, terminals show again in about a third of the time.
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
After an update, the new daemon adopted each terminal host one at a time before answering, leaving 113 terminals blank for 8-20s. It now runs the host handshakes (connect plus snapshot replay) in parallel on up to 16 threads while keeping every registry and lifecycle decision serial and in record order, so the daemon answers in about a third of the time with no change to output order.
- The startup pass still makes all registry decisions serially and in record order; only the host handshakes run concurrently, and their results commit in the same order as before.
- While the batch runs, a Kitty budget owner floor gives side-by-side reservations their final share at once; a drop guard ends the floor even on early error.
- Adds a debug-only seam (
CMUX_TUI_TEST_ADOPT_HANDSHAKE_DELAY_MS) and a test that SIGKILLs the daemon and restarts it with 16 slowed handshakes, down from 6.4s to 0.63s.
Changelog: Fixed: after an update, terminals show again in about a third of the time.