FIFO issues

Enrico Forestieri forenr@lyx.org
Tue Sep 20 17:20:08 GMT 2022


On Tue, Sep 20, 2022 at 06:18:54PM -0700, Ken Brown wrote:
> 
> On 9/20/2022 2:54 AM, Enrico Forestieri wrote:
> > 
> >  I compared the behavior of read() and select() on 3 different platforms.
> >  My conclusion is that, actually, read() behaves the same on all of them,
> >  whereas cygwin differs in the way select() works.
> 
> Then I'm even more confused than I was before. Are you saying that
> there are situations in which read() reports EOF but select() does not
> report read ready? Could you post the code you used for testing?

I simply introduced a timeout for select() such that it returns on any
platform. On cygwin, when using "O_RDONLY | O_NONBLOCK" for the input
pipe, select() always returns 1, while on all other platforms it returns 0
unless a client actually opens it for writing.

As the "if (FD_ISSET(infd, &readfds))" block would not be entered on the
other platforms, I replaced it with "if (TRUE)" so that the read() is
always executed. In this conditions, the output is the same on all
platforms, meaning that the issue is the select() and not the read() call.

This is the attached fifocomm-test.c file.

-- 
Enrico
-------------- next part --------------
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define FIFO_IN  "/tmp/pipe.in"
#define FIFO_OUT "/tmp/pipe.out"

#define FALSE 0
#define TRUE  1

typedef int bool;

void openpipes(void);
void closepipes(void);
int startpipe(char *name, bool inputpipe);
void endpipe(char *name, int fd);

int infd;
int outfd;
int count = 0;

int main(void)
{
    int nsel;
    bool done = FALSE;
    char const *msg = "You sent: ";
    int mlen = strlen(msg);
    fd_set readfds;
    FD_ZERO(&readfds);

    openpipes();

    do {
	FD_SET(infd, &readfds);
	struct timeval wait_tm = { 0l, 200000l }; /* 200 millisecs */
	do {
	    nsel = select(infd + 1, &readfds, 0, 0, &wait_tm);
	} while (nsel == -1 && (errno == EINTR || errno == EAGAIN));

	if (nsel == -1) {
	    perror("select");
	    exit(4);
	}

	if (TRUE) {
	    char buf[100] = "You sent: ";
	    int status;
	    int count = 0;
	    strcpy(buf, msg);
	    while ((status = read(infd, buf + mlen, sizeof(buf) - mlen - 1))) {
		if (status > 0) {
		    buf[mlen + status] = '\0';
		    if (write(outfd, buf, strlen(buf)) < 0) {
			if (buf[mlen + status - 1] == '\n')
			    buf[mlen + status - 1] = '\0';
			fprintf(stderr, "Error sending message: '%s': %s\n",
				buf, strerror(errno));
		    }
		    if (strncasecmp(buf + mlen, "quit", 4) == 0)
			done = TRUE;
		} else if (errno == EAGAIN) {
		    /* Nothing to read, continue */
		    break;
		} else {
		    /* An error occurred, bail out */
		    perror("read");
		    done = TRUE;
		    break;
		}
	    }
	    if (!done) {
		sleep(3);
		closepipes();
		openpipes();
		errno = 0;
	    }
	}
    } while (!done);

    closepipes();
    return 0;
}


void openpipes(void)
{
    fprintf(stderr, "%d) Opening pipes\n", ++count);

    infd = startpipe(FIFO_IN, TRUE);
    if (infd == -1)
	exit(1);

    outfd = startpipe(FIFO_OUT, FALSE);
    if (outfd == -1)
	exit(2);

    if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) {
	fprintf(stderr, "Could not set flags on pipe %s: %s\n",
		FIFO_OUT, strerror(errno));
	exit(3);
    }
}


void closepipes()
{
    fprintf(stderr, "%d) Closing pipes\n", count);

    endpipe(FIFO_IN, infd);
    endpipe(FIFO_OUT, outfd);
}


int startpipe(char *name, bool inputpipe)
{
    static bool stalepipe = FALSE;
    int fd;

    if (access(name, F_OK) == 0) {
	if (inputpipe) {
	    /* Let's see whether we have a stale pipe */
	    fd = open(name, O_WRONLY | O_NONBLOCK);
	    if (fd >= 0) {
		/* pipe exists and is used by another process */
		close(fd);
	    } else if (errno == ENXIO) {
		/* No process is reading from the other end */
		fprintf(stderr, "Removing stale pipe %s\n", name);
		stalepipe = TRUE;
		endpipe(name, -1);
	    }
	} else if (stalepipe) {
	    fprintf(stderr, "Removing stale pipe %s\n", name);
	    endpipe(name, -1);
	    stalepipe = FALSE;
	}
	if (access(name, F_OK) == 0) {
	    fprintf(stderr, "Pipe %s already exists and is in use.\n", name);
	    return -1;
	}
    }

    if (mkfifo(name, 0600) < 0) {
	fprintf(stderr, "Could not create pipe %s: %s\n",
		name, strerror(errno));
	return -1;
    }

    fd = open(name, inputpipe ? (O_RDONLY | O_NONBLOCK) : O_RDWR);

    if (fd < 0) {
	fprintf(stderr, "Could not open pipe %s: %s\n",
		name, strerror(errno));
	endpipe(name, -1);
	return -1;
    }

    return fd;
}


void endpipe(char *name, int fd)
{
    if (fd >= 0 && close(fd) < 0)
	fprintf(stderr, "Could not close pipe %s: %s\n",
		name, strerror(errno));

    if (remove(name) < 0)
	fprintf(stderr, "Could not remove pipe %s: %s\n",
		name, strerror(errno));
}


More information about the Cygwin mailing list