fix(app): held keys for one terminal go out through one ordered send queue (P0 1193)
Summary
P0 1193 family (lead 00:2xZ, from the re-review of #29693 at bc84781571b): two typing runs held for one terminal whose view is gone could reach that terminal out of order. Each held .type run for a terminal with no view is its own daemon send. One run can need several requests (text, then a key such as Return), and between them it awaits a reply; the connection takes other work meanwhile, so a later run for the same terminal ran in between. With "ab" + Return held, then a key for another terminal, then "cd", the shell got "ab", "cd", Return.
Root fix: one ordered send queue per terminal. DaemonService.sendInOrder(_:to:_:) (CmuxNextDaemonService) runs each send to a surface after the one before it. The ticket opens at the call, as send(_:_:)'s does; a failed send is logged and the next send still runs; the queue entry clears after the last send. The held-key daemon path (CreationInputCoordinator.type) uses it. The queued task keeps its service alive, as send(_:_:) does, so its ticket always closes and a send whose connection is gone is reported as "not connected" (review FIX at a51d0a85518). CmuxNextApp: -10 lines.
Base: stacked on #29693's branch fix/p0-1193-followup-v2; after #29693 merges, the base moves to feat-cmux-next (no rebase).
Testing
Red-first: SecondCreationKeysTests.twoHeldRunsForAnEvictedTerminalKeepTheirOrder for the order fix (two creations with no early view; "ab", Return for terminal A, "x" for B, "cd" for A; A's workspace hibernates so only the daemon can type; A's shell must get 61 62 0d 63 64). OrderedSendReleaseTests.anOrderedSendWhoseServiceIsGoneFailsAndClosesItsTicket for the review FIX (two sends queued in a DaemonCommandScope, the service released before their turn; each must fail and close its ticket).
| Ref | What it is | Job | Result |
|---|---|---|---|
| fd0d7c38f21 | #29693's head + the test only | 86629ed7bb65ace8c2ab036a | the suite ran 3 times; each time only the new test failed, on its outcome: the shell got 61 62 63 64 0d ("ab", "cd", Return) |
| a51d0a85518 | the fix, before the review FIX | 49d3e24b272233904affb05d | the suite ran 3 times, 11 of 11 each; HeldKeyInputTests 3 of 3 |
| 59a874136b8 | a51d0a85518 + OrderedSendReleaseTests only (review FIX red) | aed37e4a78a8e8857543d051 | the 1 test ran and failed on its outcome: both sends whose service was gone reported success ([nil, nil]), the scope stayed busy (ticket open), 0 failures recorded (want 2) |
| ded9410c31e (this head) | the fix + the review FIX (strong capture) | f9a684a619873b48e6011967 | OrderedSendReleaseTests 1 of 1; SecondCreationKeysTests ran 3 times, 11 of 11 each; HeldKeyInputTests 3 of 3 |
Display steps (console session, CMUX_TEST_REQUIRE_GUI=1), each execution counted in the log. Compiles: head bb40d2709adffee001fb9294, red 4f7623c4b97910fc88db196b. Review-FIX compiles: head 74988f2532c6851464f29e56, red 33aaa7536438d8d83cb87c54.
Observed / Not run
Observed: the runs above. Not run: Other writers to the same terminal (its view's attachment path, TerminalSession.sendBytes, the send-key in TerminalHooks) are not in this queue; they need a live view, and were unordered with the old send too (follow-up). Other callers of DaemonService.send that write to one surface (they keep their old path; only held keys use the queue now).
Performance
No perf numbers: a correctness fix. A send to a surface now waits for the earlier send to the same surface (one reply round trip); sends to other surfaces do not wait. No work moves onto the main thread. perf claim: none.
Changelog
Fixed: Keys typed for a pane while new panes open arrive in the order they were typed, also when that pane is hidden
Proof
No capture: key order inside the daemon path; the evidence is the fleet tests above.