summaryrefslogtreecommitdiff
path: root/src/ninep/events.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 03:08:56 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commitcf2415cc6527cb92898694aae88fd84d54aac28c (patch)
tree115e40da5efd3041754a6c10a2790f3868b14c39 /src/ninep/events.zig
parent80ed1bf997117a36b9d0e10744de736e84d96e7f (diff)
downloadpardes-cf2415cc6527cb92898694aae88fd84d54aac28c.tar.gz
pardes-cf2415cc6527cb92898694aae88fd84d54aac28c.zip
A failed builtin's err replaces its msg, and (xN) is a running total
A builtin failing a ctl write logged its Verbose announcement, its words as a msg, and the write's err, so the same failure again never repeated the newest record and never collapsed. Its err alone is logged now (the msg is still shown and kept in +Messages). A repeat of a record a follower has read is a new line with the running total, (xN) being N in all. fs.md states both rules. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/events.zig')
-rw-r--r--src/ninep/events.zig49
1 files changed, 30 insertions, 19 deletions
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index c54d8454..e0e66800 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -154,16 +154,7 @@ pub fn noteMessage(p: *Pardes, serial: u32, text: []const u8) void {
// A builtin's own word for what it did stands in for its Verbose
// announcement just before (`Undo` then `Undo: nothing to undo`), so
// that 1000 Undos with nothing left are one line, counted, as err's are.
- if (newest(p)) |last| if (!followerRead(p, last.seq) and std.mem.startsWith(u8, last.text, "msg ")) {
- const rest = last.text["msg ".len..];
- const sp = std.mem.indexOfScalar(u8, rest, ' ') orelse rest.len;
- const word = rest[@min(sp + 1, rest.len)..];
- var who: [16]u8 = undefined;
- const mine = if (serial == 0) "-" else std.fmt.bufPrint(&who, "{d}", .{serial}) catch "";
- if (std.mem.eql(u8, rest[0..sp], mine) and word.len > 0 and std.mem.indexOfScalar(u8, word, ' ') == null and
- text.len > word.len and std.mem.startsWith(u8, text, word) and text[word.len] == ':')
- p.fs.log.buf.shrinkRetainingCapacity(last.at);
- };
+ dropAnnouncement(p, serial, text);
pushCounting(p, (if (serial == 0)
std.fmt.bufPrint(&buf, "msg - {s}\n", .{text})
else
@@ -218,8 +209,27 @@ pub fn noteError(p: *Pardes, req: Req, reply: Reply) void {
pushCounting(p, record);
}
+/// Drops the newest record when it is the Verbose announcement (`msg
+/// <serial> <Word>`) of the builtin whose words `text` are (`Word: ...`):
+/// what the builtin said, or the err its failure logs, takes its place.
+/// Not one a follower has read.
+pub fn dropAnnouncement(p: *Pardes, serial: u32, text: []const u8) void {
+ const last = newest(p) orelse return;
+ if (followerRead(p, last.seq) or !std.mem.startsWith(u8, last.text, "msg ")) return;
+ const rest = last.text["msg ".len..];
+ const sp = std.mem.indexOfScalar(u8, rest, ' ') orelse rest.len;
+ const word = rest[@min(sp + 1, rest.len)..];
+ var who: [16]u8 = undefined;
+ const mine = if (serial == 0) "-" else std.fmt.bufPrint(&who, "{d}", .{serial}) catch "";
+ if (std.mem.eql(u8, rest[0..sp], mine) and word.len > 0 and std.mem.indexOfScalar(u8, word, ' ') == null and
+ text.len > word.len and std.mem.startsWith(u8, text, word) and text[word.len] == ':')
+ p.fs.log.buf.shrinkRetainingCapacity(last.at);
+}
+
/// Pushes `record`, or, when it repeats the newest record word for word,
-/// counts that one instead: `<record> (x2)`.
+/// counts that one instead: `<record> (x2)`, the count being every time it
+/// was said. One a follower has already read is not rewritten: the repeat
+/// is a new line carrying the running count, `(x3)`.
fn pushCounting(p: *Pardes, record: []u8) void {
// A client retrying a write that fails the same way would fill the ring
// with one line, so a repeat of the newest record is that record counted,
@@ -234,10 +244,10 @@ fn pushCounting(p: *Pardes, record: []u8) void {
said = said[0..x];
} else |_| {}
};
- if (!std.mem.eql(u8, said, record[0 .. record.len - 1]) or followerRead(p, last.seq)) return pushLog(p, record);
+ if (!std.mem.eql(u8, said, record[0 .. record.len - 1])) return pushLog(p, record);
var counted: [pardes.Messages.LoggedMessage.cap + 64]u8 = undefined;
const again = std.fmt.bufPrint(&counted, "{s} (x{d})\n", .{ said, times +| 1 }) catch return;
- p.fs.log.buf.shrinkRetainingCapacity(last.at);
+ if (!followerRead(p, last.seq)) p.fs.log.buf.shrinkRetainingCapacity(last.at);
pushLog(p, again);
}
@@ -900,7 +910,7 @@ test "the log records whether or not anyone reads, and an open that follows wait
}
try testing.expectEqualStrings(try std.fmt.bufPrint(&expected, "msg {d} same failure\n", .{said_by}), pieced[0..got]);
p.setMessage(0, "same failure");
- try testing.expectEqualStrings(try std.fmt.bufPrint(&expected, "msg {d} same failure\n", .{said_by}), rdf.next(p, log, fh, frozen).bytes);
+ try testing.expectEqualStrings(try std.fmt.bufPrint(&expected, "msg {d} same failure (x2)\n", .{said_by}), rdf.next(p, log, fh, frozen).bytes);
// A follower the ring outran hears how much it missed, then carries on.
var filler: [200]u8 = @splat('x');
@@ -951,19 +961,20 @@ test "a refused or failed write is an err record in the log, saying which file a
// A write that succeeds says nothing.
_ = wr(p, Node.of(serial, .addr), "#0");
try testing.expectEqual(Status.again, call(p, .{ .tag = 5, .op = .read, .node = log, .handle = f, .off = frozen, .size = 4096 }).reply.status);
- // A follower that has read an err waits on its repeat: a line of its own.
+ // A follower that has read an err waits on its repeat: a line of its
+ // own, with the running count.
_ = wr(p, Node.of(serial, .addr), "/nowhere/");
try testing.expectEqualStrings(try std.fmt.bufPrint(&want, "err {d} addr: no match for regexp\n", .{serial}), next.record(p, f, frozen));
_ = wr(p, Node.of(serial, .addr), "/nowhere/");
- try testing.expectEqualStrings(try std.fmt.bufPrint(&want, "err {d} addr: no match for regexp\n", .{serial}), next.record(p, f, frozen));
+ try testing.expectEqualStrings(try std.fmt.bufPrint(&want, "err {d} addr: no match for regexp (x2)\n", .{serial}), next.record(p, f, frozen));
_ = call(p, .{ .tag = 6, .op = .release, .node = log, .handle = f });
- // With the follower gone, repeats are the newest record counted: the
- // one it read, and three more.
+ // With the follower gone, repeats are the newest record counted on:
+ // its two, and three more.
for (0..3) |_| _ = wr(p, Node.of(serial, .addr), "/elsewhere/");
const g = call(p, .{ .tag = 7, .op = .open, .node = log }).reply.handle;
const text = call(p, .{ .tag = 8, .op = .read, .node = log, .handle = g, .size = 1 << 16 }).bytes;
- try testing.expect(std.mem.endsWith(u8, text, try std.fmt.bufPrint(&want, "regexp\nerr {d} addr: no match for regexp (x4)\n", .{serial})));
+ try testing.expect(std.mem.endsWith(u8, text, try std.fmt.bufPrint(&want, "regexp\nerr {d} addr: no match for regexp (x5)\n", .{serial})));
_ = call(p, .{ .tag = 9, .op = .release, .node = log, .handle = g });
}