summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 17:53:02 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:17 -0300
commit031989d927b07d38f0874a0b78a52f54ad285efb (patch)
tree1172d6d1afc72b03f4768030443a17cd2a6dc22d /src
parente3e605bd33fc5be56cb7ec842449718bc5337a75 (diff)
downloadpardes-031989d927b07d38f0874a0b78a52f54ad285efb.tar.gz
pardes-031989d927b07d38f0874a0b78a52f54ad285efb.zip
Writes held before the shell's clock is first seen no longer overflow its first step
A 9P write served before a detached host's first pump holds its batch stamped at time 0, so nextWake said to wake at 20 ms while advance, seeing the clock for the first time, set stepped_ns days later and counted due - stepped_ns back past zero: an integer-overflow panic. It was what the 64 KiB msize trial hit in fs-test (restore detached space, the connection closing at the first write's clunk), a race any msize can lose. A batch's wake is now never before the next frame. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/ninep/pane.zig15
-rw-r--r--src/pardes.zig6
2 files changed, 19 insertions, 2 deletions
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig
index 4327c403..bb4ebf86 100644
--- a/src/ninep/pane.zig
+++ b/src/ninep/pane.zig
@@ -1190,6 +1190,21 @@ test "an open's writes in a row are held and go in as one edit, seen by the next
try testing.expectEqualStrings("abone\n", rd(p, body, 0, 64).bytes);
}
+test "writes held before the shell's clock is first seen go in at its first step" {
+ const p = try withFile(testing.allocator, "one\n");
+ defer p.deinit();
+ const body = Node.of(serialOf(p), .body);
+ try testing.expect(!p.clock_started);
+ const h = call(p, .{ .tag = 1, .op = .open, .node = body, .omode = 1 }).reply.handle;
+ const revision = p.panes[0].?.file.?.revision;
+ _ = call(p, .{ .tag = 2, .op = .write, .node = body, .handle = h, .data = "x\n" });
+ try testing.expectEqual(@as(u64, 0), p.fs.batch.last_ns);
+ // A detached host's first step: its clock is days into the machine's.
+ p.advance(700_000 * std.time.ns_per_s);
+ flushPausedBatch(p);
+ try testing.expectEqual(revision + 1, p.panes[0].?.file.?.revision);
+}
+
test "a tag write past the limit is refused whole, naming the limit" {
const p = try withFile(testing.allocator, "x\n");
defer p.deinit();
diff --git a/src/pardes.zig b/src/pardes.zig
index c1b12a8b..cb39b5d1 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -7129,8 +7129,10 @@ pub const Pardes = struct {
/// sleep exactly as long as nothing changes.
pub fn nextWake(p: *const Pardes) ?u64 {
const next = p.stepped_ns + animation.frame_ns;
- // Held 9P writes go in when they pause: wake for it.
- if (p.fs.batch.bytes.items.len > 0) return p.fs.batch.last_ns + ctlfs.pane.batch_pause_ns;
+ // Held 9P writes go in when they pause: wake for it, never before
+ // the next frame (writes served before the shell's clock was first
+ // seen are stamped 0, which `advance` would count back from).
+ if (p.fs.batch.bytes.items.len > 0) return @max(next, p.fs.batch.last_ns + ctlfs.pane.batch_pause_ns);
if (p.chrome_animation.isActive() or p.presentation.animating()) return next;
var frames: ?u64 = null;
if (p.look_hover_wait) |waiting| {