diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 00:40:03 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 6732ce6edcf1562d3e126ed02f96466ab07c498e (patch) | |
| tree | 77c68f4357b335e67b34f19e248f1ea00240e0eb /src/ninep | |
| parent | fad6bf0cc7cc2cb579d9ad03147a7014906f6b92 (diff) | |
| download | pardes-6732ce6edcf1562d3e126ed02f96466ab07c498e.tar.gz pardes-6732ce6edcf1562d3e126ed02f96466ab07c498e.zip | |
A desktop session holds 64 panes, and every route that finds none free says so
Sixteen panes was far too few for a real session. MAX_PANES is now 64 on a
desktop and stays 16 on the board; the 48 more slots cost about 3.7 KB each
in fixed tables (the core 1.33 to 1.45 MB, its surface 19 to 72 KB), and a
pane itself (84 KB and its terminal) is allocated only when it opens. At the
cap, pane/new fails with `pane/new: no pane slots: 64 max` and an err
record rather than a bare ENFILE, a 9P look or exec that would open a pane
fails its write with the same words and reads back empty, and New, Tty,
Tutor and Alt-n say it on the message row (their ctl writes fail with it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep')
| -rw-r--r-- | src/ninep/ctl.zig | 7 | ||||
| -rw-r--r-- | src/ninep/tree.zig | 31 |
2 files changed, 37 insertions, 1 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 11de3c1c..325fc8c8 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -138,7 +138,14 @@ pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool) Reply { if (!apply) continue; const id = if (serial) |s| p.paneBySerial(s) orelse break else p.active; if (p.panes[id] == null) return Reply.fail(req.tag, E.NOENT); + p.fs.no_pane_slot = false; run(p, id, exec, text); + // A pane it would have opened had no slot: the write fails, and + // a look reads back nothing. + if (p.fs.no_pane_slot) { + p.fs.results_len = 0; + return tree.failText(req.tag, E.IO, p.fs.ename[0..p.fs.no_pane_slot_len]); + } } } return .{ .tag = req.tag, .written = @intCast(req.data.len) }; diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index 3be75960..c4ade1f7 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -664,7 +664,7 @@ fn open(p: *Pardes, req: Req, target: Target) Reply { // pane it made, so reading it twice answers the same one and // closing it leaves the pane alone. .new => { - const slot = p.freeSlot() orelse return Reply.fail(req.tag, E.NFILE); + const slot = p.freeSlot() orelse return failText(req.tag, E.NFILE, std.fmt.bufPrint(&p.fs.ename, "pane/new: " ++ pardes.Messages.no_pane_slots, .{pardes.MAX_PANES}) catch "no pane slots"); p.newScratchFor(p.active, .fs); const made = p.panes[slot] orelse return Reply.fail(req.tag, E.NFILE); return .{ .tag = req.tag, .handle = made.serial }; @@ -1284,3 +1284,32 @@ test "editor paths resolve to the same nodes the wire serves" { try testing.expect(resolveSelf(p, "pane/new/body") == null); try testing.expectEqual(before, p.next_serial); } + +test "at the pane cap, pane/new, look and New each say so, and look reads back empty" { + const p = try th.withFile(testing.allocator, "x\n"); + defer p.deinit(); + const new = @intFromEnum(TopFile.new); + while (p.freeSlot() != null) try testing.expectEqual(Status.ok, call(p, .{ .tag = 1, .op = .open, .node = new }).reply.status); + var want: [64]u8 = undefined; + const said = try std.fmt.bufPrint(&want, "no pane slots: {d} max", .{pardes.MAX_PANES}); + const refused = call(p, .{ .tag = 2, .op = .open, .node = new }); + try testing.expectEqual(E.NFILE, refused.errno()); + try testing.expect(std.mem.endsWith(u8, refused.reply.ename, said)); + try testing.expect(th.logHas(p, said)); + // A look that would open a pane fails its write, and says the same. + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.writeFile(testing.io, .{ .sub_path = "f.txt", .data = "f\n" }); + var dir_buf: [4096]u8 = undefined; + const dir = dir_buf[0..try tmp.dir.realPath(testing.io, &dir_buf)]; + var line: [4200]u8 = undefined; + const look_node = @intFromEnum(TopFile.look); + const looked = th.wr(p, look_node, try std.fmt.bufPrint(&line, "{s}/f.txt\n", .{dir})); + try testing.expect(looked.errno() != 0); + try testing.expect(std.mem.indexOf(u8, looked.reply.ename, said) != null); + try testing.expectEqualStrings("", th.rd(p, look_node, 0, 64).bytes); + // New from a pane's ctl fails as well. + const ctl_node = Node.of(p.panes[0].?.serial, .ctl); + const made = th.wr(p, ctl_node, "New\n"); + try testing.expect(std.mem.indexOf(u8, made.reply.ename, said) != null); +} |
