13
votes

I am trying to write a program where i am supposed to watch the ends of some named pipes using poll function. I have a for loop to check every pipe whenever poll returns >0 and i know that when a pipe gets closed from the procedure at the other end, i will get POLLHUP or POLLIN | POLLHUP in the revents field of the pollfd struct.

My question is: when one pipe does indeed get closed and returns POLLHUP to me, what will happen on the next loop? Is it going to return POLLHUP again and again in the next and any subsequent loop or is poll function going to ignore it after the first POLLHUP?

2
Sounds like this would be fairly straightforward to find out by testing it.Ben Voigt
i tried but something goes wrong and pollhup returns too many times, much more than anticipated so i was just wondering what could possible be wrong with my code so i asked that questionnikos
@nikos: You probably want to tell poll you're no longer interested in events from that fd (or only some events, if the connection is half-closed).Ben Voigt
so if i don't tell that to poll it will continue examining that pipe and returning POLLHUP everytime, right?nikos
@nikos: As long as you leave that fd in the list passed to poll it will have to examine it. And yes, I would expect it to continue returning POLLHUP since the fd is still in that state.Ben Voigt

2 Answers

4
votes

Minimal example

Source below. Usage:

sudo mknod poll0.tmp p
sudo mknod poll1.tmp p
sudo chmod 666 poll*.tmp
./poll.out

On another shell:

printf a > poll0.tmp
printf b > poll1.tmp

Output:

loop
POLLIN i=0 n=1 buf=a
loop
POLLHUP i=0
loop
POLLIN i=1 n=1 buf=b
POLLHUP i=1
loop

So notice how poll waits for the reads without looping.

Cooler example:

(while true; do date; sleep 1; done) > poll0.tmp &
(while true; do date; sleep 2; done) > poll1.tmp &

0 gets written every one second, and 1 every two seconds, which shows how poll() is dealing with both inputs concurrently, without stalling each other.

Source:

poll.c

#define _XOPEN_SOURCE 700
#include <fcntl.h> /* creat, O_CREAT */
#include <poll.h> /* poll */
#include <stdio.h> /* printf, puts, snprintf */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <unistd.h> /* read */

int main(void) {
    enum { N = 2 };
    char buf[1024], path[1024];
    int fd, i, n;
    short revents;
    struct pollfd pfds[N];

    for (i = 0; i < N; ++i) {
        snprintf(path, sizeof(path), "poll%d.tmp", i);
        /* O_NONBLOCK is required or else the open blocks
         * until the other side of the pipe opens. */
        fd = open(path, O_RDONLY | O_NONBLOCK);
        if (fd == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }
        pfds[i].fd = fd;
        /* Only events in this mask will be listened to.
         * However, there are also some events that are unmaskable,
         * notably POLLHUP when pipe closes! */
        pfds[i].events = POLLIN;
    }
    while (1) {
        puts("loop");
        i = poll(pfds, N, -1);
        if (i == -1) {
            perror("poll");
            exit(EXIT_FAILURE);
        }
        for (i = 0; i < N; ++i) {
            revents = pfds[i].revents;
            if (revents & POLLIN) {
                n = read(pfds[i].fd, buf, sizeof(buf));
                printf("POLLIN i=%d n=%d buf=%.*s\n", i, n, n, buf);
            }
            if (revents & POLLHUP) {
                printf("POLLHUP i=%d\n", i);

                /* This happens when the other side closed.
                 * This event is only cleared when we close the reader. */

                /* poll won't set POLLHUP anymore once all fds are closed.
                 * Any futher polls on this will give the POLLNVAL event instead. */
                close(pfds[i].fd);

                /* negative fds are ignored. So if we negate an FD,
                 * we can both turn if off for a while, and turn it on
                 * later on by re-nagating it. */
                pfds[i].fd *= -1;
            }
        }
    }
}

Compile with:

gcc -o poll.out -std=c99 poll.c

Tested in Ubuntu 14.04.

GitHub upstream.

To answer the original question:

when one pipe does indeed get closed and returns POLLHUP to me, what will happen on the next loop? Is it going to return POLLHUP again and again in the next and any subsequent loop or is poll function going to ignore it after the first POLLHUP?

Remove the lines:

close(pfds[i].fd);
pfds[i].fd *= -1;

and you will see that it loops forever over POLLHUP.

Remove just:

close(pfds[i].fd);

and you get POLLNVAL instead, as it tries to use a closed fd: How to handle the Linux socket revents POLLERR, POLLHUP and POLLNVAL?

3
votes

It will continue to set POLLHUP in revents.
Also, see http://linux.die.net/man/3/poll for reference.