summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.agents/skills/pardes-9p/SKILL.md3
-rw-r--r--docs/fs.md5
-rw-r--r--src/ninep/events.zig22
3 files changed, 27 insertions, 3 deletions
diff --git a/.agents/skills/pardes-9p/SKILL.md b/.agents/skills/pardes-9p/SKILL.md
index e363b7cb..f0ae51e4 100644
--- a/.agents/skills/pardes-9p/SKILL.md
+++ b/.agents/skills/pardes-9p/SKILL.md
@@ -65,7 +65,8 @@ $m/pane/<n>/pty/run write one line, read `exit N` + its output, or `busy` / `e
$m/log recent events, then EOF: new|del|rename|save <serial> <name>, msg <serial|-> <text>,
run <serial> <line>, exit <serial> <N|?>, send <from> <to> <repl-id>,
dump|restore <path>, err <serial|-> <file>: <why>
- (exec 3<>$m/log; echo follow >&3; cat <&3 waits for new ones; tail -f does not;
+ (exec 3<>$m/log; echo follow >&3; cat <&3 replays what is there, then waits for new
+ ones; `echo follow new` waits for new ones only; tail -f does not wait;
while read -r line <&3; do ...; done loses nothing within a session: a Restore
hangs every connection up, so dial again, and restart a 9ns mount)
$m/screen the rendered screen as JSON, frozen per open
diff --git a/docs/fs.md b/docs/fs.md
index 04cef2f9..7c13d223 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -721,7 +721,10 @@ text, a cluster to a cell, is apart from this.) A click in a
tag gives offsets into the whole tag as `tag` reads it, the path first.) An
open freezes the ring's text the way `/screen` freezes a frame: reads walk it
and end. Writing `follow` to that same open makes reads past it wait for the
-next record, one per read; a follower the ring outran reads `lost N` first.
+next record, one per read, after first reading all that the open froze (the
+ring's whole history, up to 64 KiB); `follow new` skips that and waits for
+what comes after, as `tail -n0 -f` does. A follower the ring outran reads
+`lost N` first.
Closing the open is the only way back, as with rio's `consctl`. A follower
misses nothing within a session only: a Restore hangs its connection up
(and a 9ns mount with it, which must be restarted), so it dials again and
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index a3f8c278..1067b60e 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -371,7 +371,12 @@ pub fn readLog(p: *Pardes, req: Req) Reply {
/// records instead of ending; closing it is the way back.
pub fn writeLog(p: *Pardes, req: Req) Reply {
const slot = &(tree.openOf(p, req) orelse return Reply.fail(req.tag, E.INVAL)).what.log;
- if (!std.mem.eql(u8, std.mem.trim(u8, req.data, " \t\r\n"), "follow")) return Reply.fail(req.tag, E.INVAL);
+ const word = std.mem.trim(u8, req.data, " \t\r\n");
+ // `follow` reads what the open froze, then waits; `follow new` skips
+ // what was there and waits for what comes after, as tail -n0 -f does.
+ if (std.mem.eql(u8, word, "follow new")) {
+ slot.pos = slot.bytes.len;
+ } else if (!std.mem.eql(u8, word, "follow")) return Reply.fail(req.tag, E.INVAL);
slot.follow = true;
return .{ .tag = req.tag, .written = @intCast(req.data.len) };
}
@@ -1110,3 +1115,18 @@ test "a click in the body reports the offsets of the word it takes, so the recor
_ = call(p, .{ .tag = 3, .op = .release, .node = event, .handle = h });
}
}
+
+test "follow new skips what the log held and waits for what comes after" {
+ const p = try withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ p.update(.tick); // the pane's `new` is logged first
+ while (p.nextEffect()) |_| {}
+ p.setMessage(0, "before the open");
+ const log = @intFromEnum(tree.TopFile.log);
+ const h = call(p, .{ .tag = 1, .op = .open, .node = log }).reply.handle;
+ try testing.expectEqual(Status.ok, call(p, .{ .tag = 2, .op = .write, .node = log, .handle = h, .data = "follow new\n" }).reply.status);
+ try testing.expectEqual(Status.again, call(p, .{ .tag = 3, .op = .read, .node = log, .handle = h, .size = 4096 }).reply.status);
+ p.setMessage(0, "after it");
+ try testing.expect(std.mem.endsWith(u8, call(p, .{ .tag = 4, .op = .read, .node = log, .handle = h, .size = 4096 }).bytes, " after it\n"));
+ _ = call(p, .{ .tag = 5, .op = .release, .node = log, .handle = h });
+}