On 25/09/2026 14:47, Taylor R Campbell wrote:
Date: Thu, 24 Sep 2026 07:52:12 -0700
From: Collin Funk <[email protected]>
It has been a while since I looked at pipe-f2.sh, but I remember coming
to the conclusion that it was a NetBSD bug [1]. I never managed to
create a simple reproduction to report, and feel a bit bad just sending
over a long test case and asking them to fix it for me.
I would be curious to see how you came to that conclusion!
I compared ktraces of coreutils tail(1) and NetBSD's tail(1) in a
reduced test case of:
$ rm -f out exp
$ mkfifo out
$ echo 1 >out & echo=$!
$ ktrace tail -f out >exp & tail=$!
$ wait $echo
$ sleep 5
$ kill $tail
$ wait $tail
$ kdump -R | grep -v mmap | tail -20
I inserted a sleep and used kdump -R (show relative timestamps) so you
can see where the time is spent -- you'll see that it is only in the
very last read _after_ the fifo reports EOF.
Here's the output for NetBSD tail(1):
29985 29985 tail 0.000000764 RET __fstat50 0
29985 29985 tail 0.000006412 CALL read(3,0x730a164d1000,0x1000)
29985 29985 tail 0.000006292 GIO fd 3 read 2 bytes
"1\n"
29985 29985 tail 0.000000355 RET read 2
29985 29985 tail 0.000008934 CALL read(3,0x730a164d1000,0x1000)
29985 29985 tail 0.000001248 GIO fd 3 read 0 bytes
""
29985 29985 tail 0.000000207 RET read 0
29985 29985 tail 0.000001842 CALL write(1,0x730a164c0000,2)
29985 29985 tail 0.000005768 GIO fd 1 wrote 2 bytes
"1\n"
29985 29985 tail 0.000000270 RET write 2
29985 29985 tail 0.000003033 CALL kqueue
29985 29985 tail 0.000000944 RET kqueue 4
29985 29985 tail 0.000003459 CALL __kevent50(4,0x7f7fff2787a0,1,0,0,0)
29985 29985 tail 0.000001950 RET __kevent50 0
29985 29985 tail 0.000001287 CALL read(3,0x730a164d1000,0x1000)
29985 29985 tail 5.002272721 RET read RESTART
29985 29985 tail 0.000001185 PSIG SIGTERM SIG_DFL: code=SI_USER sent by
pid=5142, uid=1000)
Here's the output for coreutils tail(1);
16702 16702 tail 0.000012542 CALL open(0x7f7fffb6ad6f,0,0)
16702 16702 tail 0.000000728 NAMI "out"
16702 16702 tail 0.000003938 RET open 3
16702 16702 tail 0.000002380 CALL __fstat50(3,0x7f7fffb6a630)
16702 16702 tail 0.000000792 RET __fstat50 0
16702 16702 tail 0.000008315 CALL read(3,0x73aa41997500,0x400)
16702 16702 tail 0.000089855 GIO fd 3 read 2 bytes
"1\n"
16702 16702 tail 0.000000594 RET read 2
16702 16702 tail 0.000003812 CALL read(3,0x73aa41997500,0x400)
16702 16702 tail 0.000018985 GIO fd 3 read 0 bytes
""
16702 16702 tail 0.000000385 RET read 0
16702 16702 tail 0.000017100 CALL __fstat50(1,0x7f7fffb6a280)
16702 16702 tail 0.000001326 RET __fstat50 0
16702 16702 tail 0.000006237 CALL __fstat50(1,0x7f7fffb6a590)
16702 16702 tail 0.000000623 RET __fstat50 0
16702 16702 tail 0.000001737 CALL read(3,0x7f7fffb6a0b0,0x400)
16702 16702 tail 5.002260818 RET read RESTART
16702 16702 tail 0.000001380 PSIG SIGTERM SIG_DFL: code=SI_USER sent by
pid=29372, uid=1000)
As you can see, the process wakes up promptly to receive the zero-byte
return from read() indicating EOF in both cases, but the coreutils
tail(1) doesn't write anything after that -- it fstats stdout a couple
times, but then goes right back to reading until SIGTERM is delivered.
So this is not consistent with the hypothesis laid out at
<https://lists.gnu.org/archive/html/coreutils/2026-04/msg00129.html>
that NetBSD fails to indicate EOF promptly.
The point wasn't about the first read(),
it was about the blocking in the subsequent read().
On Linux for example we see read() always returning 0:
read(3, "x\n", 8192) = 2
read(3, "", 8192) = 0
read(3, "", 8192) = 0
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=1, tv_nsec=0})
read(3, "", 8192) = 0
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=1, tv_nsec=0})
...
Padraig