fix(cmux-tui-core): viewer sizes go behind the input port's held keys without waiting; the settle ends by its deadline; a lost taken frame may be written (R-DAEMON-STATE S1.6c part 2)
Summary
R-DAEMON-STATE stage S1 step 6c, part 2 (handoff/rewrites/R-DAEMON-STATE/DESIGN.md, section 3). Behind the per-mux switch CMUX_TUI_RDS_INPUT (default off; S1.7 turns it on). It closes the remaining review items of #35082 and #35280 before S1.7.
The defects (lane on).
- #35082 review 1 item 4: a key that moves the grid sends a
ViewerSizeon its own turn, under the global sizing lock (apply_terminal_grid->resize->send_viewer_size). That frame waited in the input port's order barrier for up to 2 s behind the port's frames (a slow paste) while it held the sizing lock, and then it went first anyway. - #35082 review 2 should-do 2: an attach that settles the host's viewer size waited the barrier's 2 s, past its own deadline.
- #35082 review 2 should-do 3: a
Writtenframe whose writer took it and then was lost (a panic during the write) answered "not written", so the creating-input flush dropped bytes that may be on the host.
The fix.
ViewerSizeandReleaseViewergo on the input port when it holds frames (PortOrder::enqueue_after_pending): after those frames, with no waiter and no wait. A newer size replaces a queued one that is still the last frame, so sizes cannot pile up behind a stalled port. When the port holds nothing (or the lane is off: the port then holds nothing), they are written directly, as before.begin_viewer_size_barriertakes the attach's deadline (direct_writer_until), so the settle ends by its own deadline.- A frame the writer took and lost answers
InputMayBeWritten; one still queued answers "not written". - (#35375 review) A size also queues while the writer waits for a reconnect. Written directly then, it went to the lost connection, and a size queued before the loss reached the new host after the install's replay of the newest lease: the host kept the stale size (or a lease the daemon had released), and a later send of the same lease wrote nothing. Queued, the newest size replaces the trailing one, so the new host's last size frame is the newest lease or release. The barrier's reconnect skip exists because its callers hold the runtime lock the install needs; this enqueue never waits.
reconnect_waitreads the port state its caller holds and never takes the port lock again. The first version ofenqueue_after_pendingcalled it under the port lock, and in test builds it locked the same mutex again (the test override iscfg(test)), so the test hung. Release builds never took that second lock.- From the #35280 review: tests that drive the real call sites (the reader's backoff give-up
wait_or_fail, andSurface::disconnect_for_daemon_shutdown) while aWrittenwrite waits for a reconnect; the reply-timeout tests take their frame on a running writer, not a new thread (#35082 review 2 should-do 4); the docs state the lock rule the right way round (the port lock is a leaf).
The person's keys (InputReply::Accepted) still never wait for a write.
Testing
Focused command: ./scripts/verify-cmux-tui-hosted.sh --filter hosted (Linux test (linux)).
- Red at ac7ffa2b4e1, run https://github.com/manaflow-ai/cmux/actions/runs/41819135277: 3 FAILED:
a_viewer_size_behind_a_key_the_port_holds_goes_after_it_without_waiting("a viewer size waited 2.000073955s for the key the port held"),a_viewer_size_settle_ends_by_its_own_deadline_behind_a_held_frame("an attach with a 200 ms deadline waited 2.000094865s"),a_write_the_writer_lost_after_taking_it_may_be_written("reported as not written"). - Call-site tests (#35280 review): red on the part-1 fix with its three call sites removed, 19e10238b8c (
test/rds-s16c-callsite-red-never-merge), run https://github.com/manaflow-ai/cmux/actions/runs/41820912843:a_write_waiting_for_a_reconnect_fails_when_the_reader_gives_up,..._when_the_readers_backoff_gives_upand..._when_the_daemon_shuts_downFAILED, each write waited its 5 s reconnect deadline. - The first green runs at 10b577c0a0b, 67023f50a46 and 476e0bfc4a0 hung in
a_viewer_size_behind_a_key_the_port_holds_goes_after_it_without_waiting(the test-only self-deadlock above; fleet step b419d937adc3be34aab5e459: TIMEOUT at 120 s, 36 of 37 passed). Those runs were cancelled. - Green at edc0dfbbfae (the lock fix): run https://github.com/manaflow-ai/cmux/actions/runs/41827148358:
test (linux)passed, everysurface::tests::hostedtest ok, the six above included. Fleet nextest step 00d7a23c4471da68e76d001b (surface::tests::hosted, one process per test): 37 passed, the viewer-size test in 0.57 s. --filter sized_attachat 67023f50a46, run https://github.com/manaflow-ai/cmux/actions/runs/41819782256: passed.- At 3bafe07e51f (the #35280 squash and feat e424d61b452 merged; own-diff patch-id 49555f8085ef against the merge-base): run https://github.com/manaflow-ai/cmux/actions/runs/41829221044:
test (linux)andlint (linux)passed. - Review fix: red at 5ae437af1df (
test/rds-s16c2-r1-red-never-merge), run https://github.com/manaflow-ai/cmux/actions/runs/41832423867 (--filter during_a_reconnect_wait):a_viewer_size_sent_during_a_reconnect_wait_is_the_new_hosts_last_sizeanda_viewer_size_release_during_a_reconnect_wait_is_the_new_hosts_last_size_frameFAILED (the new host's size frames were[Some((97, 31))]: the stale size, not 100x30 or the release). Green at the head 01af275609c, run https://github.com/manaflow-ai/cmux/actions/runs/41832544739 (--filter hosted):test (linux)andlint (linux)passed, both tests ok.
Compile: fleet job d5fb95c05f9bedac13aaf0ca at the head 01af275609c: CmuxNext, app and cmux-tui passed (also 1abd4a080106318b89e00eb5 at 3bafe07e51f and c507ba1785de9898784039bf at edc0dfbbfae).
Observed / Not run
- Observed: the hosted runs and fleet steps above.
lint (linux)passed at 3bafe07e51f and at the head (at edc0dfbbfae it was red only on the base findingcmux-tui/src/cloud_environment.rs:22, from #35133). - Not run: macOS execution of the focused tests. The S1.7 typing A/B runs on a build that contains this change with the review fix (S1.7 head 0678016998d, build f8685e3d4f92deb22eb9a313).
Changelog
none
Proof
No capture: daemon-only change behind a default-off switch, no UI.
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes three remaining review items for the input port lane: viewer-size frames no longer wait behind the keys the port holds, the viewer-size settle ends by its own deadline, and a frame the writer took and lost answers "may be written" so the creating-input flush keeps its bytes.
ViewerSizeandReleaseViewerare now queued on the input port behind its pending frames without waiting; a newer size replaces a queued one, so sizes cannot pile up. When the port holds nothing they are written directly as before. This also holds while the writer waits for a reconnect, so the new host's last size frame is the newest lease (or release) rather than a stale Queued size.- The viewer-size settle barrier now uses the attach's deadline (
direct_writer_until) instead of the order barrier's 2 s wait. - A
Writtenframe whose writer took it and was lost answersInputMayBeWritten; one still queued answers "not written". reconnect_waitnow reads the port state its caller already holds, fixing a test-only self-deadlock inenqueue_after_pending(release builds never took the second lock).- Adds tests driving the real call sites (reader's backoff give-up and daemon shutdown detach) and starts the reply-timeout tests on a running writer, not a new thread.