diff options
Diffstat (limited to 'src/ninep')
| -rw-r--r-- | src/ninep/ctl.zig | 14 | ||||
| -rw-r--r-- | src/ninep/events.zig | 4 | ||||
| -rw-r--r-- | src/ninep/pane.zig | 10 | ||||
| -rw-r--r-- | src/ninep/pty.zig | 21 | ||||
| -rw-r--r-- | src/ninep/tree.zig | 15 |
5 files changed, 46 insertions, 18 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 83b1196f..a3c1ab9e 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -192,9 +192,15 @@ pub fn readPane(p: *Pardes, req: Req, pane: *Pane) Reply { /// the keyboard. It belongs to the open that wrote it, which alone may /// `unlock`, and closing that open or the pane gives it up. pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply { - // The record of this open is what holds the lock; a write that came on - // no writable open (the editor's own) has none. - const mine: ?u8 = if (tree.openOf(p, req)) |o| @intCast(o - &p.fs.opens[0]) else null; + // This open's handle is what holds the lock; a write that came on no + // writable open (the editor's own) has none. + const mine: ?u32 = if (tree.openOf(p, req)) |o| (if (o.what == .ctl) req.handle else null) else null; + // The pane keeps the holder's handle, which names that open only on + // this pane's ctl node; one that no longer does holds nothing. + if (pane.fs.lock) |h| { + const o = tree.openOf(p, .{ .tag = 0, .op = .write, .node = tree.Node.of(pane.serial, .ctl), .handle = h }); + if (o == null or o.?.what != .ctl) pane.fs.lock = null; + } const other = pane.fs.lock != null and pane.fs.lock != mine; var asked = false; // Checked whole before anything applies, so a write that must wait for @@ -337,7 +343,7 @@ test "a second lock waits until the holder unlocks or closes, and binds nobody e 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(?u8, @intCast(a - 1)), p.panes[0].?.fs.lock); + try testing.expectEqual(@as(?u32, a), p.panes[0].?.fs.lock); // The lock lives and dies with the pane: closing it wakes whoever waits. const other = try th.newPane(p); diff --git a/src/ninep/events.zig b/src/ninep/events.zig index cb31e2aa..8d5ba16c 100644 --- a/src/ninep/events.zig +++ b/src/ninep/events.zig @@ -608,6 +608,10 @@ test "a pane deleted while its event file is open leaves no suppression behind" _ = wr(p, Node.of(serial, .exec), "Del\n"); try testing.expect(p.paneBySerial(serial) == null); try testing.expectEqual(@as(u16, 0), p.fs.listeners); + // The reader's next read hears what acme says of a window gone under it. + const shut = call(p, .{ .tag = 19, .op = .read, .node = event, .handle = a.reply.handle, .size = 64 }); + try testing.expectEqual(E.IO, shut.errno()); + try testing.expectEqualStrings(tree.e_shut_down, shut.reply.ename); _ = call(p, .{ .tag = 20, .op = .release, .node = event, .handle = a.reply.handle }); _ = call(p, .{ .tag = 21, .op = .release, .node = event, .handle = b.reply.handle }); diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index 3d7834c4..24398445 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -43,11 +43,11 @@ pub const State = struct { /// An open reads pty/data, so output queues for it; a second is refused. pty_reader: bool = false, pty_out: events.Queue = .{}, - /// The open record (tree.zig) of the pty/run waiting on this shell's - /// current command. - run: ?u8 = null, - /// The open record of the ctl open that wrote `lock` (ctl.zig). - lock: ?u8 = null, + /// The handle of the pty/run open waiting on this shell's current + /// command, good only through `tree.openOf` on this pane's run node. + run: ?u32 = null, + /// The handle of the ctl open that wrote `lock` (ctl.zig), the same way. + lock: ?u32 = null, /// The host started a shell it could not teach to mark its prompts. unmarked: bool = false, /// Installed during this update; /log hears about it once the update ends diff --git a/src/ninep/pty.zig b/src/ninep/pty.zig index 936f9b73..49f52c82 100644 --- a/src/ninep/pty.zig +++ b/src/ninep/pty.zig @@ -164,7 +164,7 @@ pub fn writeRun(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { const marks = &state.stream.handler; if (pf.unmarked) { answer(p, slot, "error no prompt marks", .{}); - } else if (pf.run != null or marks.phase != .input or + } else if (waitingRun(p, pane) != null or marks.phase != .input or !pardes.panes.Terminal.promptInputEmpty(pane) or p.hostTtyTaken(id)) { // Something is running, someone has typed at the prompt, or the @@ -180,7 +180,7 @@ pub fn writeRun(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { p.emitWrite(id, line); p.emitWrite(id, "\r"); slot.phase = .sent; - pf.run = @intCast(req.handle - 1); + pf.run = req.handle; } return .{ .tag = req.tag, .written = @intCast(req.data.len) }; } @@ -217,7 +217,7 @@ pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { if (comptime !pardes.panes.Terminal.enabled) return; const state = pane.terminal orelse return; const pf = &pane.fs; - const slot = &p.fs.opens[pf.run orelse return].what.run; + const slot = waitingRun(p, pane) orelse return; const marks = &state.stream.handler; if (marks.phase != .input) return; // ponytail: a prompt redrawn before the command starts (a resize in that @@ -297,11 +297,22 @@ pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { /// The pane closed or its shell was replaced: the command's end will never /// be reported, so the run says so instead of waiting forever. pub fn shellGone(p: *Pardes, pane: *Pane) void { - const idx = pane.fs.run orelse return; - answer(p, &p.fs.opens[idx].what.run, "error shell gone", .{}); + answer(p, waitingRun(p, pane) orelse return, "error shell gone", .{}); pane.fs.run = null; } +/// The run waiting on this shell's command. The pane keeps its open's +/// handle, which names that open only on the node it was opened on, so it +/// is looked up the way a request's is; one that no longer names a run is +/// forgotten. +fn waitingRun(p: *Pardes, pane: *Pane) ?*Run { + const handle = pane.fs.run orelse return null; + if (tree.openOf(p, .{ .tag = 0, .op = .read, .node = tree.Node.of(pane.serial, .pty_run), .handle = handle })) |o| + if (o.what == .run) return &o.what.run; + pane.fs.run = null; + return null; +} + const e_bad_line = "bad command line"; pub fn readData(p: *Pardes, req: Req, pf: *pane_files.State) Reply { diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index c9e7b31a..f93ab203 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -64,6 +64,7 @@ pub const e_bad_event = "bad event syntax"; /// A second open reading a file whose reads consume: rio's word for it /// (windows/rio/xfid.c:25), which 9ns turns into EBUSY. pub const e_in_use = "file in use"; +pub const e_shut_down = "window shut down"; fn reads(omode: u8) bool { return omode & 3 != 1; // OWRITE is the one access mode that never reads @@ -136,7 +137,7 @@ pub const Open = struct { /// `asker` is the connection the read came on, which only the listener /// knows. - pub const Held = struct { asker: *anyopaque, req: Req }; + pub const Held = struct { asker: *anyopaque, req: Req, ticket: cloud9.fs.Ticket }; pub fn deinit(o: *Open, gpa: std.mem.Allocator) void { switch (o.what) { @@ -655,13 +656,13 @@ fn releaseHandle(p: *Pardes, req: Req) void { pn.fs.pty_out.clearAndFree(p.gpa); }, // The command runs on in the shell; nobody is waiting for it any more. - .run => if (pn.fs.run == @as(u8, @intCast(req.handle - 1))) { + .run => if (pn.fs.run == req.handle) { 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. - .ctl => if (pn.fs.lock == @as(u8, @intCast(req.handle - 1))) { + .ctl => if (pn.fs.lock == req.handle) { pn.fs.lock = null; pardes.turn.parked = true; }, @@ -717,7 +718,13 @@ fn read(p: *Pardes, req: Req, target: Target) Reply { .pane => |t| { // A run's answer outlives the pane it ran in. if (t.file == .pty_run and req.handle != 0) return pty.readRun(p, req); - const id = p.paneBySerial(t.serial) orelse return Reply.fail(req.tag, E.NOENT); + const id = p.paneBySerial(t.serial) orelse { + // An event or pty/data read the pane closed under: acme's + // answer to the same (editors/acme/xfid.c:1005). + if (openOf(p, req)) |o| if (o.what == .event or o.what == .pty_data) + return failText(req.tag, E.IO, e_shut_down); + return Reply.fail(req.tag, E.NOENT); + }; const pn = p.panes[id].?; if (t.file.inPty() and !pn.isTerminal()) return Reply.fail(req.tag, E.NOENT); return pane.read(p, req, id, pn, t.file); |
