From eb2428115588004a8bc04a09d45fb1104f727882 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 13:58:36 -0300 Subject: Unattended messages expire in the detached session's loop, not as a 9P request is served serveFs expired them, but a read is answered while a step is out in a syscall, because reads change nothing, and this one changed pane messages and needs_frame from under the step. The detached session's loop now expires them between steps, and while nobody is attached and a message is up it looks again every 100 ms rather than waiting for something to wake it. Co-Authored-By: Claude Opus 5.5 --- src/detached/server.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/detached/server.zig') diff --git a/src/detached/server.zig b/src/detached/server.zig index 6a4b5001..61fbe111 100644 --- a/src/detached/server.zig +++ b/src/detached/server.zig @@ -770,8 +770,10 @@ pub const Session = struct { const completed = s.ninep_wake.swap(false, .acq_rel) or drained; for (&s.clients) |*c| if (c.fd >= 0) s.flush(c); const regridded = s.reconcile(); - // Nobody attached, nobody to dismiss a message (expireUnattended). + // Nobody attached, nobody to dismiss a message: the loop expires + // them itself, between the editor's steps (expireUnattended). s.core.unattended = s.primary() == null; + pardes.Messages.expireUnattended(s.core); const now = monotonicMs(); var fds: [poll_slots]libc.pollfd = undefined; @@ -904,6 +906,11 @@ pub const Session = struct { due = if (due) |d| @min(d, at9) else at9; } } + // Unattended messages go on the clock: look again in a while. + if (s.core.unattended) for (s.core.panes) |slot| if (slot) |pane| if (pane.msg_len > 0) { + due = if (due) |d| @min(d, now + 100) else now + 100; + break; + }; const at = due orelse return null; return @intCast(@max(0, @min(at - now, std.math.maxInt(c_int)))); } -- cgit v1.3