Skip site navigation (1)Skip section navigation (2)
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>