title: Reading from multiple nonblocking named pipes in Linux
body: <p>Building on a similar example located <a href="https://stackoverflow.com/questions/14594508/fifo-pipe-is-always-readable-in-select">here</a> in stackoverflow,
  I have three named pipes, pipe_a, pipe_b, and pipe_c that are being fed from external processes.  I'd like to have a reader process that outputs to the console, whatever is written to any of these pipes.</p>
  <p>The program below is an all-in-one c program that should read the three pipes in a non-blocking manner, and display output when any one of the pipes gets new data.</p>
  <p>However, it isn't working - it is blocking!  If pipe_a gets data, it will display it and then wait for new data to arrive in pipe_b, etc...</p>
  <p>select() should allow the monitoring of multiple file descriptors until one is ready, at which time we should drop into the pipe's read function and get the data.</p>
  <p>Can anyone help identify why the pipes are behaving like they are in blocking mode?</p>
  <pre><code>/*
   * FIFO example using select.
   *
   * $ mkfifo /tmp/fifo
   * $ clang -Wall -o test ./test.c
   * $ ./test &amp;
   * $ echo 'hello' &gt; /tmp/fifo
   * $ echo 'hello world' &gt; /tmp/fifo
   * $ killall test
   */
  
  #include &lt;sys/types.h&gt;
  #include &lt;sys/select.h&gt;
  #include &lt;errno.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;fcntl.h&gt;
  #include &lt;unistd.h&gt;
  
  
  // globals
  int fd_a, fd_b, fd_c;
  int nfd_a, nfd_b, nfd_c;
  fd_set set_a, set_b, set_c;
  char buffer_a[100*1024];
  char buffer_b[100*1024];
  char buffer_c[100*1024];
  
  
  int readPipeA()
  {
  ssize_t bytes;
  size_t total_bytes;
  
  if (FD_ISSET(fd_a, &amp;set_a)) {
      printf(&quot;\nDescriptor %d has new data to read.\n&quot;, fd_a);
      total_bytes = 0;
      for (;;) {
          printf(&quot;\nDropped into read loop\n&quot;);
          bytes = read(fd_a, buffer_a, sizeof(buffer_a));
          if (bytes &gt; 0) {
              total_bytes += (size_t)bytes;
              printf(&quot;%s&quot;, buffer_a);
          } else {
              if (errno == EWOULDBLOCK) {
                  printf(&quot;\ndone reading (%ul bytes)\n&quot;, total_bytes);
                  break;
              } else {
                  perror(&quot;read&quot;);
                  return EXIT_FAILURE;
              }
          }
      }
  }
  }
  
  int readPipeB()
  {
  ssize_t bytes;
  size_t total_bytes;
  
  if (FD_ISSET(fd_b, &amp;set_b)) {
      printf(&quot;\nDescriptor %d has new data to read.\n&quot;, fd_b);
      total_bytes = 0;
      for (;;) {
          printf(&quot;\nDropped into read loop\n&quot;);
          bytes = read(fd_b, buffer_b, sizeof(buffer_b));
          if (bytes &gt; 0) {
              total_bytes += (size_t)bytes;
              printf(&quot;%s&quot;, buffer_b);
          } else {
              if (errno == EWOULDBLOCK) {
                  printf(&quot;\ndone reading (%ul bytes)\n&quot;, total_bytes);
                  break;
              } else {
                  perror(&quot;read&quot;);
                  return EXIT_FAILURE;
              }
          }
      }
  }
  }
  
  int readPipeC()
  {
  ssize_t bytes;
  size_t total_bytes;
  
  if (FD_ISSET(fd_c, &amp;set_c)) {
      printf(&quot;\nDescriptor %d has new data to read.\n&quot;, fd_c);
      total_bytes = 0;
      for (;;) {
          printf(&quot;\nDropped into read loop\n&quot;);
          bytes = read(fd_c, buffer_c, sizeof(buffer_c));
          if (bytes &gt; 0) {
              total_bytes += (size_t)bytes;
              printf(&quot;%s&quot;, buffer_c);
          } else {
              if (errno == EWOULDBLOCK) {
                  printf(&quot;\ndone reading (%ul bytes)\n&quot;, total_bytes);
                  break;
              } else {
                  perror(&quot;read&quot;);
                  return EXIT_FAILURE;
              }
          }
      }
  }
  }
  
  
  int main(int argc, char* argv[])
      {
  
  
      // create pipes to monitor (if they don't already exist)
      system(&quot;mkfifo /tmp/PIPE_A&quot;);
      system(&quot;mkfifo /tmp/PIPE_B&quot;);
      system(&quot;mkfifo /tmp/PIPE_C&quot;);
  
  
      // open file descriptors of named pipes to watch
      fd_a = open(&quot;/tmp/PIPE_A&quot;, O_RDWR | O_NONBLOCK);
      if (fd_a == -1) {
      perror(&quot;open&quot;);
      return EXIT_FAILURE;
      }
      FD_ZERO(&amp;set_a);
      FD_SET(fd_a, &amp;set_a);
  
  
      fd_b = open(&quot;/tmp/PIPE_B&quot;, O_RDWR | O_NONBLOCK);
      if (fd_b == -1) {
      perror(&quot;open&quot;);
      return EXIT_FAILURE;
      }
      FD_ZERO(&amp;set_b);
      FD_SET(fd_b, &amp;set_b);
  
  
      fd_c = open(&quot;/tmp/PIPE_C&quot;, O_RDWR | O_NONBLOCK);
      if (fd_c == -1) {
      perror(&quot;open&quot;);
      return EXIT_FAILURE;
      }
      FD_ZERO(&amp;set_c);
      FD_SET(fd_c, &amp;set_c);
  
  
  
      for(;;)
      {
          // check pipe A
          nfd_a= select(fd_a+1, &amp;set_a, NULL, NULL, NULL);
          if (nfd_a) {
              if (nfd_a == -1) {
                  perror(&quot;select&quot;);
                  return EXIT_FAILURE;
              }
              readPipeA();
          }
  
          // check pipe B
          nfd_b= select(fd_b+1, &amp;set_b, NULL, NULL, NULL);
          if (nfd_b) {
              if (nfd_b == -1) {
                  perror(&quot;select&quot;);
                  return EXIT_FAILURE;
              }
              readPipeB();
          }
  
          // check pipe C
          nfd_c= select(fd_c+1, &amp;set_c, NULL, NULL, NULL);
          if (nfd_c) {
              if (nfd_c == -1) {
                  perror(&quot;select&quot;);
                  return EXIT_FAILURE;
              }
              readPipeC();
          }
      }
  
      return EXIT_SUCCESS;
  }
  </code></pre>
  <p>--- Updated Code ---</p>
  <p>Modified the application based on the feedback here, and some more reading:</p>
  <pre><code>    /*
       * FIFO example using select.
       *
       * $ mkfifo /tmp/fifo
       * $ clang -Wall -o test ./test.c
       * $ ./test &amp;
       * $ echo 'hello' &gt; /tmp/fifo
       * $ echo 'hello world' &gt; /tmp/fifo
       * $ killall test
       */
      
      #include &lt;sys/types.h&gt;
      #include &lt;sys/select.h&gt;
      #include &lt;sys/time.h&gt;
      #include &lt;sys/types.h&gt;
      #include &lt;errno.h&gt;
      #include &lt;stdlib.h&gt;
      #include &lt;stdio.h&gt;
      #include &lt;fcntl.h&gt;
      #include &lt;unistd.h&gt;
      
      
      int readPipe(int fd)
      {
          ssize_t bytes;
          size_t total_bytes = 0;
          char buffer[100*1024];
      
          printf(&quot;\nDropped into read pipe\n&quot;);
          for(;;) {
              bytes = read(fd, buffer, sizeof(buffer));
              if (bytes &gt; 0) {
                  total_bytes += (size_t)bytes;
                  printf(&quot;%s&quot;, buffer);
              } else {
                  if (errno == EWOULDBLOCK) {
                      printf(&quot;\ndone reading (%d bytes)\n&quot;, (int)total_bytes);
                      break;
                  } else {
                      perror(&quot;read&quot;);
                      return EXIT_FAILURE;
                  }
              }
          }
          return EXIT_SUCCESS;
      }
      
      
      int main(int argc, char* argv[])
      {
          int fd_a, fd_b, fd_c;   // file descriptors for each pipe
          int nfd;                // select() return value
          fd_set read_fds;        // file descriptor read flags
          struct timeval tv;
          tv.tv_sec = 0;
          tv.tv_usec = 0;
      
          // create pipes to monitor (if they don't already exist)
          system(&quot;mkfifo /tmp/PIPE_A&quot;);
          system(&quot;mkfifo /tmp/PIPE_B&quot;);
          system(&quot;mkfifo /tmp/PIPE_C&quot;);
      
          // open file descriptors of named pipes to watch
          fd_a = open(&quot;/tmp/PIPE_A&quot;, O_RDWR | O_NONBLOCK);
          if (fd_a == -1) {
              perror(&quot;open&quot;);
              return EXIT_FAILURE;
          }
      
          fd_b = open(&quot;/tmp/PIPE_B&quot;, O_RDWR | O_NONBLOCK);
          if (fd_b == -1) {
              perror(&quot;open&quot;);
              return EXIT_FAILURE;
          }
      
          fd_c = open(&quot;/tmp/PIPE_C&quot;, O_RDWR | O_NONBLOCK);
          if (fd_c == -1) {
              perror(&quot;open&quot;);
              return EXIT_FAILURE;
          }
      
          FD_ZERO(&amp;read_fds);
          FD_SET(fd_a, &amp;read_fds);  // add pipe to the read descriptor watch list
          FD_SET(fd_b, &amp;read_fds);
          FD_SET(fd_c, &amp;read_fds);
      
          for(;;)
          {
              // check if there is new data in any of the pipes
              nfd = select(fd_a+1, &amp;read_fds, NULL, NULL, &amp;tv);
              if (nfd != 0) {
                  if (nfd == -1) {
                      perror(&quot;select&quot;);
                      return EXIT_FAILURE;
                  }
      
                  if (FD_ISSET(fd_a, &amp;read_fds)) {
                      readPipe(fd_a);
                  }
              }
      
              nfd = select(fd_b+1, &amp;read_fds, NULL, NULL, &amp;tv);
              if (nfd != 0) {
                  if (nfd == -1) {
                      perror(&quot;select&quot;);
                      return EXIT_FAILURE;
                  }
      
                  if (FD_ISSET(fd_b, &amp;read_fds)){
                      readPipe(fd_b);
                  }
              }
              nfd = select(fd_c+1, &amp;read_fds, NULL, NULL, &amp;tv);
              if (nfd != 0) {
                  if (nfd == -1) {
                      perror(&quot;select&quot;);
                      return EXIT_FAILURE;
                  }
                  if (FD_ISSET(fd_c, &amp;read_fds)){
                      readPipe(fd_c);
                  }
              }
      
              usleep(10);
          }
          return EXIT_SUCCESS;
      }
  </code></pre>
  <p>Still having an issue with the select returning zero (0) when there is data waiting in any one of the watched pipes?  I must not be using the <code>select()</code> and <code>fd_isset()</code> correctly. Can you see what I'm doing wrong? Thanks.</p>