From 8598a5fbe6ffecad46891957356d47a37649e547 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 30 Sep 2026 14:41:52 -0300 Subject: A write of a multiple of 4096 bytes holds the partial line it ends with, as one that fills its Twrite does: a burst through a mount no longer runs its cut lines as two commands selfmount's 1000-line burst reported 943 lines, on main too. The log ring was not losing records: a follower opened before the burst heard exactly 943 and no `lost`, and the burst itself exited 1 with `err 1 exec: wrong #args in control message "Msg"`. `seq ... > exec` through the mount arrives as stdio's 4096-byte writes, each shorter than a Twrite. The rule that such a write is whole ran each one's cut last line at once, so both halves of the line ran as commands (hence `exit 127` records). At 12288 bytes the cut left a bare `Msg`, which was refused and failed the write and the rest of the burst. A write of a multiple of 4096 bytes is where a writer's buffer (stdio, a page cache) filled, so it may go on: its unended tail now waits for the next write or the close, like one that fills its Twrite. `printf Save > exec` stays whole at once. fs.md says so, the 9P fuzzer's model of served lines follows it, and a unit test cuts a `Msg` at 4096 bytes. A follower does not lose records silently: a ring that outruns one already reads it `lost N` first (documented, tested). selfmount now follows the log from before the burst, so it checks every line heard once, in order, or a loss said, not a fresh open of a 64 KiB ring. It passed 3 in 3 and joins the gates with the new 9ns. Co-Authored-By: Claude Opus 5.5 --- docs/fs.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/fs.md b/docs/fs.md index 146ff17b..34664343 100644 --- a/docs/fs.md +++ b/docs/fs.md @@ -156,7 +156,10 @@ effect, as acme's ctl does. Blank lines are skipped. A mount cuts a big write into pieces of at most one message (msize 64 KiB, 65536 negotiated, less the header), and each line runs once it is whole. A write that does not fill its message is -whole, so its last line runs even without a newline (`printf Save > exec`). An `Edit` whose `{` or +whole, so its last line runs even without a newline (`printf Save > exec`), +unless it is a multiple of 4096 bytes: that is where a writer's buffer (stdio, +a mount's page cache) filled and cut a line, so its tail waits for the next +write or the close. An `Edit` whose `{` or `a`/`c`/`i` text is still open waits for the next write on that open, and fails at the close if it never ends (``unmatched `{'``). A line held past 1 MiB is refused. -- cgit v1.3