From 28c814aa5cfea23e8950ef007916ffc5d089288d Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sun, 27 Sep 2026 20:09:02 -0300 Subject: Hold a read that has nothing yet and answer it when its file has news A following log, event, pty/data or a pty/run before its answer used to answer .again and wait for a wakeAll, which only the parked-write path asked for, so the band-aid had every queue push set turn.parked. Now the core keeps such a read (ctlfs.hold) and, as the turn is given up after anything that queued a record, ran a command out or closed a pane, answers it on its own connection, the way factotum answers the log reads it keeps and acme an event read. Only a read the engine still holds parked is answered, because cloud9 tells the backend nothing of a Tflush, so a flushed read spends no record. Co-Authored-By: Claude Opus 5.5 --- src/ninep/pty.zig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/ninep/pty.zig') diff --git a/src/ninep/pty.zig b/src/ninep/pty.zig index a689d87f..2a38f1f0 100644 --- a/src/ninep/pty.zig +++ b/src/ninep/pty.zig @@ -162,10 +162,10 @@ pub fn openRun(p: *Pardes, req: Req, serial: u32) Reply { return Reply.fail(req.tag, E.NFILE); } -fn answer(slot: *Run, comptime fmt: []const u8, args: anytype) void { +fn answer(p: *Pardes, slot: *Run, comptime fmt: []const u8, args: anytype) void { slot.len = @intCast((std.fmt.bufPrint(&slot.answer, fmt ++ "\n", args) catch unreachable).len); slot.phase = .done; - pardes.turn.parked = true; // wake the read waiting on it + p.fs.news = true; // the read held on it can be answered } pub fn writeRun(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { @@ -180,7 +180,7 @@ pub fn writeRun(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { const state = pane.terminal orelse return tree.failText(req.tag, E.INVAL, e_bad_line); const marks = &state.stream.handler; if (pf.unmarked) { - answer(slot, "error no prompt marks", .{}); + answer(p, slot, "error no prompt marks", .{}); } else if (pf.run != null or marks.phase != .input or !pardes.panes.Terminal.promptInputEmpty(pane) or p.hostTtyTaken(id)) { @@ -189,7 +189,7 @@ pub fn writeRun(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { // the middle of it. The phase is pardes's own marks: a nested // shell's prompt (ssh, a shell with its own integration) looks like // an empty prompt to ghostty but is not the shell this run knows. - answer(slot, "busy", .{}); + answer(p, slot, "busy", .{}); } else { slot.want = marks.started +% 1; slot.prompts = marks.prompts; @@ -285,7 +285,7 @@ pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { .kept_line => {}, } if (!empty) return; - answer(slot, "error not run", .{}); + answer(p, slot, "error not run", .{}); pf.run = null; return; } @@ -305,11 +305,11 @@ pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { // The header is the whole first line, so a count there can never be // mistaken for output; `cut` with no count: its start scrolled away. if (printed == null or scrolled_out) - answer(slot, "exit {d} cut", .{status}) + answer(p, slot, "exit {d} cut", .{status}) else if (cut > 0) - answer(slot, "exit {d} cut {d}", .{ status, cut }) + answer(p, slot, "exit {d} cut {d}", .{ status, cut }) else - answer(slot, "exit {d}", .{status}); + answer(p, slot, "exit {d}", .{status}); if (keep.len > 0) { const nl = @intFromBool(keep[keep.len - 1] != '\n'); if (p.gpa.alloc(u8, keep.len + nl)) |owned| { @@ -326,7 +326,7 @@ pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { pub fn shellGone(p: *Pardes, id: usize) void { const pf = &p.fs.panes[id]; const idx = pf.run orelse return; - answer(&p.fs.runs[idx], "error shell gone", .{}); + answer(p, &p.fs.runs[idx], "error shell gone", .{}); pf.run = null; } -- cgit v1.3