summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/ctl.zig14
-rw-r--r--src/ninep/events.zig4
-rw-r--r--src/ninep/pane.zig10
-rw-r--r--src/ninep/pty.zig21
-rw-r--r--src/ninep/tree.zig15
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);