summaryrefslogtreecommitdiff
path: root/src/ninep/events.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/ninep/events.zig')
-rw-r--r--src/ninep/events.zig67
1 files changed, 35 insertions, 32 deletions
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index 78945c5c..8f66cf3a 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -169,37 +169,42 @@ fn pushLog(p: *Pardes, record: []u8) void {
p.fs.news = true;
}
+/// What an open of /log holds.
+pub const LogOpen = struct {
+ /// The ring's text when it was opened.
+ bytes: []const u8 = &.{},
+ /// This open waits for records after `bytes`, from `next`.
+ follow: bool = false,
+ next: u64 = 0,
+ /// How much of record `next` a short read already took.
+ part: u32 = 0,
+ /// How much of `bytes` this open has read. Reads walk it as a stream once
+ /// following and ignore their offset, since `exec 3<>log; echo follow
+ /// >&3` leaves the shared offset past the word it wrote.
+ pos: u64 = 0,
+};
+
/// An open freezes the ring's text, so `cat log` answers what happened lately
/// and ends; `next` is where a follower picks up after it.
-pub fn openLog(p: *Pardes, req: Req) Reply {
- for (&p.fs.snapshots, 0..) |*slot, i| {
- if (slot.node != 0) continue;
- var text: std.ArrayList(u8) = .empty;
- var at = p.fs.log.head;
- const items = p.fs.log.buf.items;
- var n: u64 = 0;
- while (at + 4 <= items.len) : (n += 1) {
- const len = std.mem.readInt(u32, items[at..][0..4], .little);
- text.appendSlice(p.gpa, items[at + 4 ..][0..len]) catch {
- text.deinit(p.gpa);
- return Reply.fail(req.tag, E.NOMEM);
- };
- at += 4 + len;
- }
- const bytes = text.toOwnedSlice(p.gpa) catch return Reply.fail(req.tag, E.NOMEM);
- slot.* = .{ .node = req.node, .bytes = bytes, .next = p.fs.log.dropped + n };
- return .{ .tag = req.tag, .handle = @intCast(i + 1) };
+pub fn freezeLog(p: *Pardes) !LogOpen {
+ var text: std.ArrayList(u8) = .empty;
+ errdefer text.deinit(p.gpa);
+ var at = p.fs.log.head;
+ const items = p.fs.log.buf.items;
+ var n: u64 = 0;
+ while (at + 4 <= items.len) : (n += 1) {
+ const len = std.mem.readInt(u32, items[at..][0..4], .little);
+ try text.appendSlice(p.gpa, items[at + 4 ..][0..len]);
+ at += 4 + len;
}
- return Reply.fail(req.tag, E.NFILE);
+ return .{ .bytes = try text.toOwnedSlice(p.gpa), .next = p.fs.log.dropped + n };
}
/// Past the frozen text, a following open reads newer records one per read,
-/// parked until one arrives; one that fell behind the ring hears `lost N`.
+/// held until one arrives; one that fell behind the ring hears `lost N`.
pub fn readLog(p: *Pardes, req: Req) Reply {
- if (req.handle == 0 or req.handle > p.fs.snapshots.len) return Reply.fail(req.tag, E.INVAL);
- const slot = &p.fs.snapshots[req.handle - 1];
- if (slot.node != req.node) return Reply.fail(req.tag, E.INVAL);
- const frozen = slot.bytes orelse &.{};
+ const slot = &(tree.openOf(p, req) orelse return Reply.fail(req.tag, E.INVAL)).what.log;
+ const frozen = slot.bytes;
const out = p.fs.stage(p.gpa);
if (!slot.follow or slot.pos < frozen.len) {
// Until `follow`, the frozen text is a file like any other: `tail -n`
@@ -243,9 +248,7 @@ pub fn readLog(p: *Pardes, req: Req) Reply {
/// `follow`, written to an open of the log, makes that open wait for newer
/// records instead of ending; closing it is the way back.
pub fn writeLog(p: *Pardes, req: Req) Reply {
- if (req.handle == 0 or req.handle > p.fs.snapshots.len) return Reply.fail(req.tag, E.INVAL);
- const slot = &p.fs.snapshots[req.handle - 1];
- if (slot.node != req.node) return Reply.fail(req.tag, E.INVAL);
+ 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);
slot.follow = true;
return .{ .tag = req.tag, .written = @intCast(req.data.len) };
@@ -386,7 +389,7 @@ pub fn noteAction(
pub fn notePtyOutput(p: *Pardes, id: usize, bytes: []const u8) void {
if (id >= MAX_PANES or bytes.len == 0) return;
const pf = &(p.panes[id] orelse return).fs;
- if (pf.pty_readers == 0) return;
+ if (!pf.pty_reader) return;
var off: usize = 0;
while (off < bytes.len) {
const n = @min(bytes.len - off, queue_cap / 2);
@@ -745,7 +748,7 @@ test "the log records whether or not anyone reads, and an open that follows wait
try testing.expect(std.mem.startsWith(u8, lost, "lost "));
try testing.expect(std.mem.startsWith(u8, rdf.next(p, log, fh, frozen).bytes, "msg "));
_ = call(p, .{ .tag = 10, .op = .release, .node = log, .handle = fh });
- for (p.fs.snapshots) |slot| try testing.expect(slot.node == 0);
+ for (p.fs.opens) |o| try testing.expect(o.node == 0);
// A record bigger than the whole ring is cut to fit, not dropped with
// everything else pushed out ahead of it.
@@ -759,12 +762,12 @@ test "the log records whether or not anyone reads, and an open that follows wait
_ = call(p, .{ .tag = 13, .op = .release, .node = log, .handle = cut.reply.handle });
}
-test "opens of the log share the snapshot slots, and a closed one frees its slot" {
+test "opens of the log share the open records, and a closed one frees its record" {
const gpa = testing.allocator;
const p = try withFile(gpa, "x\n");
defer p.deinit();
const log = @intFromEnum(tree.TopFile.log);
- var handles: [tree.screen.snapshot_slots]u32 = undefined;
+ var handles: [tree.open_slots]u32 = undefined;
for (&handles) |*h| h.* = call(p, .{ .tag = 1, .op = .open, .node = log }).reply.handle;
try testing.expectEqual(E.NFILE, call(p, .{ .tag = 2, .op = .open, .node = log }).errno());
_ = call(p, .{ .tag = 3, .op = .release, .node = log, .handle = handles[5] });
@@ -772,5 +775,5 @@ test "opens of the log share the snapshot slots, and a closed one frees its slot
try testing.expectEqual(Status.ok, again.reply.status);
handles[5] = again.reply.handle;
for (handles) |h| _ = call(p, .{ .tag = 5, .op = .release, .node = log, .handle = h });
- for (p.fs.snapshots) |slot| try testing.expect(slot.node == 0);
+ for (p.fs.opens) |o| try testing.expect(o.node == 0);
}