Search code examples
c++posixpollingfile-descriptor

Why poll() returns immediately on regular files and blocks on fifo?


I checked this code several times and cannot understand why does poll() return immediately?

Here file is opened for read and should wait for event. How to make it wait for input?

#include <iostream>

#include <poll.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>


using namespace std;

ssize_t read_out_to_the_end(int fd){
   char chunk[1024];
   ssize_t ret = 0, n;
   while((n = ::read(fd, chunk, sizeof chunk)) > 0){
      ret += n;
      cerr << "read chunk: " << n << " | ";
      cerr.write(chunk, n);
      cerr << endl;
   }
   if (n < 0) {
       cerr << "err in read" << endl;
   }
   else if (ret == 0){
      cerr << "nothing to read" << endl;
   }
   return ret;
}

int main() {
   int bininfd = open("bin-in", O_RDONLY | O_CREAT);//, 0644/*S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH*/);
   if (bininfd < 0) {
      perror("err in open(binin)");
      return -1;
   }

   struct pollfd pollfds[] = {
         {bininfd, POLLIN, 0},
   };
   auto&[pfd] = pollfds;

   while (1) {
      pfd.revents = 0;  // cleanup, shouldn't it be redundant
      int pollret = poll(pollfds, 1, -1);
      if (pollret > 0) {
         if (pfd.revents & POLLIN) {
            cerr << "(pfd.revents & POLLIN)" << endl;
            read_out_to_the_end(pfd.fd);
         }
      } else if (pollret == 0) {
         cerr << "poll timed out" << endl;
         continue;
      } else {
         cerr << "check for error" << endl;
         continue;
      }
   }
}

the output is

(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
............... etc ....................

live example

UPDATE:

  1. read_out_to_the_end() fixed. Thanks to @RemyLebeau
  2. it works (blocks) on fifos as I expect, but not on regular files. Why?

Solution

  • poll() or select() never block on regular files. They always return a regular file as "ready". If you want to use poll() to do what tail -f does, you're on the wrong track.

    Quoting from the SUSv4 standard:

    The poll() function shall support regular files, terminal and pseudo-terminal devices, FIFOs, pipes, sockets and [OB XSR] STREAMS-based files. The behavior of poll() on elements of fds that refer to other types of file is unspecified.

    Regular files shall always poll TRUE for reading and writing.

    Since using poll() or select() on regular files is pretty much useless, newer interfaces have tried to remedy that. On BSD, you could use kqueue(2) with EVFILT_READ, and on Linux inotify(2) with IN_MODIFY. The newer epoll(7) interface on Linux will simply error out with EPERM if you try to watch a regular file.

    Unfortunately, neither of those is standard.