summaryrefslogtreecommitdiff
path: root/src/ninep/tree.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/ninep/tree.zig')
-rw-r--r--src/ninep/tree.zig31
1 files changed, 31 insertions, 0 deletions
diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig
index e8bcad2f..3ebea669 100644
--- a/src/ninep/tree.zig
+++ b/src/ninep/tree.zig
@@ -107,6 +107,32 @@ pub fn changesPane(req: Req) bool {
pub const out_reserve = 4 * 1024;
+/// A read that found nothing yet (`.again`), kept to be answered when what it
+/// waits on has something, the way factotum keeps its log's waiting reads
+/// and answers them on append (security/auth/factotum/log.c:4-52) and acme
+/// an event read (editors/acme/xfid.c:994). `asker` is the connection it
+/// came on, which only the listener knows (src/9p_io.zig, `answerHeld`).
+pub const Held = struct { asker: *anyopaque, req: Req };
+
+/// Keeps a read that answered `.again`. An open waits with one read at a
+/// time, as acme's window keeps one `eventx`: a newer read on the same open
+/// takes the place of the older.
+pub fn hold(p: *Pardes, req: Req, asker: *anyopaque) void {
+ var free: ?*?Held = null;
+ for (&p.fs.held) |*slot| {
+ const held = slot.* orelse {
+ if (free == null) free = slot;
+ continue;
+ };
+ if (held.req.node == req.node and held.req.handle == req.handle) {
+ slot.* = .{ .asker = asker, .req = req };
+ return;
+ }
+ }
+ // One slot per open that can wait, so a free one is always there.
+ if (free) |slot| slot.* = .{ .asker = asker, .req = req };
+}
+
// ---- nodes ----
pub const TopFile = enum(u4) {
@@ -559,6 +585,11 @@ fn remove(p: *Pardes, req: Req) Reply {
}
fn release(p: *Pardes, req: Req) Reply {
+ // A read held on this open goes with it: a hangup pays its releases
+ // before the connection's slot is reused, so none outlives its asker.
+ for (&p.fs.held) |*slot| if (slot.*) |held| {
+ if (held.req.node == req.node and held.req.handle == req.handle) slot.* = null;
+ };
// The handle's bookkeeping runs whether or not the removal is allowed.
const done = releaseHandle(p, req);
return if (req.remove) remove(p, req) else done;