10

Is there a way (in C, or preferably in Perl) to determine whether a named pipe may be written - i.e. there is an active reading process It seems that if I open for write nonblocking, the open returns at once but a select for write also returns immediately. The goal is for the writing process to just carry on (i.e. skip sending) if the reading end is not ready

ValenceElectron
  • 2,678
  • 6
  • 26
  • 27
user1938139
  • 141
  • 1
  • 5

3 Answers3

6

Chances are that you are not paying attention to the return codes from open. If you open a FIFO for writing and as non-blocking there are two possible outcomes.

  1. If there is already a reader the open will immediately return successfully.

  2. If there is no pipe reader the open will fail with errno = ENXIO.

The following program demonstrates.

#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<stdlib.h>
#include <sys/stat.h>

#define SERVFIFO "/tmp/server.fifo"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

void syserr(const char *str)
{
    perror(str);
    exit(1);
}

int main(int argc, char** argv)
{
    umask(0);

    if (mkfifo(SERVFIFO, FILE_MODE) < 0 && errno != EEXIST)
        syserr("mkfifo");

    // try to open for write with no readers

    int fdw = open(SERVFIFO, O_WRONLY | O_NONBLOCK);
    if (fdw == -1)
        perror("non-blocking open for write with no readers failed");

    // create a reader - the process itself - non-blocking

    int fdr = open(SERVFIFO, O_RDONLY | O_NONBLOCK);
    if (fdr == -1)
        syserr("non-blocking open for read no writers failed");

    // try again to open for write but this time with a reader
    fdw = open(SERVFIFO, O_WRONLY | O_NONBLOCK);
    if (fdw == -1)
        syserr("non-blocking open with readers failed");

    printf("non-blocking open for write succeeded\n");

    close(fdw);
    close(fdr);
    unlink(SERVFIFO);
}

That said, the fact that the open fails without blocking isn't all that useful. About the only thing you can do is continually try to open until you succeed and that kind of polling can be wasteful, and in the context of a long running program waiting for intermittent readers is sort of ridiculous.

One solution is that used above - open the FIFO for reading yourself - but this has its own problems.

  1. If you use your FIFO write fd in your select statement it is always going to be writable...until it isn't. That is, because you are your own reader too, the FIFO writes will succeed until you fill the FIFO buffer with PIPE_BUF bytes. After that the FIFO won't be writable and your writes will fail with EAGAIN until a legitimate reader (i.e. not yourself) comes along, opens for reading, and starts draining the buffer.

  2. By opening your own FIFO for reading and writing, you won't see an EOF from legitimate users when they close the FIFO. Since you are only concerned about writing this may not be an issue for you.

Another possible solution, which I have never tried, is to use inotify on your FIFO. You can monitor the inotify file descriptor in your select and determine when someone has opened the FIFO. Then you know you can safely open your end for writing. You would probably want to mask off open permissions on the FIFO so that you can be the only writer, if possible for your application.

FIFOs should be renamed PITAs for these funky semantics. If you can make the change, this is why the Unix gods bestowed domain sockets upon us mere mortals.

Duck
  • 26,924
  • 5
  • 64
  • 92
2

Opening the write side of a pipe will block until a reader opens the read side, regardless of any other settings. So you can't return from open until the pipe is connected.

If a reader closes its side of an already open pipe, the writer will receive E_PIPE at the next write attempt. As far as I've been able to determine, this is the only indication you get that a pipe is no longer connected.

Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
  • 1
    The write side (opened as non-blocking) will return but with errno set to an error. – Duck Dec 20 '13 at 00:49
0

it seems that - with NO chance for the writer to check, this strategy will work: open nonblocking, then just write - regardless of changing the mode to blocking or not after the open, the write seems to succeed (so the writer will just continue if the reader is not actually waiting)

user1938139
  • 141
  • 1
  • 5
  • The write isn't succeeding, you are ignoring write errors to an un-opened file descriptor. – Duck Dec 20 '13 at 00:46