summaryrefslogtreecommitdiff
path: root/src/acmefs.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/acmefs.zig')
-rw-r--r--src/acmefs.zig23
1 files changed, 22 insertions, 1 deletions
diff --git a/src/acmefs.zig b/src/acmefs.zig
index 21beb7f6..dc2780fe 100644
--- a/src/acmefs.zig
+++ b/src/acmefs.zig
@@ -690,7 +690,28 @@ pub fn notePtyOutput(p: *Pardes, id: usize, bytes: []const u8) void {
if (id >= MAX_PANES or bytes.len == 0) return;
const pf = &p.fs.panes[id];
if (pf.pty_readers == 0) return;
- pf.pty_out.push(p.gpa, bytes);
+ // SPLIT, because a record larger than `queue_cap - 4` can never be
+ // admitted: `Queue.push`'s eviction loop pops until `peek()` is null —
+ // destroying every unread byte the script was still owed — and then drops
+ // the new record too, silently.
+ //
+ // That is not a theoretical size. Every host reads a pty master with a
+ // 64 KiB buffer (`pty_chunk` in detached/server.zig, `[0x10000]u8` in
+ // tty.zig, gui.zig and macos.zig) and a single read really does return
+ // 65536 on Linux — measured. So a pane running a build or a `cat` of
+ // anything large produces exactly the record that empties the queue,
+ // repeatedly, for as long as a script holds `pty/data` open.
+ //
+ // The `event` queue never met this because its records are a few dozen
+ // bytes; `pty/data` inherited the cap without inheriting that property.
+ // Half the cap per record, so a full queue is at least two records and the
+ // eviction loop always has something to evict.
+ var off: usize = 0;
+ while (off < bytes.len) {
+ const n = @min(bytes.len - off, queue_cap / 2);
+ pf.pty_out.push(p.gpa, bytes[off..][0..n]);
+ off += n;
+ }
}
// ============================================================================