fix(cmux-next): a refused write ends the daemon connection (follow-up of #28654)

Merged
LA
7d ago
+51-6

Summary

This is a follow-up of #28654 (merged). Since #28654, the heartbeat no longer closes a live daemon's connection. That opened a gap:

  • When the writer refused a write (its backlog limit, because the daemon stopped reading its socket), the transport only failed the pending requests.
  • The socket stayed open, the reader stayed in read, and no onClose came. DaemonConnection kept the dead transport as "ready", so every later request failed at once.
  • No real signal ended the connection: the writer's refusal was the signal, and nothing acted on it.

Now:

  • A refused write goes through end(), as close() does: the pending requests fail, the socket shuts down and a bridge child ends. The reader then reads EOF and posts onClose, and the owner reconnects.
  • A closed transport is never a live peer (peerProcessIsAlive checks isClosed). This only matters for a transport that closes while a heartbeat ask is out: the heartbeat already stops at a closed transport (tick returns at once), so the real fix is the end() above.

feat's heartbeat asks every interval, not after a silence. An answered ask already marks the daemon answering again, so the perf port's "a frame after a miss" rule does not apply here.

Found by the independent review of the perf port of #28654 (finding 2).

Takes back

Not applicable: this is a fix-forward follow-up of #28654. A revert of #28654 would bring back the "closed by client" P0 ("I can't even make new workspaces").

Testing

Focused command (fleet, CmuxNext package test lane), python3 /work/wt/chrome-parity-design/proof-step.py:

  • Red on the base: red --branch cp/fix-p0-refused-write-feat --ref 9fef9a6729f944264468cae63325eb6b480bcb95 --suite RefusedWriteEndsConnectionTests -- --red. Job 2f1a63e631a530988520a803: failed on the test's own assertions (2 issues, no crash):
    • RefusedWriteEndsConnectionTests.swift:31:9: Expectation failed: (ended.count → 0) == 1 ("a refused write left the connection open: no onClose ([])")
    • RefusedWriteEndsConnectionTests.swift:32:9: Expectation failed: !((transport → CmuxNextDaemon.LineTransport).peerProcessIsAlive → true → true) ("a closed transport must not report its peer alive")
  • Green on the head: green --branch cp/fix-p0-refused-write-feat --ref dc5125f97747d790c64a32084c2be8718841c92a --suite RefusedWriteEndsConnectionTests,HeartbeatLiveDaemonTests,BusyRefusalReconnectTests. Job 1e4c7b7e07deca9de9a170df: passed: RefusedWriteEndsConnectionTests (13 s), HeartbeatLiveDaemonTests (3 s), BusyRefusalReconnectTests (2 s); "All 3 suites passed", no FLAKY notice.

Observed / Not run

Changelog

Fixed: cmux reconnects to cmux-tui when cmux-tui stops reading its socket, instead of keeping a dead connection.

No capture: no UI change.