14

In C pseudo-code:

while (1) {
    fifo = open("fifo", O_RDONLY | O_NONBLOCK);
    fd_set read;
    FD_SET(fifo, &read);
    select(nfds, &read, NULL, NULL, NULL);
}

The process sleeps as triggered by select() until another process writes into fifo. Afterwards it will always find fifo as a readable file descriptor.

How to avoid this behavior (that is, after fifo has been read once, how to make it be found as unreadable until it gets another write?)

Matoe
  • 2,742
  • 6
  • 33
  • 52

3 Answers3

41

You opened that FIFO as read only (O_RDONLY), whenever there is no writer to the FIFO, the read end will receive an EOF.

Select system call will return on EOF and for every EOF you handle there will be a new EOF. This is the reason for the observed behavior.

To avoid this open that FIFO for both reading and writing (O_RDWR). This ensures that you have at least one writer on the FIFO thus there wont be an EOF and as a result select won't return unless someone writes to that FIFO.

Java Devil
  • 10,629
  • 7
  • 33
  • 48
Fox
  • 446
  • 5
  • 5
  • 3
    That was the correct answer to prevent running an endless loop of EOL detection – Big Papoo Jan 22 '14 at 23:39
  • this was the correct answer for me. Though, in my case I had minor variant of it: pipe always returns 2 fds - 1 for read and 1 for write; in my case i explicitly closed the write end (following code example from a C/S application). Hence, I encountered same problem that the read end was always ready for read because of the EOF. THANKS FOR THE TIP – avner Sep 09 '14 at 12:53
  • OMG, was looking for something like that for literally 4 hours. Finally found the solution here. +1 – siiikooo0743 Mar 02 '16 at 18:56
  • when i do this I never register a response in the select block from the write end of the fifo. it just stalls. Even if i redirect string from terminal to the fifo, i still get nothing – Jack Frye Aug 05 '18 at 17:33
3

The simple answer is to read until read() returns EWOULDBLOCK (or EAGAIN), or craps out with an error.

What you are saying simply cannot be happening unless the operating system (or runtime) that you are using is buggy. Otherwise you must be doing something wrong. For example, select() is using level-triggered I/O. I'd think that, most likely, you are not draining the socket completely, and so select() always indicates that you have something left in there (this does not happen with edge-triggered event notifications).

Below is a simple example that shows how one should read until the read() returns EWOULDBLOCK in order to avoid leaving descriptor in readable state (I've compiled and tested this on OS X, and there is also mostly no error checking, but you should get the idea):

/*
 * FIFO example using select.
 *
 * $ mkfifo /tmp/fifo
 * $ clang -Wall -o test ./test.c
 * $ ./test &
 * $ echo 'hello' > /tmp/fifo
 * $ echo 'hello world' > /tmp/fifo
 * $ killall test
 */

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

int main()
{
    int fd;
    int n;
    fd_set set;
    ssize_t bytes;
    size_t total_bytes;
    char buf[1024];

    fd = open("/tmp/fifo", O_RDWR | O_NONBLOCK);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    FD_ZERO(&set);
    FD_SET(fd, &set);

    for (;;) {
        n = select(fd+1, &set, NULL, NULL, NULL);
        if (!n)
            continue;
        if (n == -1) {
            perror("select");
            return EXIT_FAILURE;
        }
        if (FD_ISSET(fd, &set)) {
            printf("Descriptor %d is ready.\n", fd);
            total_bytes = 0;
            for (;;) {
                bytes = read(fd, buf, sizeof(buf));
                if (bytes > 0) {
                    total_bytes += (size_t)bytes;
                } else {
                    if (errno == EWOULDBLOCK) {
                        /* Done reading */
                        printf("done reading (%lu bytes)\n", total_bytes);
                        break;
                    } else {
                        perror("read");
                        return EXIT_FAILURE;
                    }
                }
            }
        }
    }

    return EXIT_SUCCESS;
}

Basically, level-triggered I/O means that you get notified all the time if there is something to read, even though you might have been notified of this before. On a contrary, edge-triggered I/O means that you are getting notified only once every time new data arrives and it doesn't matter whether you read it or not. select() is a level-triggered I/O interface.

Hope it helps. Good Luck!

  • 5
    I think the other answer contradicts this one, and is correct. `select` returns when a blocking `read` would return, and `read` returns `0` (signalling EOF) if no-one has your fifo open for writing. Your example program never does this because it has the fifo open for writing itself. – Ben Millwood May 22 '15 at 11:22
0

I had this problem, presumably due to an operating system bug. I found that closing and reopening the fifo after each return from the select call solved the problem.

John Chew
  • 319
  • 2
  • 4