MFC r196460

Fix the conformance of poll(2) for sockets after r195423 by
  returning POLLHUP instead of POLLIN for several cases. Now, the
  tools/regression/poll results for FreeBSD are closer to that of the
  Solaris and Linux.

  Also, improve the POSIX conformance by explicitely clearing POLLOUT
  when POLLHUP is reported in pollscan(), making the fix global.

  Submitted by:	bde
  Reviewed by:	rwatson

MFC r196556

  Fix poll() on half-closed sockets, while retaining POLLHUP for fifos.

  This reverts part of r196460, so that sockets only return POLLHUP if both
  directions are closed/error. Fifos get POLLHUP by closing the unused
  direction immediately after creating the sockets.

  The tools/regression/poll/*poll.c tests now pass except for two other
  things:
  - if POLLHUP is returned, POLLIN is always returned as well instead of
    only when there is data left in the buffer to be read
  - fifo old/new reader distinction does not work the way POSIX specs it

  Reviewed by:	kib, bde

MFC r196554

  Add some tests for poll(2)/shutdown(2) interaction.

Approved by:	re (kensmith)
This commit is contained in:
Jilles Tjoelker 2009-09-01 20:58:41 +00:00
parent e2ba743711
commit 33656b91cd
4 changed files with 215 additions and 2 deletions

View file

@ -193,6 +193,9 @@ fifo_open(ap)
goto fail2;
fip->fi_writesock = wso;
error = soconnect2(wso, rso);
/* Close the direction we do not use, so we can get POLLHUP. */
if (error == 0)
error = soshutdown(rso, SHUT_WR);
if (error) {
(void)soclose(wso);
fail2:

View file

@ -1228,6 +1228,13 @@ pollscan(td, fds, nfd)
selfdalloc(td, fds);
fds->revents = fo_poll(fp, fds->events,
td->td_ucred, td);
/*
* POSIX requires POLLOUT to be never
* set simultaneously with POLLHUP.
*/
if ((fds->revents & POLLHUP) != 0)
fds->revents &= ~POLLOUT;
if (fds->revents != 0)
n++;
}

View file

@ -3,14 +3,15 @@
# Nothing yet works with gmake for the path to the sources.
.PATH: ..
PROG= pipepoll pipeselect
PROG= pipepoll pipeselect sockpoll
CFLAGS+= -Werror -Wall
all: ${PROG}
pipepoll: pipepoll.c
pipeselect: pipeselect.c
sockpoll: sockpoll.c
pipepoll pipeselect:
pipepoll pipeselect sockpoll:
${CC} ${CFLAGS} ${LDFLAGS} -o $@ $@.c
test: all

View file

@ -0,0 +1,202 @@
/* $FreeBSD$ */
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <err.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static const char *
decode_events(int events)
{
char *ncresult;
const char *result;
switch (events) {
case POLLIN:
result = "POLLIN";
break;
case POLLOUT:
result = "POLLOUT";
break;
case POLLIN | POLLOUT:
result = "POLLIN | POLLOUT";
break;
case POLLHUP:
result = "POLLHUP";
break;
case POLLIN | POLLHUP:
result = "POLLIN | POLLHUP";
break;
case POLLOUT | POLLHUP:
result = "POLLOUT | POLLHUP";
break;
case POLLIN | POLLOUT | POLLHUP:
result = "POLLIN | POLLOUT | POLLHUP";
break;
default:
asprintf(&ncresult, "%#x", events);
result = ncresult;
break;
}
return (result);
}
static void
report(int num, const char *state, int expected, int got)
{
if (expected == got)
printf("ok %-2d ", num);
else
printf("not ok %-2d", num);
printf(" state %s: expected %s; got %s\n",
state, decode_events(expected), decode_events(got));
fflush(stdout);
}
static int
set_nonblocking(int sck)
{
int flags;
flags = fcntl(sck, F_GETFL, 0);
flags |= O_NONBLOCK;
if (fcntl(sck, F_SETFL, flags))
return -1;
return 0;
}
static char largeblock[1048576]; /* should be more than AF_UNIX sockbuf size */
static int fd[2];
static struct pollfd pfd0;
static struct pollfd pfd1;
void
setup(void)
{
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0)
err(1, "socketpair");
if (set_nonblocking(fd[0]) == -1)
err(1, "fcntl");
if (set_nonblocking(fd[1]) == -1)
err(1, "fcntl");
pfd0.fd = fd[0];
pfd0.events = POLLIN | POLLOUT;
pfd1.fd = fd[1];
pfd1.events = POLLIN | POLLOUT;
}
int
main(void)
{
int num;
num = 1;
printf("1..18\n");
fflush(stdout);
/* Large write with close */
setup();
if (poll(&pfd0, 1, 0) == -1)
err(1, "poll");
report(num++, "initial 0", POLLOUT, pfd0.revents);
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "initial 1", POLLOUT, pfd1.revents);
if (write(fd[0], largeblock, sizeof(largeblock)) == -1)
err(1, "write");
if (poll(&pfd0, 1, 0) == -1)
err(1, "poll");
report(num++, "after large write", 0, pfd0.revents);
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "other side after large write", POLLIN | POLLOUT, pfd1.revents);
close(fd[0]);
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "other side after close", POLLIN | POLLHUP, pfd1.revents);
if (read(fd[1], largeblock, sizeof(largeblock)) == -1)
err(1, "read");
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "other side after reading input", POLLHUP, pfd1.revents);
close(fd[1]);
/* With shutdown(SHUT_WR) */
setup();
if (shutdown(fd[0], SHUT_WR) == -1)
err(1, "shutdown");
if (poll(&pfd0, 1, 0) == -1)
err(1, "poll");
report(num++, "after shutdown(SHUT_WR)", POLLOUT, pfd0.revents);
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "other side after shutdown(SHUT_WR)", POLLIN | POLLOUT, pfd1.revents);
switch (read(fd[1], largeblock, sizeof(largeblock))) {
case 0:
break;
case -1:
err(1, "read after other side shutdown");
break;
default:
errx(1, "kernel made up data that was never written");
}
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "other side after reading EOF", POLLIN | POLLOUT, pfd1.revents);
if (write(fd[1], largeblock, sizeof(largeblock)) == -1)
err(1, "write");
if (poll(&pfd0, 1, 0) == -1)
err(1, "poll");
report(num++, "after data from other side", POLLIN | POLLOUT, pfd0.revents);
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "after writing", POLLIN, pfd1.revents);
if (shutdown(fd[1], SHUT_WR) == -1)
err(1, "shutdown second");
if (poll(&pfd0, 1, 0) == -1)
err(1, "poll");
report(num++, "after second shutdown", POLLIN | POLLHUP, pfd0.revents);
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "after second shutdown", POLLHUP, pfd1.revents);
close(fd[0]);
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "after close", POLLHUP, pfd1.revents);
close(fd[1]);
/*
* With shutdown(SHUT_RD)
* Note that shutdown(SHUT_WR) is passed to the peer, but
* shutdown(SHUT_RD) is not.
*/
setup();
if (shutdown(fd[0], SHUT_RD) == -1)
err(1, "shutdown");
if (poll(&pfd0, 1, 0) == -1)
err(1, "poll");
report(num++, "after shutdown(SHUT_RD)", POLLIN | POLLOUT, pfd0.revents);
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "other side after shutdown(SHUT_RD)", POLLOUT, pfd1.revents);
if (shutdown(fd[0], SHUT_WR) == -1)
err(1, "shutdown");
if (poll(&pfd0, 1, 0) == -1)
err(1, "poll");
report(num++, "after shutdown(SHUT_WR)", POLLHUP, pfd0.revents);
if (poll(&pfd1, 1, 0) == -1)
err(1, "poll");
report(num++, "other side after shutdown(SHUT_WR)", POLLIN | POLLOUT, pfd1.revents);
close(fd[0]);
close(fd[1]);
return (0);
}