Date: Thu, 1 Feb 1996 13:56:26 +1100 From: Bruce Evans <bde@zeta.org.au> To: current@freebsd.org, dyson@freebsd.org Subject: new pipes fail several tests #4 Message-ID: <199602010256.NAA06197@godzilla.zeta.org.au>
next in thread | raw e-mail | index | archive | help
The fix for #3 seems to work. Thanks. New pipes don't honor the O_NONBLOCK flag or _POSIX_PIPE_MAX or PIPE_MAX. sys_pipe.c doesn't even reference these values. This test demonstrates that non-blocking writes block and that atomic writes may be non-atomic. PIPESIZE can be determined in a machine-independent way by nonblock-writing a byte at a time until the pipe fills up, but this can't be used here because the writes would block. PIPE_MAX can be checked automatically iff nonblocking writes work. Old pipes have several bugs involving PIPE_MAX: 1. PIPE_MAX is defined in <limits.h>, so if you fix it or change it, all binaries that depend on it must be recompiled. 2. PIPE_MAX is identical with _POSIX_PIPE_MAX (512). It is actually kinda sorta identical with MCLBYTES (2048). 3. MCLBYTES > _POSIX_PIPE_MAX is not enforced. 3. The corresponding socket semantics are a little different. Nonblocking that don't fit work correctly when <= MCLBYTES is written (-1/EAGAIN is returned) buy sometimes fail when > MCLBYTES is written (-1/EAGAIN is sometimes returned instead of writing what fits). PIPE_MAX should be a significant fraction of PIPESIZE in the new implementation. I think Linux uses 4095 for PIPE_MAX and 4096 for PIPESIZE. The non-power of 2 is probably not so good - processes wanting to do atomic writes to pipes should write PIPE_MAX bytes at a time and this will cause extra blocking overhead. Bruce #include <sys/types.h> #include <sys/ioctl.h> #include <assert.h> #include <errno.h> #include <fcntl.h> #include <limits.h> #include <signal.h> #include <stdio.h> #include <unistd.h> #define check(what, expected) assert((what, expected)) #define NONBLOCKING 0 /* option */ #define USE_NAMED_PIPE_P 0 /* option */ #if USE_NAMED_PIPE_P #define PIPESIZE 8192 #else #define PIPESIZE 16384 #endif sig_atomic_t caught; static void catch(int s) { caught = 1; } int main(void) { char buf[_POSIX_PIPE_BUF]; int fd[2]; size_t nw; int r; long tot; #if USE_NAMED_PIPE_P fd[0] = open("p", O_RDONLY | O_NONBLOCK); check("open", fd[0] != -1); r = fcntl(fd[0], F_SETFL, O_RDONLY); check("fcntl", r == 0); fd[1] = open("p", O_WRONLY | O_NONBLOCK); check("open", fd[1] != -1); r = fcntl(fd[1], F_SETFL, O_WRONLY); check("fcntl", r == 0); #else r = pipe(fd); check("pipe", r == 0); #endif #if NONBLOCKING r = fcntl(fd[1], F_SETFL, O_NONBLOCK); check("fcntl", r == 0); #endif /* * Fill up the pipe except for a little less than _POSIX_PIPE_BUF * bytes so that the next write of _POSIX_PIPE_BUF bytes (only) * partly fits. */ for (tot = 0; tot + sizeof buf <= PIPESIZE;) { nw = (tot + sizeof buf == PIPESIZE) ? 1 : sizeof buf; r = write(fd[1], buf, nw); if (r != -1) tot += r; fprintf(stderr, "write returned %d, tot = %ld\n", r, tot); check("write", r == nw); } check("almost filled", tot < PIPESIZE && tot + sizeof buf > PIPESIZE); /* Trap failures. */ check("siginterrupt", siginterrupt(SIGALRM, 1) == 0); check("signal", signal(SIGALRM, catch) != SIG_ERR); alarm(2); /* * Now we should be able to write at least _POSIX_PIPE_BUF bytes * atomically (or PIPE_BUF bytes, or fpathconf(fd[1], _PC_PIPE_BUF) * bytes, but those are bogusly the same as _POSIX_PIPE_BUF under * FreeBSD). */ r = write(fd[1], buf, sizeof buf); alarm(0); fprintf(stderr, "wrote %d bytes, errno = %d, caught = %d\n", r, errno , caught); #if NONBLOCKING /* Should have returned immediately without writing anything. */ check("write", r == -1); check("write", errno == EAGAIN); check("write", caught == 0); #else /* Should have returned after the alarm without writing anything. */ check("write", r == -1); check("write", errno == EINTR); check("write", caught == 1); #endif return 0; }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199602010256.NAA06197>