diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ninep/ctl.zig | 41 | ||||
| -rw-r--r-- | src/ninep/events.zig | 2 | ||||
| -rw-r--r-- | src/ninep/tree.zig | 4 |
3 files changed, 18 insertions, 29 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 4afc82dc..5fe04f5c 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -374,11 +374,13 @@ pub fn readPane(p: *Pardes, req: Req, pane: *Pane) Reply { /// `lock` and `unlock` are acme's (editors/acme/xfid.c:603-611), so that a /// client can make an edit of several writes to addr and data without /// another's landing in between. As in acme the lock binds only the clients -/// that ask for it: a `lock` while another open holds it waits (parked, as -/// acme's qlock blocks the writer) until that open unlocks or closes; a -/// write to any other file is never refused for it, nor is the person at -/// the keyboard. It belongs to the open that wrote it, which alone may -/// `unlock`, and closing that open or the pane gives it up. +/// that ask for it; a write to any other file is never refused for it, nor +/// is the person at the keyboard. It belongs to the open that wrote it, +/// which alone may `unlock`, and closing that open or the pane gives it up. +/// Where acme's qlock blocks a second locker, this refuses it at once with +/// `file in use`, and the client tries again: through a kernel or FUSE +/// mount a blocked write holds the file's writes, the holder's own `unlock` +/// and close among them, so a waiting lock would never be let in. pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply { // This open's handle is what holds the lock; a write that came on no // writable open (the editor's own) has none. @@ -407,16 +409,13 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply { if (errno != 0) return Reply.fail(req.tag, errno); } else if (std.mem.eql(u8, line, "lock")) { if (mine == null) return tree.failText(req.tag, E.INVAL, tree.e_bad_ctl); - if (other) return .{ .tag = req.tag, .status = .again }; + if (other) return tree.failText(req.tag, E.BUSY, tree.e_in_use); held = true; if (apply) pane.fs.lock = mine; } else if (std.mem.eql(u8, line, "unlock")) { if (!held) return tree.failText(req.tag, E.INVAL, e_not_locked); held = false; - if (apply) { - pane.fs.lock = null; - pardes.turn.parked = true; // a `lock` waiting goes again - } + if (apply) pane.fs.lock = null; } else if (!apply) { if (checkBuiltin(p, req, line, .pane)) |refusal| return refusal; } else if (runBuiltin(p, req, p.paneBySerial(serial).?, line)) |refusal| { @@ -630,7 +629,7 @@ test "the root ctl reads the settings as a write takes them, and takes the sessi } -test "a second lock waits until the holder unlocks or closes, and binds nobody else" { +test "a second lock is refused until the holder unlocks or closes, and binds nobody else" { const p = try withFile(testing.allocator, "one\ntwo\n"); defer p.deinit(); const serial = serialOf(p); @@ -649,37 +648,31 @@ test "a second lock waits until the holder unlocks or closes, and binds nobody e try testing.expectEqual(Status.ok, w.ctl(p, ctl_node, a, "lock\n").reply.status); try testing.expectEqual(Status.ok, w.ctl(p, ctl_node, a, "lock\n").reply.status); - // Another open's lock waits, and its unlock is refused; writes to the - // pane's other files are not. - try testing.expectEqual(Status.again, w.ctl(p, ctl_node, b, "lock\n").reply.status); + // Another open's lock is refused at once, and so is its unlock; writes + // to the pane's other files are not. + try testing.expectEqualStrings(tree.e_in_use, w.ctl(p, ctl_node, b, "lock\n").reply.ename); + try testing.expectEqual(E.BUSY, w.ctl(p, ctl_node, b, "lock\n").errno()); try testing.expectEqualStrings(e_not_locked, w.ctl(p, ctl_node, b, "unlock\n").reply.ename); try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .addr), "1").reply.status); try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .data), "ONE\n").reply.status); try testing.expectEqualStrings("ONE\ntwo\n", p.panes[0].?.file.?.content); - // Unlocking lets the parked lock go again, and now it takes the lock. - pardes.turn.parked = false; + // Once the holder unlocks, the other's try takes it. try testing.expectEqual(Status.ok, w.ctl(p, ctl_node, a, "unlock\n").reply.status); - try testing.expect(pardes.turn.parked); try testing.expectEqual(Status.ok, w.ctl(p, ctl_node, b, "lock\n").reply.status); - try testing.expectEqual(Status.again, w.ctl(p, ctl_node, a, "lock\nunlock\n").reply.status); + try testing.expectEqual(E.BUSY, w.ctl(p, ctl_node, a, "lock\nunlock\n").errno()); // Closing the holder gives it up. - pardes.turn.parked = false; _ = call(p, .{ .tag = 5, .op = .release, .node = ctl_node, .handle = b }); - try testing.expect(pardes.turn.parked); try testing.expectEqual(Status.ok, w.ctl(p, ctl_node, a, "lock\nunlock\nlock\n").reply.status); try testing.expectEqual(E.INVAL, w.ctl(p, ctl_node, a, "unlock\nunlock\n").errno()); try testing.expectEqual(@as(?u32, a), p.panes[0].?.fs.lock); - // The lock lives and dies with the pane: closing it wakes whoever waits. + // The lock lives and dies with the pane. const other = try th.newPane(p); const other_ctl = Node.of(other, .ctl); const c = call(p, .{ .tag = 6, .op = .open, .node = other_ctl, .omode = 1 }).reply.handle; try testing.expectEqual(Status.ok, w.ctl(p, other_ctl, c, "lock\n").reply.status); - pardes.turn.parked = false; try testing.expectEqual(Status.ok, th.rmdir(p, Node.of(other, .dir)).reply.status); - try testing.expect(pardes.turn.parked); - pardes.turn.parked = false; try testing.expectEqual(E.NOENT, w.ctl(p, other_ctl, c, "lock\n").errno()); for ([_]u64{ ctl_node, other_ctl }, [_]u32{ a, c }) |node, h| _ = call(p, .{ .tag = 7, .op = .release, .node = node, .handle = h }); diff --git a/src/ninep/events.zig b/src/ninep/events.zig index 34355e5c..172f5c00 100644 --- a/src/ninep/events.zig +++ b/src/ninep/events.zig @@ -116,8 +116,6 @@ pub fn noteRetire(p: *Pardes, pane: *Pane) void { tree.pty.shellGone(p, pane, false); p.fs.news = true; // a read held on its event or pty/data hears it went p.fs.listeners -|= pane.fs.readers; - // A write parked on its lock goes again, and finds the pane gone. - if (pane.fs.lock != null) pardes.turn.parked = true; if (pane.fs.unannounced) { pane.fs.unannounced = false; return; diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index a83b0193..f118e4b4 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -669,11 +669,9 @@ fn releaseHandle(p: *Pardes, req: Req) void { pn.fs.run = null; }, // Closing the open that holds the lock gives it up, as acme's clunk - // of its ctlfid does (editors/acme/xfid.c:211); a write parked on - // `lock` goes again. + // of its ctlfid does (editors/acme/xfid.c:211). .ctl => if (pn.fs.lock == req.handle) { pn.fs.lock = null; - pardes.turn.parked = true; }, .snapshot, .log => {}, }; |
