* [ANNOUNCE] mises v0.40.0: dup, dup2 and pipe
@ 2026-07-23 17:35 kaguya3311
0 siblings, 0 replies; only message in thread
From: kaguya3311 @ 2026-07-23 17:35 UTC (permalink / raw)
To: [email protected]
[-- Attachment #1.1.1: Type: text/plain, Size: 4192 bytes --]
Mises v0.40.0 is tagged. It adds the three descriptor syscalls thatredirection is made of, which finishes the kernel side of `>` and `|`.
Since v0.39 an fd has been a counted reference to an open file
description, and a child inherits its parent's whole table across
spawn. What was still missing was any way for the parent to change
what a number *means* before it spawns, and any description that is a
channel between two processes rather than a file.
0x50 dup(fd) -> newfd second name, lowest free fd
0x51 dup2(oldfd, newfd) -> newfd second name at a chosen fd
0x52 pipe(fds_ptr, flags) -> 0 [read_fd, write_fd], two LE u32
These open a new 0x50..=0x5F "descriptor" band rather than spending the
file band's last three numbers. The narrow reason is that 0x10..=0x1F
had exactly three left and link(2) still wants one. The real reason is
that none of these takes a path: they operate on the fd table and on
open file descriptions, which is a different kind of object -- the same
argument the mount band and the system-control band each made. The file
band was going to overflow regardless, since lseek, fstat, fcntl,
ftruncate, ioctl and poll are all fd-shaped and path-free; those are
reserved in the new band. The ABI is append-only, so this was settled
before any code was written.
dup is literally the existing Tables::link. dup2 is the same with a
caller-chosen fd, plus a release of what it displaced -- a real close,
flush included. Two orderings in it are load-bearing: the new reference
is taken before the displaced one is dropped, and old == new returns
early having closed nothing but still validating. Get either wrong and
dup2 destroys the fd it was asked to preserve.
A pipe lives in a new kern::pipe, which is a second leaf lock that the
fd layer never nests under: retiring a pipe end wakes the scheduler, so
every close path extracts the dying description under the fd lock, drop
the guard, and only then retires it. A pipe's readers/writers count
DESCRIPTIONS, not fds, which is the whole of end-of-file -- a dup2 or a
spawn adds an fd to an existing description and moves neither count.
Buffered bytes drain before EOF is reported. A write with no reader is
EPIPE; there is no SIGPIPE to send.
Userland can now deadlock the machine, which is new. A parent that waits
for a child blocked on a pipe nobody drains is the classic pipeline
mistake and Mises now has it, for the same reason POSIX does.
Tested by user/pipetest and tests/pipe.rs. The probe re-launches itself
with a role operand so writer and reader are one binary; the children
write to fd 1 and mention no pipe, and the parent re-emits what it read
back out *framed*, so "arrived through the pipe" and "went straight to
the terminal" are two different strings -- which lets the test require
the raw form to be absent. It moves 12 KiB through a 4 KiB pipe with
both ends blocking.
Six bug-injections were run against it. Three were worth writing down:
- A dup2(fd, fd) check on stderr proves nothing. fds 0/1/2 share one
description, so a close-then-link dup2 drops it from three
references to two and the fd keeps working by accident. Confirmed
by running the injection with both forms present.
- A leaked pipe is invisible to every behavioural assertion. One
whose end counts are right but which is never freed reads, writes,
blocks and reports EOF perfectly. Only a count() hook sees the page
it pins each time.
- The drive-loop bound had to be sized from the measured healthy cost
(4 spins). At the neighbouring tests' 50,000,000 the deadlock
injection became a 60-second QEMU timeout with no output; at
200,000 it fails a named assertion in under a second.
Rationale, alternatives and invariants: ADR-0051.
`>` and `|` are now entirely user/sh's work, with no further kernel
change. There is no O_CLOEXEC yet, so a pipeline must spawn its writer
first and drain before it waits; both are documented.
Suite: 357 [ok] across 58 test binaries (60 Running lines), 0 failed,
0 warnings.
kaguya
[-- Attachment #1.1.2.1: Type: text/html, Size: 6501 bytes --]
[-- Attachment #1.2: publickey - [email protected] - 0x09BF9D29.asc --]
[-- Type: application/pgp-keys, Size: 856 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 343 bytes --]
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-07-23 17:35 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-23 17:35 [ANNOUNCE] mises v0.40.0: dup, dup2 and pipe kaguya3311
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox