Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Poll() on Named Pipe returns with POLLHUP constantly and immediately

I open a named pipe (fifo created by mkfifo) with non blocking flag (open(...O_NONBLOCK)) then start polling (poll(...)). So far so good. Then from the command line I do a couple of

echo 123 > /tmp/fifo

They are all read out of the pipe as expected (at least I expect that's how they should work normally).

My problem is that after the first echo, POLLHUP is set and it is stuck, poll returns immediately from that point.

How do I clear / get rid of POLLHUP ?

It starts to drive me crazy :(

Yes the other end of the pipe got closed (after it was opened previously), so it became half closed, but my end is still open and alive and I like it that way. It's not dead, I can still receive new echoes via the pipe, it's just poll cries a river of POLLHUP (which I didn't even requested in the first place in events, but poll can just tag them on anyway [man poll: "revents can include any of those specified in events, or one of the values POLLERR, POLLHUP"]) and pretty much useless because of that.

Obviously I cannot get that fd out of the set, because I still want to be notified about new data on it.

I do NOT want to close it, because it's not a use-once-pipe, I like reusing the same thing and not throw them away... among other things I do not have the pipe name anymore, I only have the file descriptor (and getting the filename from fd seems like a bitch... I Googled that too...)

I still believe in the power of Linux, and that there must be a better (more performance efficient / race condition safe) way to do this.

Here is what I have read, but did not help solving the problem.

  • http://www.greenend.org.uk/rjk/tech/poll.html - Poll seems to be a f***load of fun indeed...
  • socket: constantly getting POLLHUP on half-close - No, I do not wish to close it, it's not a socket, it's a pipe (that must be opened first on the reader side anyway)
  • How to use the poll C function to watch named pipes in Linux? - This is my problem but how to solve it? When I open the pipe it is as much half closed (actually half opened:) as after someone wrote something in it and closed their end after them, but I only get POLLHUP storm on half closed ones not half open ones (so there is a difference).

In my desperation I tried doing even things like this (which did not help):

    int newfd = dup(fds[i].fd);
    close(fds[i].fd);
    dup2(newfd, fds[i].fd);
    close(newfd);

Any ideas? Am I doing something completely wrong?

(I can always go back to just trying to read all pipes periodically (which actually works), this right now is not latency critical, but I don't know what would I do if it were...)

Here are some code to reproduce my problem (This is not the production code that I'm trying to build, there are obviously more than 1 pipe that I want to poll...)

#include <stdio.h>

#include <sys/types.h>  // mkfifo
#include <sys/stat.h>   // mkfifo

#include <unistd.h>
#include <fcntl.h>

#include <errno.h>
#include <string.h>

#include <poll.h>


int main(int argc, char **argv) {
    char* pipename = "/tmp/fifo";
    mkfifo(pipename, S_IWUSR | S_IRUSR | S_IRGRP);
    int fd = open(pipename, O_RDONLY | O_NONBLOCK); /// O_ASYNC, O_SYNC

    struct pollfd fds[1];
        fds[0].fd = fd;
        fds[0].events = POLLIN;
        fds[0].revents = 0;

    while (1) {
        int res = poll(fds, 1, 1000);
        if (res<0)  { perror("poll");  return 1; }
        if (res==0) { printf(".\n"); continue; }
        printf("Poll [%d]: 0x%04x on %d\n", 0, fds[0].revents, fds[0].fd);

        char buff[512];
        int count = (int)read(fds[0].fd, buff, (size_t)(sizeof(buff)-1));
        if (count>=0) {
            buff[count] = 0;
            printf("Pipe read %d bytes: '%s'\n", count, buff);
            usleep(1000*100); /// cpu protector sleep for POLLHUP :)
        }   
    }
    return 0;
}

Notes:

I'm using gcc (4.6.3) on Linux (lubuntu) platform (x64). But in the end I want to cross compile it for embedded target.

I'm sure I must have missed some information peaces out, so ask away...

Solution/Workaround:

  1. Workaround #1 suggested by mark4o is to open the pipe with O_RDWR instead of O_RDONLY. This way you won't get constant POLLHUP-s (of course you won't get any witch might be a problem). Also the reader will need write permission to the pipe (which you might not have in certain cases).
like image 763
Steven Spark Avatar asked Feb 25 '14 17:02

Steven Spark


1 Answers

Because pipes provide only a single unidirectional channel (not a separate bidirectional channel for each client like a socket), they are normally used when you have just one process that needs to send data to only one other process. When the writer closes the pipe, POLLHUP (hangup) tells the reader that the pipe is closed and it can finish processing and terminate.

It is possible to use a pipe with multiple writers but you will need to be careful if the messages could be larger than PIPE_BUF or 512 bytes. Otherwise, because it is only a single channel, the messages from multiple writers writing at the same time could be interleaved. Also because it is a single channel you won't be able to tell whether a long message is a single write from one client or multiple writes from multiple clients, unless you have some convention like one line (terminated by a newline) per client message.

POLLHUP indicates that the last writer has closed the pipe, and persists until another process opens the pipe for writing or it is closed by all readers. If you don't want this, open the pipe with O_RDWR instead of O_RDONLY so that the pipe will stay open. This works because then there will always be a writer (your program) as long as you have it open.

like image 192
mark4o Avatar answered Oct 29 '22 22:10

mark4o