5861f96654
and recorded results for several operating systems. Submitted by: bde Approved by: re (kensmith)
319 lines
7.0 KiB
C
319 lines
7.0 KiB
C
/* $FreeBSD$ */
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/select.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#define FIFONAME "fifo.tmp"
|
|
#define FT_END 3
|
|
#define FT_FIFO 2
|
|
#define FT_PIPE 0
|
|
#define FT_SOCKETPAIR 1
|
|
|
|
#define SETUP(fd, rfds, tv) do { \
|
|
FD_ZERO(&(rfds)); \
|
|
FD_SET((fd), &(rfds)); \
|
|
(tv).tv_sec = 0; \
|
|
(tv).tv_usec = 0; \
|
|
} while (0)
|
|
|
|
static int filetype;
|
|
|
|
static const char *
|
|
decode_events(int events)
|
|
{
|
|
return (events ? "set" : "clear");
|
|
}
|
|
|
|
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(" %s state %s: expected %s; got %s\n",
|
|
filetype == FT_PIPE ? "Pipe" :
|
|
filetype == FT_SOCKETPAIR ? "Sock" : "FIFO",
|
|
state, decode_events(expected), decode_events(got));
|
|
fflush(stdout);
|
|
}
|
|
|
|
static pid_t cpid;
|
|
static pid_t ppid;
|
|
static volatile sig_atomic_t state;
|
|
|
|
static void
|
|
catch(int sig)
|
|
{
|
|
state++;
|
|
}
|
|
|
|
static void
|
|
child(int fd, int num)
|
|
{
|
|
fd_set rfds;
|
|
struct timeval tv;
|
|
int fd1, fd2;
|
|
char buf[256];
|
|
|
|
if (filetype == FT_FIFO) {
|
|
fd = open(FIFONAME, O_RDONLY | O_NONBLOCK);
|
|
if (fd < 0)
|
|
err(1, "open for read");
|
|
}
|
|
if (fd >= FD_SETSIZE)
|
|
errx(1, "fd = %d too large for select()", fd);
|
|
|
|
if (filetype == FT_FIFO) {
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
/*
|
|
* This state (a reader for which there has never been a
|
|
* writer) is reported quite differently for select() than
|
|
* for poll(). select() must see a ready-to-read descriptor
|
|
* since read() will see EOF and not block; it cannot
|
|
* distinguish this state from the one of a reader for which
|
|
* there has been a writer but all writers have gone away
|
|
* and all data has been read. poll() and distinguish these
|
|
* states by returning POLLHUP only for the latter; it does
|
|
* this, although this makes it inconsistent with the
|
|
* blockability of read() in the former.
|
|
*/
|
|
report(num++, "0", 1, FD_ISSET(fd, &rfds));
|
|
}
|
|
kill(ppid, SIGUSR1);
|
|
|
|
usleep(1);
|
|
while (state != 1)
|
|
;
|
|
if (filetype != FT_FIFO) {
|
|
/*
|
|
* The connection cannot be reestablished. Use the code that
|
|
* delays the read until after the writer disconnects since
|
|
* that case is more interesting.
|
|
*/
|
|
state = 4;
|
|
goto state4;
|
|
}
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "1", 0, FD_ISSET(fd, &rfds));
|
|
kill(ppid, SIGUSR1);
|
|
|
|
usleep(1);
|
|
while (state != 2)
|
|
;
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "2", 1, FD_ISSET(fd, &rfds));
|
|
if (read(fd, buf, sizeof buf) != 1)
|
|
err(1, "read");
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "2a", 0, FD_ISSET(fd, &rfds));
|
|
kill(ppid, SIGUSR1);
|
|
|
|
usleep(1);
|
|
while (state != 3)
|
|
;
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "3", 1, FD_ISSET(fd, &rfds));
|
|
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:
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "4", 0, FD_ISSET(fd, &rfds));
|
|
kill(ppid, SIGUSR1);
|
|
|
|
usleep(1);
|
|
while (state != 5)
|
|
;
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "5", 1, FD_ISSET(fd, &rfds));
|
|
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 a data-readable condition, and that
|
|
* the data can be read in the usual way.
|
|
*/
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "6", 1, FD_ISSET(fd, &rfds));
|
|
if (read(fd, buf, sizeof buf) != 1)
|
|
err(1, "read");
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "6a", 1, FD_ISSET(fd, &rfds));
|
|
if (filetype == FT_FIFO) {
|
|
/*
|
|
* Check that the readable-data condition is sticky for a
|
|
* new reader and for the old reader. We really only have
|
|
* a hangup condition, but select() can only see this as
|
|
* a readable-data condition for null data. select()
|
|
* cannot distinguish this state from the initial state
|
|
* where there is a reader but has never been a writer, so
|
|
* the following tests (to follow the pattern in pipepoll.c)
|
|
* essentially test state 0 again.
|
|
*/
|
|
fd2 = open(FIFONAME, O_RDONLY | O_NONBLOCK);
|
|
if (fd2 < 0)
|
|
err(1, "open for read");
|
|
fd1 = fd;
|
|
fd = fd2;
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "6b", 1, FD_ISSET(fd, &rfds));
|
|
fd = fd1;
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "6c", 1, FD_ISSET(fd, &rfds));
|
|
close(fd2);
|
|
SETUP(fd, rfds, tv);
|
|
if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
|
|
err(1, "select");
|
|
report(num++, "6d", 1, FD_ISSET(fd, &rfds));
|
|
}
|
|
close(fd);
|
|
kill(ppid, SIGUSR1);
|
|
|
|
exit(0);
|
|
}
|
|
|
|
static void
|
|
parent(int fd)
|
|
{
|
|
usleep(1);
|
|
while (state != 1)
|
|
;
|
|
if (filetype == FT_FIFO) {
|
|
fd = open(FIFONAME, O_WRONLY | O_NONBLOCK);
|
|
if (fd < 0)
|
|
err(1, "open for write");
|
|
}
|
|
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)
|
|
;
|
|
if (filetype != FT_FIFO)
|
|
return;
|
|
fd = open(FIFONAME, 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], num;
|
|
|
|
num = 1;
|
|
printf("1..20\n");
|
|
fflush(stdout);
|
|
signal(SIGUSR1, catch);
|
|
ppid = getpid();
|
|
for (filetype = 0; filetype < FT_END; filetype++) {
|
|
switch (filetype) {
|
|
case FT_FIFO:
|
|
if (mkfifo(FIFONAME, 0666) != 0)
|
|
err(1, "mkfifo");
|
|
fd[0] = -1;
|
|
fd[1] = -1;
|
|
break;
|
|
case FT_SOCKETPAIR:
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, AF_UNSPEC,
|
|
fd) != 0)
|
|
err(1, "socketpair");
|
|
break;
|
|
case FT_PIPE:
|
|
if (pipe(fd) != 0)
|
|
err(1, "pipe");
|
|
break;
|
|
}
|
|
state = 0;
|
|
switch (cpid = fork()) {
|
|
case -1:
|
|
err(1, "fork");
|
|
case 0:
|
|
(void)close(fd[1]);
|
|
child(fd[0], num);
|
|
break;
|
|
default:
|
|
(void)close(fd[0]);
|
|
parent(fd[1]);
|
|
break;
|
|
}
|
|
num += filetype == FT_FIFO ? 12 : 4;
|
|
}
|
|
(void)unlink(FIFONAME);
|
|
return (0);
|
|
}
|