Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
2472251
test(cmux-tui-core): the person's key waits for no shared lock (R-DAE…
lawrencecchen Nov 3, 2026
7dc4598
test(cmux-tui-core): a key waits for no client registry lock (R-DAEMO…
lawrencecchen Nov 3, 2026
f1ea1b6
fix(cmux-tui-core): the person's key reads the client registry's publ…
lawrencecchen Nov 3, 2026
590cb11
fix(cmux-tui-core): the key reads the session's published routes; one…
lawrencecchen Nov 3, 2026
5d0e4a0
test(cmux-tui-core): a key waits for no surface admission lock, with …
lawrencecchen Nov 3, 2026
8e5c8a2
fix(cmux-tui-core): the surface admission budget reserves with atomic…
lawrencecchen Nov 3, 2026
154730a
test(cmux-tui-core): a key waits for neither the link peers nor the p…
lawrencecchen Nov 3, 2026
48c4d9e
test(cmux-tui-core): a change cut short by a panic is never published…
lawrencecchen Nov 3, 2026
4462389
test(cmux-tui-core): a reservation unshares a published route map; a …
lawrencecchen Nov 3, 2026
8f7d405
fix(cmux-tui-core): link peers and host proxy authors are published; …
lawrencecchen Nov 3, 2026
eecb0d8
test(cmux-tui-core): a key waits for no creating-input lock, and for …
lawrencecchen Nov 3, 2026
8a50288
fix(cmux-tui-core): a key skips the creating-input lock while nothing…
lawrencecchen Nov 3, 2026
831686b
test(cmux-tui-core): the surface budget's limits on both arms; a lock…
lawrencecchen Nov 3, 2026
10f1669
fix(cmux-tui-core): drop an unnecessary path qualification in a test …
lawrencecchen Nov 3, 2026
690b82f
test(cmux-tui-core): a poisoned session state never routes by the pub…
lawrencecchen Nov 3, 2026
bb0265f
fix(cmux-tui-core): the lane's surface read takes the old path on a p…
lawrencecchen Nov 3, 2026
3e0f7b2
Merge branch 'fix/p0-typing-s1-relay-peers' into fix/p0-typing-s1-cre…
lawrencecchen Nov 3, 2026
3b2e29f
fix(cmux-tui-core): the creating-input live check reads the routes on…
lawrencecchen Nov 3, 2026
123e363
Merge branch 'fix/p0-typing-s1-creating-input' into fix/p0-typing-s1-…
lawrencecchen Nov 3, 2026
dfe81bf
test(cmux-tui-core): a key waits for neither the sizing nor the activ…
lawrencecchen Nov 3, 2026
0fb7c43
fix(cmux-tui-util): a test dereferences the published u64 once (E0614…
lawrencecchen Nov 3, 2026
8ee8220
Merge branch 'fix/p0-typing-s1-relay-peers' into fix/p0-typing-s1-cre…
lawrencecchen Nov 3, 2026
4fd7672
Merge branch 'fix/p0-typing-s1-creating-input' into fix/p0-typing-s1-…
lawrencecchen Nov 3, 2026
7fad48f
fix(cmux-tui-core): a key skips its sizing note when its view is the …
lawrencecchen Nov 3, 2026
cd9a6dc
fix(cmux-tui-core): the sizing test hook went in above another test h…
lawrencecchen Nov 3, 2026
19dd118
fix(cmux-tui-core): the lane's surface read loads the routes before i…
lawrencecchen Nov 3, 2026
24dd4c6
Merge branch 'fix/p0-typing-s1-relay-peers' into fix/p0-typing-s1-cre…
lawrencecchen Nov 3, 2026
846d412
Merge branch 'fix/p0-typing-s1-creating-input' into fix/p0-typing-s1-…
lawrencecchen Nov 3, 2026
4672321
Merge commit 'ff04cf67b63' into fix/p0-typing-s1-admission
lawrencecchen Nov 3, 2026
c4b9ddb
Merge #34014's squash (4bb12b86ecd) into fix/p0-typing-s1-admission: …
lawrencecchen Nov 3, 2026
e2a052e
Merge feat-cmux-next (8880dae3652e) into fix/p0-typing-s1-admission
lawrencecchen Nov 3, 2026
eaef70c
test(cmux-tui-core): a key does not wait for another writer's blocked…
lawrencecchen Nov 4, 2026
331fe49
fix(cmux-tui-core): a person's key to a hosted terminal goes to the t…
lawrencecchen Nov 4, 2026
4bf5af5
test(cmux-tui-core): after a respawn, keys skip the sizing lock again…
lawrencecchen Nov 4, 2026
5fe770d
fix(cmux-tui-core): a sizing entry follows its terminal across a resp…
lawrencecchen Nov 4, 2026
89414e0
fix(cmux-tui-core): name the input port module without super:: where …
lawrencecchen Nov 4, 2026
8c109c2
Merge branch 'fix/p0-typing-s1-sizing' into fix/p0-typing-s1-input-port
lawrencecchen Nov 4, 2026
ae5c3cf
Merge branch 'feat-cmux-next' into fix/p0-typing-s1-admission
cmux-bot[bot] Nov 4, 2026
8ccc6f0
test(cmux-tui-core): a settled size-state read takes the session lock…
lawrencecchen Nov 4, 2026
8701dbd
fix(cmux-tui-core): a sizing entry looks its surface up only when it …
lawrencecchen Nov 4, 2026
417fab7
fix(cmux-tui-core): the input port's order test can fail; the port co…
lawrencecchen Nov 4, 2026
6d8a5b4
Merge branch 'fix/p0-typing-s1-sizing' into fix/p0-typing-s1-input-port
lawrencecchen Nov 4, 2026
8d40cfa
Merge the squashed S1.3 head (ae5c3cf1bfd, #34146) into fix/p0-typing…
lawrencecchen Nov 4, 2026
e87f276
Merge feat-cmux-next just before #34146's squash (530dd321d1b) into f…
lawrencecchen Nov 4, 2026
1a457f2
Merge #34146's squash (dfcfac60c35) into fix/p0-typing-s1-relay-peers…
lawrencecchen Nov 4, 2026
7e8aa9e
Merge feat-cmux-next (0ae1413031d4) into fix/p0-typing-s1-relay-peers
lawrencecchen Nov 4, 2026
7b90f43
Merge branch 'fix/p0-typing-s1-relay-peers' into fix/p0-typing-s1-cre…
lawrencecchen Nov 4, 2026
b86e5e8
Merge branch 'fix/p0-typing-s1-creating-input' into fix/p0-typing-s1-…
lawrencecchen Nov 4, 2026
4c8d169
Merge remote-tracking branch 'origin/fix/p0-typing-s1-sizing' into fi…
lawrencecchen Nov 4, 2026
7c90c29
fix(cmux-tui-core): the input port refuses only when the host reader …
lawrencecchen Nov 4, 2026
2dd59d8
fix(cmux-tui-core): a key to a failed host link is refused at once on…
lawrencecchen Nov 4, 2026
a3d307a
Merge feat-cmux-next just before #34171's squash into fix/p0-typing-s…
lawrencecchen Nov 4, 2026
6234806
Merge #34171's squash (e039c73b64a) into fix/p0-typing-s1-creating-in…
lawrencecchen Nov 4, 2026
5aa3ec4
Merge feat-cmux-next (2e0b6acc3593) into fix/p0-typing-s1-creating-input
lawrencecchen Nov 4, 2026
f738b2e
Merge remote-tracking branch 'origin/fix/p0-typing-s1-creating-input'…
lawrencecchen Nov 4, 2026
5586fb8
fix(cmux-tui-core): a failed reconnect completion on a failed link al…
lawrencecchen Nov 4, 2026
8b9f34a
test(cmux-tui-core): a frame sent after a key the input port holds mu…
lawrencecchen Nov 4, 2026
91af73e
fix(cmux-tui-core): every other frame to a hosted terminal's host goe…
lawrencecchen Nov 4, 2026
1f3d590
Merge remote-tracking branch 'origin/fix/p0-typing-s1-sizing' into fi…
lawrencecchen Nov 4, 2026
5efb06b
Merge remote-tracking branch 'origin/fix/p0-typing-s1-input-port' int…
lawrencecchen Nov 4, 2026
189ba58
test(cmux-tui-core): an automation write answers after the input port…
lawrencecchen Nov 4, 2026
9afa300
fix(cmux-tui-core): on the input lane only the person's keys answer b…
lawrencecchen Nov 4, 2026
d11ff86
Merge feat-cmux-next before the S1.4 squash (e4a43f9def2) into fix/p0…
lawrencecchen Nov 4, 2026
d34f488
Merge the S1.4 squash (eaa00d9434d, #34174) into fix/p0-typing-s1-siz…
lawrencecchen Nov 4, 2026
f36b4be
Merge feat-cmux-next (2fa7c631ef7c) into fix/p0-typing-s1-sizing
lawrencecchen Nov 4, 2026
0df96cf
Merge the S1.5 landed head (f36b4be84a0) into fix/p0-typing-s1-input-…
lawrencecchen Nov 4, 2026
9f1f00c
Merge feat-cmux-next before the S1.5 squash (9f173d32a4c) into fix/p0…
lawrencecchen Nov 4, 2026
6801a09
Merge the S1.5 squash (053e300dcaa, #34261) into fix/p0-typing-s1-inp…
lawrencecchen Nov 4, 2026
fbb7767
Merge feat-cmux-next (191537144e99) into fix/p0-typing-s1-input-port
lawrencecchen Nov 4, 2026
3a0af4b
Merge origin/fix/p0-typing-s1-input-port (fbb7767ad5e) into fix/p0-ty…
lawrencecchen Nov 4, 2026
2133c10
test(cmux-tui-core): the input port's reconnect and reply rules from …
lawrencecchen Nov 4, 2026
bbc44c2
fix(cmux-tui-core): a route publication ends the input port's reconne…
lawrencecchen Nov 4, 2026
74d2ad1
Merge feat-cmux-next before the S1.6a squash (45901128f15) into fix/p…
lawrencecchen Nov 4, 2026
c7a4a91
Merge the S1.6a squash (1fa9cfc37ed, #34544) into fix/p0-typing-s1-in…
lawrencecchen Nov 4, 2026
ee8896f
Merge feat-cmux-next (3961c3e98ecc) into fix/p0-typing-s1-input-port-…
lawrencecchen Nov 4, 2026
f9c75f1
test(cmux-tui-core): the review-1 port tests name Frame and input_may…
lawrencecchen Nov 4, 2026
b6b0b37
test(cmux-tui-core): a write waiting for a reconnect fails when the r…
lawrencecchen Nov 4, 2026
860edd9
fix(cmux-tui-core): the reader's end, give-up and the owner's detach …
lawrencecchen Nov 4, 2026
1e84ed3
Merge feat-cmux-next (d1decc7c4474) into fix/p0-typing-s1-6c-control-…
lawrencecchen Nov 4, 2026
b005df2
Merge branch 'feat-cmux-next' into fix/p0-typing-s1-input-port-order
cmux-bot[bot] Nov 4, 2026
0b22b65
Merge the landed S1.6b head (b005df28c6a, #35082) into fix/p0-typing-…
lawrencecchen Nov 4, 2026
73837b4
Merge feat-cmux-next before the S1.6b squash (a0cd00b1b87) into fix/p…
lawrencecchen Nov 4, 2026
6821f76
Merge the S1.6b squash (65d098cc113, #35082) into fix/p0-typing-s1-6c…
lawrencecchen Nov 4, 2026
32f0563
Merge feat-cmux-next (ca738232f32f) into fix/p0-typing-s1-6c-control-…
lawrencecchen Nov 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmux-tui/crates/cmux-tui-core/src/surface/hosted_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ struct WakeInputOnReaderEnd(Arc<Surface>);
impl Drop for WakeInputOnReaderEnd {
fn drop(&mut self) {
if let Some(pty) = self.0.as_pty() {
pty.stream_progress.notify();
pty.reader_gave_up();
// A reader that gave up on its host leaves the terminal `failed`:
// push the tab here, with no lock held, so clients show it.
if matches!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ impl HostedReader {
if !reconnect_mux.terminal_host_connection_lost(surface.id, &identity) {
pty.host_connection_state
.store(TerminalHostConnectionState::Failed as u8, Ordering::Release);
pty.stream_progress.notify();
pty.reader_gave_up();
return None;
}
// A resync's reconnect that failed is a loss clients have not heard of.
Expand Down
19 changes: 16 additions & 3 deletions cmux-tui/crates/cmux-tui-core/src/surface/input_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ struct PortState {
/// The writer waits for a reconnect: a barrier does not wait then; the connection it would write to is gone, and
/// the install it waits for may need the runtime lock that barrier's caller holds.
reconnecting: bool,
/// Moves each time the terminal publishes a route ([`InputPort::route_changed`]): ends the writer's reconnect wait.
/// Moves each time the terminal publishes a route ([`InputPort::route_changed`]) or will install none
/// ([`InputPort::reconnect_ended`]): ends the writer's reconnect wait.
route_epoch: u64,
/// Tests: the writer waits here before it writes each frame.
#[cfg(test)]
Expand Down Expand Up @@ -314,6 +315,17 @@ impl InputPort {
self.shared.changed.notify_all();
}

/// The terminal will install no host connection for now (its reader ended or gave the host up, or its owner
/// detaches it): a writer waiting for a reconnect checks again at once and fails its frame, as the old path's
/// write failed then (#35082 review 2).
pub(crate) fn reconnect_ended(&self) {
let mut state = self.shared.state.lock().unwrap_or_else(PoisonError::into_inner);
state.route_epoch = state.route_epoch.wrapping_add(1);
state.reconnecting = false;
drop(state);
self.shared.changed.notify_all();
}

fn enqueue_frame(
&self,
terminal: &Arc<PtyTerminalRuntime>,
Expand Down Expand Up @@ -517,8 +529,9 @@ fn run_writer(shared: &PortShared, terminal: &Weak<PtyTerminalRuntime>) {
}
}

/// Waits until the terminal publishes a route after the one seen at epoch `observed` ([`InputPort::route_changed`]),
/// until the port closes or until `deadline`; true when a new route came. Marks the writer waiting for a reconnect
/// Waits until the terminal publishes a route after the one seen at epoch `observed` ([`InputPort::route_changed`])
/// or ends its reconnect ([`InputPort::reconnect_ended`]), until the port closes or until `deadline`; true when the
/// epoch moved (the caller checks the route and the terminal's state again). Marks the writer waiting for a reconnect
/// meanwhile, so barriers do not wait for it (a new route clears the mark at once).
fn wait_for_route(shared: &PortShared, observed: u64, deadline: Instant) -> bool {
let mut state = shared.state.lock().unwrap_or_else(PoisonError::into_inner);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl TerminalHostReconnectBackoff {
host.disconnect();
}
// Input waiting out the reconnect (surface/input.rs) fails now.
pty.stream_progress.notify();
pty.reader_gave_up();
return false;
};
#[cfg(test)]
Expand Down
12 changes: 12 additions & 0 deletions cmux-tui/crates/cmux-tui-core/src/surface/shutdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ impl Surface {
// racing termination by reconnecting a Surface that no longer
// exists in the mux topology.
pty.owner_detaching.store(true, Ordering::Release);
// A port writer waiting for a reconnect fails its frame now (#35082 review 2); only the port
// lock, which no holder of the runtime lock waits for.
pty.input_port.reconnect_ended();
{
let mut runtime = pty.runtime.lock().unwrap();
match &mut *runtime {
Expand Down Expand Up @@ -175,6 +178,9 @@ impl Surface {
Surface::Pty(pty) => {
if let PtyRuntime::Hosted(host) = &*pty.runtime.lock().unwrap() {
pty.owner_detaching.store(true, Ordering::Release);
// A port writer waiting for a reconnect fails its frame now (#35082 review 2); only the port
// lock, which no holder of the runtime lock waits for.
pty.input_port.reconnect_ended();
host.disconnect();
return;
}
Expand All @@ -187,6 +193,9 @@ impl Surface {
&*pty.runtime.lock().unwrap()
{
pty.owner_detaching.store(true, Ordering::Release);
// A port writer waiting for a reconnect fails its frame now (#35082 review 2); only the port
// lock, which no holder of the runtime lock waits for.
pty.input_port.reconnect_ended();
holder.detach();
return;
}
Expand All @@ -208,6 +217,9 @@ impl Surface {
let runtime = pty.runtime.lock().unwrap();
if let PtyRuntime::Hosted(host) = &*runtime {
pty.owner_detaching.store(true, Ordering::Release);
// A port writer waiting for a reconnect fails its frame now (#35082 review 2); only the port
// lock, which no holder of the runtime lock waits for.
pty.input_port.reconnect_ended();
let mut gap = None;
if host.supports_journal_detach_fence()
&& let Err(error) = host.detach_for_daemon_shutdown_until(deadline)
Expand Down
8 changes: 8 additions & 0 deletions cmux-tui/crates/cmux-tui-core/src/surface/terminal_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ impl PtyTerminalRuntime {
self.input_port.route_changed();
}

/// The reader will install no host connection now (it ended, gave the host up, or the owner detaches the
/// terminal): input that waits out a lost connection fails now, as the old path's write failed then.
pub(super) fn reader_gave_up(&self) {
self.stream_progress.notify();
// The input port's writer waits on its own route epoch, not on the stream (#35082 review 2).
self.input_port.reconnect_ended();
}

/// The reader shut the current host connection down and keeps retrying (`Failed`): input is refused until a
/// replacement connection is installed ([`Self::replace_runtime`] publishes its route), as the old path's write
/// to the shut connection failed. Takes the held runtime so it is ordered with every replacement.
Expand Down
30 changes: 30 additions & 0 deletions cmux-tui/crates/cmux-tui-core/src/surface/tests/hosted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1161,3 +1161,33 @@ fn the_creating_input_flush_keeps_input_that_may_have_been_written() {
let written = input_payloads(&frames_until_quiet(&mut host, Duration::from_millis(500)));
assert_eq!(written, vec![b"a".to_vec()], "the input the flush kept reached the host once");
}

/// R-DAEMON-STATE S1.6c (#35082 review 2, should-do 1): a write that waits out a lost host connection fails as soon
/// as the reader gives that host up (the link is `Failed` and nothing will install a replacement), as the old path's
/// write failed then, not at its own reconnect deadline. The reader's wake reached only `stream_progress`, which the
/// port's writer no longer waits on.
#[cfg(unix)]
#[test]
fn a_write_waiting_for_a_reconnect_fails_when_the_reader_gives_up() {
let (_mux, surface, _host) = hosted_typing_surface("hosted-input-port-give-up", true);
let pty = surface.as_pty().expect("a terminal");
pty.input_port.set_reconnect_wait_for_test(Duration::from_secs(5));
pty.input_route.store(Some(Arc::new(dead_host_route())));
std::thread::scope(|scope| {
let writing = scope.spawn(|| {
let started = Instant::now();
(surface.write_bytes(b"g"), started.elapsed())
});
wait_until("the write waits for a reconnect", || pty.input_port.reconnecting_for_test());
// The reader gives the host up, as `wait_or_fail` does at the end of its budget.
pty.host_connection_state
.store(TerminalHostConnectionState::Failed as u8, Ordering::Release);
pty.reader_gave_up();
let (written, took) = writing.join().unwrap();
written.expect_err("a write to a host the reader gave up on answered ok");
assert!(
took < Duration::from_secs(2),
"a write to a host the reader gave up on waited {took:?}, its reconnect deadline, not the give-up"
);
});
}
Loading