termio: bound POSIX read-ahead on non-Darwin

The pipelined POSIX pty reader uses multiple large gather buffers to avoid
stalling on Darwin, where pty reads are capped around 1KiB. On Linux this can
let bulk terminal producers run too far ahead of terminal parsing/rendering.

Frame-style terminal apps such as DOOM-fire can then report very high producer
FPS while Ghostty displays stale frames from the buffered stream.

Keep the Darwin-tuned 4 x 64KiB pipeline, but reduce non-Darwin read-ahead to
2 x 8KiB so the pty can apply backpressure before multiple frames are queued.
This commit is contained in:
Ēriks Remess
2026-07-07 20:24:06 +03:00
parent 160c3c69ea
commit bed47168ca

View File

@@ -1281,15 +1281,21 @@ pub const ReadThread = struct {
/// stages. The gather stage can run at most this many batches
/// ahead of the parse stage before it blocks, which (via the
/// kernel pty queue) is also what preserves flow control to the
/// child. Empirically chosen through measurements on an M4 Max.
/// Less than 4 there are minor slowdowns, above 4 there are no
/// improvements.
const buffer_count = 4;
/// child.
///
/// The Darwin value was empirically chosen through measurements on
/// an M4 Max. Less than 4 there are minor slowdowns, above 4 there
/// are no improvements.
///
/// Other POSIX platforms keep fewer batches in flight so terminal
/// parsing applies backpressure to bulk writers before frame-style
/// output can queue stale frames.
const buffer_count = if (builtin.os.tag.isDarwin()) 4 else 2;
/// The capacity of each gather buffer. One batch is also the unit
/// of work the parse stage does per terminal lock acquisition, so
/// this bounds both gather latency and lock hold time.
const buffer_capacity = 64 * 1024;
const buffer_capacity = if (builtin.os.tag.isDarwin()) 64 * 1024 else 8 * 1024;
/// How many gathered bytes mark a stream as saturated. The macOS
/// kernel tty output queue hands the master at most about 1 KiB