Skip to content

Flaky under parallel load: a command typed right after Ctrl+C on a waiting read can fail to run #436

Open

Description

@raiseCatError

Observed (once)

While verifying #426 (draft #433), one of 16 parallel copies of tests/inputOwnershipLive.test.ts (4 at a time, macOS 27, M2, 8 GB, load 3–6) failed:

live zsh: a command typed right after a read returns, or right after Ctrl+C, reaches the shell once, in order → timed out waiting for the command after Ctrl+C to run

The test runs read -r reply, waits 600 ms, sends Ctrl+C, types echo Y >> <marker> at 20 ms per key and presses Enter, then waits up to 15 s for that command's record.

Not reproduced

18/18 targeted runs on the #433 branch (12 of them 4× in parallel, load up to 16) and 18/18 on dev a8b383c2. #433 does not touch this path: typing within 2 s of Ctrl+C is type-ahead (justStopped) and never held for a look.

Possible mechanism (unconfirmed)

Right after Ctrl+C, Enter sends the composer line straight to the PTY while the interrupted command is still formally running (toProgram when justStopped()). If the shell has not yet acted on SIGINT when that line arrives, the still-waiting read can take it as its answer, so the line never runs as a command. A real terminal has the same race for type-ahead; NMSh could instead hold that Enter until the prompt marker returns (bounded), which would also make the test deterministic.

To do

Reproduce with the queue entries and the shell's received input recorded before changing behavior. Related: #426, #356.

Activity

  1. added a commit that references this issue on Oct 18, 2026
  2. raiseCatError commented on Oct 18, 2026

    @raiseCatError
    OwnerAuthor

    Reproduced and explained; fix in draft #437 (part of #431). The guess in the issue (the read taking the line) was wrong.

    An instrumented copy of the test (8 at a time, load ~30, never committed) reproduced it on unmodified dev: the command that ran was the typed line with its beginning missing (olumes/…/once, cho Y >> …, exit 127). A frontend log of every input-state change and hand-over showed two mechanisms:

    1. A late report of the question Ctrl+C ended (sampled before the interrupt) re-marked the post-Ctrl+C type-ahead as a reply; the prompt then discarded it mid-typing.
    2. The shell's own prompt hooks read as the command waiting. After any command, zsh/Bash run their prompt hooks before the prompt marker; a slow hook holds the terminal, idle, like a builtin read. This needs neither Ctrl+C nor load: a slow prompt hook of the person's reproduces it at load 3.

    Fix: stale pre-stop requests are dropped in the frontend, and each shell's first prompt hook prints an authenticated done marker after which the watch samples nothing until the next command. Regressions fail on dev and pass on the branch; full npm run verify green. Limits (zsh's single precmd function and Fish's other postexec handlers run before the marker; a tiny read-ordering window remains) are in the PR. This does not claim the whole input-race family is solved; keeping the issue open for physical QA.

  3. raiseCatError commented on Oct 19, 2026

    @raiseCatError
    OwnerAuthor

    v0.22 post-QA status (candidate fa7ec665). Physical QA A10 (prompt hooks and Ctrl+C, Ghostty + zsh) passed on 68c1cbb7. The Fish and Bash passes (B10, C10) were not run.

    Both explained mechanisms stay fixed. On the integrated candidate: ctrlCTypeAhead (deterministic), promptHookLive 2/2 (zsh, Bash), typingAfterRead 3/3 (zsh, Bash, Fish) and inputOwnershipLive 6/6 all pass; CI green.

    Remaining, as documented: zsh's single precmd function and Fish's other fish_postexec handlers run before NMSh's done marker, so a slow hook there can still be read as the command waiting. Not changed for v0.22: running NMSh's marker before a person's own precmd would mean renaming their function. The wider race family isn't declared solved; keeping this open.

  4. raiseCatError commented on Oct 19, 2026

    @raiseCatError
    OwnerAuthor

    Reassessed with #457 (e8dd44e4). The shell's done marker now reaches windows in the input state. A command typed and entered during a slow prompt hook, after the command was reported done, runs once at the verified prompt. A new live test covers zsh and Bash. Lines typed right after Ctrl+C are held until the prompt is verified, instead of being written while the stopped command still owns the terminal. If the program survives the stop, the line goes to it. Remaining, now a display limit only: zsh's single precmd and Fish's other fish_postexec handlers run before the done marker, so a slow hook there can still read as "waiting for input". Typing during it is held or taken back, never run. Open for physical QA (R6).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    candidate:v0.22Candidate for the v0.22 release; final scope decided by the maintainertype:bugBug or defect

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions