summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 13:58:36 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commiteb2428115588004a8bc04a09d45fb1104f727882 (patch)
tree5846e6f4b474bd4e900d8832b0bc635db162f092 /src
parent7da8fe5005f0fd44899a1172e763fddcd7cac4d2 (diff)
downloadpardes-eb2428115588004a8bc04a09d45fb1104f727882.tar.gz
pardes-eb2428115588004a8bc04a09d45fb1104f727882.zip
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 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/Messages.zig7
-rw-r--r--src/detached/server.zig9
-rw-r--r--src/pardes.zig1
3 files changed, 14 insertions, 3 deletions
diff --git a/src/Messages.zig b/src/Messages.zig
index 0652f974..00352afe 100644
--- a/src/Messages.zig
+++ b/src/Messages.zig
@@ -208,7 +208,9 @@ fn nowMs() i64 {
/// With no frontend attached no input dismisses a message, and a detached
/// session's /screen and messages kept what was said an hour ago: each
/// pane's lines go once its newest has been up `message_linger_ms`, on the
-/// clock, checked as the core is stepped or served.
+/// clock, checked as the core is stepped and by the detached session's loop
+/// between steps -- not as a 9P request is served, since a read is answered
+/// while a step is out and must change nothing.
/// ponytail: a pane's older lines go with its newest, not each on its own
/// clock; the render pipeline's clock will give every line its own.
pub fn expireUnattended(p: *Pardes) void {
@@ -570,7 +572,10 @@ test "with no frontend attached a message goes once it has lingered, on the cloc
// Detached with nobody attached: past its linger it is gone, and the
// line above it with it.
p.unattended = true;
+ // A read served meanwhile changes nothing; the session's loop expires.
_ = p.serveFs(.{ .tag = 1, .op = .read, .node = @intFromEnum(ctlfs.TopFile.index), .size = 64 });
+ try std.testing.expect(pane.msg_len > 0);
+ expireUnattended(p);
try std.testing.expectEqual(@as(u16, 0), pane.msg_len);
try std.testing.expectEqual(@as(u8, 0), pane.msg_older_len);
// A fresh one stays until its linger is up.
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))));
}
diff --git a/src/pardes.zig b/src/pardes.zig
index 2eab3d62..9fa747cd 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -4675,7 +4675,6 @@ pub const Pardes = struct {
/// that changes a pane costs a frame, which is why a round trip on a
/// local socket costs microseconds and not a vsync.
pub fn serveFs(p: *Pardes, req: ctlfs.Req) ctlfs.Reply {
- Messages.expireUnattended(p);
if (!ctlfs.changesPane(req)) return ctlfs.handle(p, req);
p.needs_frame = true;
p.raw_hover_intent = false;