From 70881bb4edd15fe918ee5fed4ed3e8922bc7fc6a Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 09:58:15 -0300 Subject: A command or terminal pane refused for room is refused before anything is logged or answered of it, and a serial is never handed out twice A command pane with no room logged `new N` and `run N ls`, was answered to its exec, then went with `del N` and no `exit N`; and sync gave N back (`next_serial -= 1`) to the next pane made, so a script holding N found another pane under it. Tty did the same. Both now take a refused placement back at once, before the run is logged or the serial answered (the pane was never announced, so it leaves no record), and fail the write with no room. The decrement is gone: gaps are fine, and a serial once said names one pane for good. Co-Authored-By: Claude Opus 5.5 --- src/ninep/ctl.zig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'src/ninep/ctl.zig') diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 40ffebc8..16fbabb4 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -4379,3 +4379,33 @@ test "a Mount dial with a network it does not know is a bad dial address, EINVAL try testing.expectEqual(E.INVAL, r.errno()); try testing.expectStringStartsWith(r.reply.ename, "Mount: bad dial address: nowhere!x"); } + +test "a command or a terminal refused for room is refused before anything is logged of it, and its serial is never handed out again" { + const p = try withFile(testing.allocator, "x\n"); + defer p.deinit(); + const exec = Node.of(serialOf(p), .exec); + var buf: [32]u8 = undefined; + for ([_][]const u8{ "ls\n", "Tty\n" }) |line| { + // Each a pane of its own until the column has no room for one more. + var before = p.next_serial; + var tries: usize = 0; + while (tries < pardes.MAX_PANES) : (tries += 1) { + before = p.next_serial; + const r = wr(p, exec, line); + p.sync(); + if (r.reply.status == .err) break; + } else return error.NeverRefused; + // Nothing said of the refused one: no new, no run, no del. + for (before + 1..p.next_serial + 1) |n| { + try testing.expect(!th.logHas(p, try std.fmt.bufPrint(&buf, "new {d} ", .{n}))); + try testing.expect(!th.logHas(p, try std.fmt.bufPrint(&buf, "run {d} ", .{n}))); + try testing.expect(!th.logHas(p, try std.fmt.bufPrint(&buf, "del {d} ", .{n}))); + } + // And the next pane made takes a serial past it. + const spent = p.next_serial; + p.update(.{ .resize = .{ .cols = 200, .rows = 200 } }); + const made = try th.newPane(p); + try testing.expect(made > spent); + p.update(.{ .resize = .{ .cols = 80, .rows = 24 } }); + } +} -- cgit v1.3