Blocking `flush()` on a full pty freezes the entire render loop, and there is no non-blocking / async escape hatch
Description
Terminal::draw() / Terminal::flush() writes synchronously to fd 1 (the pty). When the terminal emulator stops draining the pty's output buffer (a backgrounded tab, a dead multiplexer pane, a frozen SSH session), that buffer fills and the next write() blocks forever.
The entire interactive TUI runs on a single render thread (tokio::block_on). When that thread blocks on a pty write, the UI freezes completely: cursor, input, animations, and scroll stop.
This is the "TUI stuck" failure mode.
To Reproduce
- Run a fullscreen TUI that renders on a single blocking thread.
- Background or disconnect the terminal so the pty output buffer is not drained (e.g.
kill -STOPthe terminal emulator, or detach a tmux pane whose parent is blocked). - Trigger any frame larger than the kernel pty buffer (~a few KB on most systems).
- The render thread parks in the kernel on
write()and never returns; the UI is frozen.
A minimal reproduction: build Terminal<CrosstermBackend<Stdout>>, put the writer into a state where the pty buffer is full, then call terminal.draw(...) with a frame larger than the available buffer. The call blocks indefinitely.
Expected behavior
A TUI should be able to render without risking a hard, unrecoverable thread block on a wedged pty. The render loop must be able to detect a stall and exit cleanly while keeping the live server session resumable.
Environment
- OS: macOS / Linux
- Terminal Emulator: kitty, ghostty, Apple Terminal, tmux, etc.
- Crate version: ratatui 0.30.0
- Backend: crossterm
Additional context
This issue is distinct from ratatui #2483 / PR #2485 (CPR cursor-query racing with stdin on resize). That problem was about get_cursor_position() calls blocking on stdin replies during draw(). This issue is about flush() blocking on a full pty buffer.
Source: ratatui/ratatui