diff options
Diffstat (limited to 'src/ninep/events.zig')
| -rw-r--r-- | src/ninep/events.zig | 67 |
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); } |
