NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: PR kern/60944: fifo opened non-blocking and set to blocking regression



> Date: Sat, 31 Oct 2026 17:08:09 -0400
> From: Christos Zoulas <christos%zoulas.com@localhost>
> 
> > On Oct 31, 2026, at 5:02 PM, Taylor R Campbell <riastradh%netbsd.org@localhost> wrote:
> > 
> >> Date: Fri, 30 Oct 2026 17:50:08 +0000 (UTC)
> >> From: Christos Zoulas <christos%zoulas.com@localhost>
> >> 
> >> 	Select should block if there are no writers on freshly opened FIFO
> >> 	that is set to blocking.
> > 
> > [citation needed]
> > 
> > I reviewed all the relevant language in POSIX.1-2024 as research for
> > PR kern/60792 (fifo: poll reader before writers?).  It clearly
> > prescribes (and other operating systems agree, and applications like
> > GNU coreutils have long relied on the assumption) that _reading_ must
> > not block -- and must immediately report EOF -- in that case:
> > 
> > 	When attempting to read from an empty pipe or FIFO:
> > 	
> > 	   If no process has the pipe open for writing, read()
> > 	   shall return 0 to indicate end-of-file.
> > 	
> > https://pubs.opengroup.org/onlinepubs/9799919799/functions/read.html
> > 
> > (This was the subject of PR kern/60789 (read on fifo without writer
> > may block), which arose because applications relying on POSIX
> > semantics were broken on NetBSD due to an accident dating back to
> > 1990 that FreeBSD fixed back in 1997.)
> > 
> > And if read would return immediately without blocking, it is
> > internally inconsistent for select to block instead of reporting
> > immediately that read would not block.
> > 
> > Applications can (and do) simply open the fifo for write if they want
> > select/poll to reliably wait until a write actually materializes.
> > 
> > (If the applications are going to rely on non-POSIX guarantees, they
> > can even save a file descriptor and open it once with O_RDWR instead
> > of twice, one with O_RDONLY and the other with O_WRONLY.  I bet this
> > works on more operating systems than the previous logic did, including
> > older NetBSD.)
> 
> I don't have a citation, but I ran the example program on linux and
> MacOS/X and they both block.

Their select/poll is inconsistent for this case: it claims read would
block, in a scenario when read is guaranteed by POSIX (and implemented
in Linux and macOS) to return immediately.

And in order to implement this behaviour which is not guaranteed by
POSIX without breaking other behaviour that _is_ guaranteed by POSIX,
we would have to restructure our fifo implementation altogether so
that it keeps per-open state instead of just per-vnode state.

Which also raises a question of how much history we need to record --
if the fifo has _ever_ been open for write, does select/poll block (in
spite of the fact that read would not), or does it only apply if the
fifo has ever been open for write _while this instance for read_ has
been open?  POSIX is silent on this, as far as I can tell, and I can
imagine arguments either way.  But either way, select/poll would be
broken because they claim read will block when it is guaranteed not
to.

For details, see PR kern/60792: fifo: poll reader before writers?
<https://gnats.NetBSD.org/60792>.

> I also don't understand how to get the behavior the program wants
> now: to be able to open a fifo for reading the control endpoint
> using NONBLOCK, set it to block again, then select on a bunch of
> fd's and service control messages when they come.

I just told you:

	Applications can (and do) simply open the fifo for write if
	they want select/poll to reliably wait until a write actually
	materializes.

This is guaranteed by POSIX.1-2024 and works reliably everywhere,
including Linux, macOS, FreeBSD, and older NetBSD.

I also just told you a simpler change for xl2tpd that is not
guaranteed by POSIX but probably works everywhere you care about:

	(If the applications are going to rely on non-POSIX
	guarantees, they can even save a file descriptor and open it
	once with O_RDWR instead of twice, one with O_RDONLY and the
	other with O_WRONLY.  I bet this works on more operating
	systems than the previous logic did, including older NetBSD.)

Note that xl2tpd already has a kooky hack to work around the behaviour
that POSIX _does_ guarantee -- once a fifo has been _closed_ for
writing, select/poll report readable (with POLLHUP) and read returns
EOF.  The only part that is unspecified is whether this happens when
the fifo is opened for read and is _not_ currently open for write at
all.

So the author arranged to close and reopen the control fifo after
every time xl2tpd reads a control message from it:

    /* Otherwise select goes nuts. Yeah, this just seems wrong */
    close (control_fd);
    open_controlfd();

If, instead, xl2tpd simply held open the control fifo for reading and
for writing, this close/reopen cycle would be unnecessary and it would
rely only on POSIX semantics as implemented consistently across all
major operating systems.



Home | Main Index | Thread Index | Old Index