Skip to content

fix(configure): re-assert cooked terminal mode every tick during the wait loop#234

Merged
hyoshi merged 2 commits into
mainfrom
fix/configure-reassert-cooked-mode
Jun 12, 2026
Merged

fix(configure): re-assert cooked terminal mode every tick during the wait loop#234
hyoshi merged 2 commits into
mainfrom
fix/configure-reassert-cooked-mode

Conversation

@hyoshi

@hyoshi hyoshi commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #227 (PR #230): Ctrl+C could still go dead during a configure session. The merged fix forced cooked mode once before blocking on the stop event, but a configure action handled on the HTTP thread can run plugin code that re-flips the TTY into raw mode (ISIG off) while the main thread is already blocked — a one-shot pre-wait fix cannot recover from that, and the operator is stranded again. Reproduced in the field right after using the configure UI.

Fix

run_configure_wizard now re-asserts cooked mode on a 1-second cadence instead of once: the single stop_event.wait(timeout_seconds) becomes a deadline-bounded loop that waits in min(_COOKED_REASSERT_SECONDS, remaining) slices and calls force_cooked_mode(fd) every tick. Any mid-session raw-mode leak self-heals within one tick, so Ctrl+C reliably delivers SIGINT for the whole session.

Properties (review-verified):

  • Stop responsiveness unchanged: stop_event.wait returns immediately when /api/shutdown or the signal handler sets the event — no full-tick delay.
  • Timeout never overshoots: deadline computed once from time.monotonic(); the final slice is clamped to the residual; timeout_seconds <= 0 exits without spinning.
  • The per-tick tcgetattr/tcsetattr pair costs microseconds and is OR-only (never clears bits), so an already-cooked terminal is untouched; non-TTY stdin stays a no-op.

Verification

Real-pty end-to-end repro: spawn the wizard on a pty, flip the pty to raw (ISIG/ICANON/ECHO cleared) from another thread mid-wait, send \x03 after >1 tick — SIGINT is delivered, the stop handler fires, and the wizard shuts down cleanly.

Test plan

  • tests/test_web_server.py — new test_reasserts_cooked_mode_during_wait (shrinks the tick to 0.05s, asserts ≥4 force calls over a 0.4s wait; the mock is what is counted, so the loop is exercised identically on Windows); existing one-shot test relaxed to ≥1
  • 30 tests pass in test_web_server.py; ruff + black clean
  • CI green on the PR

@hyoshi
hyoshi merged commit cb3088c into main Jun 12, 2026
9 checks passed
@hyoshi
hyoshi deleted the fix/configure-reassert-cooked-mode branch June 12, 2026 23:21
@hyoshi hyoshi mentioned this pull request Jun 12, 2026
hyoshi added a commit that referenced this pull request Jun 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant