kern/94772: FIFOs (named pipes) + select() == broken

Bruce Evans bde at zeta.org.au
Thu Mar 23 05:10:21 UTC 2006


The following reply was made to PR kern/94772; it has been noted by GNATS.

From: Bruce Evans <bde at zeta.org.au>
To: Oliver Fromme <olli at lurza.secnetix.de>
Cc: bug-followup at freebsd.org
Subject: Re: kern/94772: FIFOs (named pipes) + select() == broken
Date: Thu, 23 Mar 2006 16:02:54 +1100 (EST)

 On Thu, 23 Mar 2006, Bruce Evans wrote:
 
 > On Wed, 22 Mar 2006, Oliver Fromme wrote:
 >> Oliver Fromme wrote:
 >> > Bruce Evans wrote:
 
 > I intened to check the behaviour for this in my test programs but don't
 > seem to have done it.  I intended to follow Linux's behaviour even if this
 > is nonstandard.  Linux used to have some special cases including a gripe
 > in a comment about having to have them to match Sun's behaviour, but I
 > couldn't find these when I last checked.  Perhaps the difference is
 > precisely between select() and poll(), to follow the standard for select()
 > and exploit the fuzziness for poll().
 
 I added the check.  Linux-2.6.10 in fact acts as guessed above.  So the
 check for select() is for the behaviour specified by POSIX (select() on
 a read descriptor that is in nonblocking mode and is for a fifo that has
 never had a writer returns success), while the check for poll() is
 for exactly the opposite behaviour (poll() blocks instead of returning
 with POLLIN set; the test actually uses a nonblocking poll() and only
 sees checks for POLLIN not set, since a test that poll() blocks would
 be messier and I think I understand at least the FreeBSD implementation
 well enough to know that this test is equivalent).
 
 > I'll add tests for the O_NONBLOCK behaviour before mailing the
 > test for poll().
 
 First a small change to add it to the select() test:
 
 %%%
 --- select.c~	Sun Feb 12 23:42:30 2006
 +++ select.c	Thu Mar 23 13:47:23 2006
 @@ -30,7 +30,19 @@
   		err(1, "open for read");
   #endif
 -	kill(ppid, SIGUSR1);
 +	if (fd >= FD_SETSIZE)
 +		errx(1, "fd = %d too large for select()", fd);
 +
 +#ifdef NAMEDPIPE
 +	FD_ZERO(&rfds);
 +	FD_SET(fd, &rfds);
 +	tv.tv_sec = 0;
 +	tv.tv_usec = 0;
 +	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
 +		err(1, "select");
 +	if (!FD_ISSET(fd, &rfds))
 +		warnx("state 0: expected set; got clear");
 +#endif
 
 -	/* XXX should check that fd fits in rfds. */
 +	kill(ppid, SIGUSR1);
 
   	usleep(1);
 %%%
 
 poll() test:
 
 %%%
 #include <sys/poll.h>
 #include <sys/stat.h>
 
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <unistd.h>
 
 static pid_t cpid;
 static pid_t ppid;
 static volatile sig_atomic_t state;
 
 static void
 catch(int sig)
 {
  	state++;
 }
 
 #ifdef USE_POLLINIGNEOF
 /*
   * FreeBSD's POLLINIGNEOF (which causes half of the bugs when the kernel
   * uses it) can be used to fix up the broken cases 3 and 6a if the kernel
   * uses it, i.e., for named pipes but not for pipes.  Note that the sense
   * of POLLINIGNEOF is reversed when passed to the kernel -- it means
   * don't-ignore-EOF in .events and if it is set there then it means
   * not-POLLHUP in .revents.
   *
   * This leaves the following broken cases:
   * state 6 (hangup but data available) for poll on a named pipe:
   *         should have POLLIN | POLLHUP, but have POLLIN only.  In this
   *         case, we don't try POLLINIGNEOF since resulting pair of revents
   *         cannot be distinguished from the pair for a case in which POLLIN
   *         only is correct.
   * state 6a (hangup and no data available) for poll on a plain pipe:
   *         should have POLLHUP only, but have POLLIN | POLLHUP.  This is
   *         what I thought is correct, but it is not what Linux-2.6.10 does
   *         for named pipes.  FreeBSD's select() currently depends on POLLIN
   *         being set in this case, and Linux's select() acts the same as
   *         FreeBSD's select() in this case.
   * states 3 and 6a (hangup and no data available) for select on a named pipe:
   *         should have FD_SET() set as in old-FreeBSD and Linux-2.6.10, but
   *         have FD_SET() clear.  The POLLINIGNEOF changes just broke select()
   *         here.  So what was the PR (34020?) which inspired these changes
   *         about?  poll() only?  This regression test uses nonblocking mode
   *         for all polls and a timeout of 0 for all selects so that the
   *         kernel state can be seen without blocking for long.  I hope that
   *         the select() blocks iff the resulting .revents indicates that it
   *         should block (it shouldn't block if it would set POLLIN).
   */
 int
 mypoll(struct pollfd *fds, nfds_t nfds, int timeout)
 {
  	struct pollfd mypfd;
  	int r;
 
  	r = poll(fds, nfds, timeout);
  	if (nfds != 1 || timeout != 0 || fds[0].revents & POLLIN)
  		return (r);
  	mypfd = fds[0];
  	mypfd.events |= POLLINIGNEOF;
  	r = poll(&mypfd, 1, 0);
  	if (r >= 0) {
  		if (mypfd.revents &= POLLIN) {
  			mypfd.revents &= ~POLLIN;
  			mypfd.revents |= POLLHUP;
  		}
  		fds[0].revents = mypfd.revents;
  	}
  	return (r);
 }
 #define	poll(fds, nfds, timeout)	mypoll((fds), (nfds), (timeout))
 #endif
 
 static void
 child(int fd)
 {
  	struct pollfd pfd;
  	char buf[256];
 
 #ifdef NAMEDPIPE
  	pfd.fd = open("p", O_RDONLY | O_NONBLOCK);
  	if (pfd.fd < 0)
  		err(1, "open for read");
 #else
  	pfd.fd = fd;
 #endif
  	pfd.events = POLLIN;
 
 #ifdef NAMEDPIPE
  	if (poll(&pfd, 1, 0) < 0)
  		err(1, "poll");
  	if (pfd.revents != 0)
  		warnx("state 0: expected 0; got %#x", pfd.revents);
 #endif
 
  	kill(ppid, SIGUSR1);
 
  	usleep(1);
  	while (state != 1)
  		;
 #ifndef NAMEDPIPE
  	/*
  	 * The connection cannot be restablished.  Use the code that delays
  	 * the read until after the writer disconnects since that case is
  	 * more interesting.
  	 */
  	state = 4;
  	goto state4;
 #endif
  	if (poll(&pfd, 1, 0) < 0)
  		err(1, "poll");
  	if (pfd.revents != 0)
  		warnx("state 1: expected 0; got %#x", pfd.revents);
  	kill(ppid, SIGUSR1);
 
  	usleep(1);
  	while (state != 2)
  		;
  	if (poll(&pfd, 1, 0) < 0)
  		err(1, "poll");
  	if (pfd.revents != POLLIN)
  		warnx("state 2: expected POLLIN; got %#x", pfd.revents);
  	if (read(pfd.fd, buf, sizeof buf) != 1)
  		err(1, "read");
  	if (poll(&pfd, 1, 0) < 0)
  		err(1, "poll");
  	if (pfd.revents != 0)
  		warnx("state 2a: expected 0; got %#x", pfd.revents);
  	kill(ppid, SIGUSR1);
 
  	usleep(1);
  	while (state != 3)
  		;
  	if (poll(&pfd, 1, 0) < 0)
  		err(1, "poll");
  	if (pfd.revents != POLLHUP)
  		warnx("state 3: expected POLLHUP; got %#x",
  		    pfd.revents);
  	kill(ppid, SIGUSR1);
 
  	/*
  	 * Now we expect a new writer, and a new connection too since
  	 * we read all the data.  The only new point is that we didn't
  	 * start quite from scratch since the read fd is not new.  Check
  	 * startup state as above, but don't do the read as above.
  	 */
  	usleep(1);
  	while (state != 4)
  		;
 state4:
  	if (poll(&pfd, 1, 0) < 0)
  		err(1, "poll");
  	if (pfd.revents != 0)
  		warnx("state 4: expected 0; got %#x", pfd.revents);
  	kill(ppid, SIGUSR1);
 
  	usleep(1);
  	while (state != 5)
  		;
  	if (poll(&pfd, 1, 0) < 0)
  		err(1, "poll");
  	if (pfd.revents != POLLIN)
  		warnx("state 5: expected POLLIN; got %#x", pfd.revents);
  	kill(ppid, SIGUSR1);
 
  	usleep(1);
  	while (state != 6)
  		;
  	/*
  	 * Now we have no writer, but should still have data from the old
  	 * writer. Check that we have both a data condition and a hangup
  	 * condition, and that the data can read the data in the usual way.
  	 * Since Linux does this, programs must not quite reading when they
  	 * see POLLHUP; they must see POLLHUP without POLLIN (or another
  	 * input condition) before they decide that there is EOF.  gdb-6.1.1
  	 * is an example of a broken program that quits on POLLHUP only --
  	 * see its event-loop.c.
  	 */
  	if (poll(&pfd, 1, 0) < 0)
  		err(1, "poll");
  	if (pfd.revents != (POLLIN | POLLHUP))
  		warnx("state 6: expected POLLIN | POLLHUP; got %#x",
  		    pfd.revents);
  	if (read(pfd.fd, buf, sizeof buf) != 1)
  		err(1, "read");
  	if (poll(&pfd, 1, 0) < 0)
  		err(1, "poll");
  	if (pfd.revents != POLLHUP)
  		warnx("state 6a: expected POLLHUP; got %#x",
  		    pfd.revents);
  	close(pfd.fd);
  	kill(ppid, SIGUSR1);
  	exit(0);
 }
 
 static void
 parent(int fd)
 {
  	usleep(1);
  	while (state != 1)
  		;
 #ifdef NAMEDPIPE
  	fd = open("p", O_WRONLY | O_NONBLOCK);
  	if (fd < 0)
  		err(1, "open for write");
 #endif
  	kill(cpid, SIGUSR1);
 
  	usleep(1);
  	while (state != 2)
  		;
  	if (write(fd, "", 1) != 1)
  		err(1, "write");
  	kill(cpid, SIGUSR1);
 
  	usleep(1);
  	while (state != 3)
  		;
  	if (close(fd) != 0)
  		err(1, "close for write");
  	kill(cpid, SIGUSR1);
 
  	usleep(1);
  	while (state != 4)
  	    ;
 #ifndef NAMEDPIPE
  	return;
 #endif
  	fd = open("p", O_WRONLY | O_NONBLOCK);
  	if (fd < 0)
  		err(1, "open for write");
  	kill(cpid, SIGUSR1);
 
  	usleep(1);
  	while (state != 5)
  		;
  	if (write(fd, "", 1) != 1)
  		err(1, "write");
  	kill(cpid, SIGUSR1);
 
  	usleep(1);
  	while (state != 6)
  		;
  	if (close(fd) != 0)
  		err(1, "close for write");
  	kill(cpid, SIGUSR1);
 
  	usleep(1);
  	while (state != 7)
  		;
 }
 
 int
 main(void)
 {
  	int fd[2];
  	int i;
 
 #ifdef NAMEDPIPE
  	if (mkfifo("p", 0666) != 0 && errno != EEXIST)
  		err(1, "mkfifo");
 #endif
  	signal(SIGUSR1, catch);
  	ppid = getpid();
  	for (i = 0; i < 2; i++) {
 #ifndef NAMEDPIPE
  		if (pipe(fd) != 0)
  			err(1, "pipe");
 #else
  		fd[0] = -1;
  		fd[1] = -1;
 #endif
  		state = 0;
  		switch (cpid = fork()) {
  		case -1:
  			err(1, "fork");
  		case 0:
  			(void)close(fd[1]);
  			child(fd[0]);
  			break;
  		default:
  			(void)close(fd[0]);
  			parent(fd[1]);
  			break;
  		}
  	}
  	return (0);
 }
 %%%
 
 The error output of these is null under Linux-2.6.10, but under
 FreeBSD-5.oldcurrent it is:
 
 poll() on a nameless pipe:
 % poll: state 6a: expected POLLHUP; got 0x11
 % poll: state 6a: expected POLLHUP; got 0x11
 
 No change for this.  For poll(), Linux consistently doesn't set POLLIN when
 there is only null data, so we check for this.
 
 poll() on a named pipe:
 % pollp: state 3: expected POLLHUP; got 0
 % pollp: state 6: expected POLLIN | POLLHUP; got 0x1
 % pollp: state 6a: expected POLLHUP; got 0
 % pollp: state 3: expected POLLHUP; got 0
 % pollp: state 6: expected POLLIN | POLLHUP; got 0x1
 % pollp: state 6a: expected POLLHUP; got 0
 
 No change for this, except I didn't compile with POLLINIGNEOF used so
 the 3 and 6a state don't get fixed up.
 
 select() on a nameless pipe:
 <no output>
 
 No change for this.  Here it doesn't matter if hangup is indicated by
 POLLHUP or POLLIN | POLLHUP -- selscan() converts both to data-ready
 although it's null data.
 
 select() on a named pipe:
 % selectp: state 0: expected set; got clear
 % selectp: state 3: expected set; got clear
 % selectp: state 6a: expected set; got clear
 % selectp: state 0: expected set; got clear
 % selectp: state 3: expected set; got clear
 % selectp: state 6a: expected set; got clear
 
 Now there is an extra failure for state 0.  Some complications will be
 required to fix this without breaking poll() on named pipe.  State 0 is
 when the read descriptor is open with O_NONBLOCK and there has "never"
 been a writer.  In this state, select() on the read descriptor must
 succeed to conform to POSIX, but poll() on the read descriptor must
 block to conform to Linux.  I think the Linux behaviour is what happens
 naturally -- the socket isn't hung up so sopoll() won't set POLLHUP,
 and there is no input so sopoll() won't set POLLIN, so sopoll() won't
 set any flags in revents and poll() will block.  An extra flag seems to
 be necessary to distinguish this state so that select() doesn't block.
 POLLINIGNEOF was supposed to be this flag.
 
 Bruce


More information about the freebsd-bugs mailing list