1

I have two pipes that both get different data at random points. What I want is to print the content from both pipes to stdout.

              __________________
   pipe1     |                  |
]==============>               -----------.
             |                  |          \
             |     process1     |           -----> stdout
   pipe2     |                  |          /
]==============>               -----------´
             |__________________|

My code looks about this in process1:

while (1) {
  read(pipe1[0], &buff, sizeof(char));  
  write(1, &buff, sizeof(char));
  read(pipe2[0], &buff2, sizeof(char)); 
  write(1, &buff2, sizeof(char));
}

However that doesn't work as one read() can be blocked(if no data is coming) while data comes from the other pipe.

How can I print simultaneously from both pipes without being blocked in one pipe? Or any other suggestion on how to solve this is welcome.

Pithikos
  • 18,827
  • 15
  • 113
  • 136
  • Reading and writing one character at a time is inefficient. `sizeof(char)` is guaranteed to be 1 by § 6.5.3.4-1 of the C standard. – outis Nov 21 '11 at 13:46
  • Well I can't see how else I could just read lines(instead of chars) as the length is going to be variable. – Pithikos Nov 21 '11 at 16:13
  • Read blocks of data rather than lines or characters. Updated my answer to make this more explicit. – outis Nov 21 '11 at 23:20

2 Answers2

3

Use select to wait on both sockets. When data is ready, it will tell you which pipes have data available.

void setnonblocking(int fd) {
    int opts;

    opts = fcntl(fd,F_GETFL);
    if (opts < 0) {
        perror("Couldn't get file descriptor flags");
        exit(EXIT_FAILURE);
    }
    opts = (opts | O_NONBLOCK);
    if (fcntl(fd,F_SETFL,opts) < 0) {
        perror("Couldn't set file descriptor to non-blocking");
        exit(EXIT_FAILURE);
    }
    return;
}

#ifndef BUFSIZE
#  define BUFSIZE 1024
#endif
void cat(fd_set* waiting, int fd) {
   static char buf[BUFSIZE];

   int readCnt;
   if (FD_ISSET(fd, waiting)) {
       while ((readCnt = read(fd, buf, BUFSIZE)) > 0) {
           write(stdout, buf, readCnt);
       }
       if (readCnt < 0) {
           perror("Error reading from pipe");
       }
   }
}

...
{
    fd_set pipes, readable;

    setnonblocking(pipes1[0]);
    setnonblocking(pipes2[0]);

    FD_ZERO(&pipes);
    FD_SET(pipe1[0],&pipes);
    FD_SET(pipe2[0],&pipes);

    int ready;
    while (1) {
        if ((ready = select(2, &pipes, NULL, NULL, NULL)) > 0) {
            cat(&pipes, pipe1[0]);
            cat(&pipes, pipe2[0]);
        } else {
            // no time limit, so there was an error
            perror("Error waiting for input");
            exit(EXIT_FAILURE);
        }
        FD_SET(pipe1[0],&pipes);
        FD_SET(pipe2[0],&pipes);
    }
}

Note the above runs forever unless there's an error. You will likely want your program to stop at some point.

outis
  • 75,655
  • 22
  • 151
  • 221
  • I don't think it's necessary to set the pipes non-blocking. You should just use the FD_ISSET macro on each pipe to see which has data, and just cat that one. – ams Nov 21 '11 at 16:11
  • @ams: setting the pipes to non-blocking is necessary to keep the `read` calls from blocking. – outis Nov 21 '11 at 23:16
  • not if you don't call read unnecessarily - the whole point of select is that it tells you *which* pipe has data. – ams Nov 22 '11 at 09:13
  • @ams: I'm not referring to reading from file descriptors that don't have waiting data. There are (rare) instances where a `read` can block, even without "unnecessary" reads. – outis Nov 22 '11 at 10:23
  • ... Replacing the read loop with a single call to `read` would reduce the possibility that the [read calls would block](http://stackoverflow.com/questions/5351994/), but it wouldn't cut it out entirely. – outis Nov 22 '11 at 10:37
  • if the return value from `select` is non-zero then read will not block on that pipe. You just have to use `FD_ISSET` to figure out which. – ams Nov 22 '11 at 11:00
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/5228/discussion-between-outis-and-ams) – outis Nov 22 '11 at 11:02
2

You need to multiplex the inputs. The relevant system call is poll or select (or ppollor pselect). The select tutorial is useful to read.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547