How to see if a pipe is empty
Asked Answered
H

3

14

Assuming a pipe,

int pipe_fd[2];
pipe(pipe_fd);

We fork, and expect that one process will write into the pipe at an arbitrary time. In one of the processes, we want to be able to check the contents of the pipe without blocking.

i.e. While a typical read will block if nothing is present and the write end remains open. I want to go do other stuff and potentially even read a bit at a time, do some stuff, and then check back to see if there's more, a la:

close(pipe_fd[1]);

while(1){
    if(/**Check pipe contents**/){
        int present_chars = 0;    

        while( read(pipe_fd[0],&buffer[present_chars],1) != 0)
            ++present_chars;

        //do something

    }
    else
        //do something else
}
Harrisonharrod answered 11/12, 2012 at 0:18 Comment(4)
Since the pipe() function does not allow you to set non-blocking mode, you have to do that after the pipe is created with fcntl() and F_GETFL and F_SETFL plus O_NONBLOCK.Pulverize
Note that Linux also has the pipe2 function, which allows you to request nonblocking mode at the same time the pipe is created. This function is not yet standard but has been accepted for inclusion in the next version of POSIX since it's also necessary for atomically setting the close-on-exec flag at the same time the pipe is created.Diffident
@R.. no standardization still ..Dennison
@snr: The next issue is not released yet. The accepted text is in the bug tracker: austingroupbugs.net/view.php?id=411Diffident
D
15

Your logic is wrong in that read will not return 0 when it runs out of characters; instead, it will block until it receives more, unless you put the file in non-blocking mode, but then it will return -1 and set errno to EWOULDBLOCK or EAGAIN rather than returning 0. The only time read can ever return 0 is when the size argument was 0 or end-of-file has been reached. And, for pipes, end-of-file means the writing end of the pipe has been closed; end-of-file status does not occur just because there's not any input available yet.

With that said, the simplest way to check is:

if (poll(&(struct pollfd){ .fd = fd, .events = POLLIN }, 1, 0)==1) {
    /* data available */
}

but unless you're using nonblocking mode, you'll need to make this check before every single read operation. Passing a larger buffer to read rather than doing it a byte-at-a-time would eliminate most of the cost of checking.

Diffident answered 11/12, 2012 at 0:26 Comment(8)
According to read(3), "If [...] O_NONBLOCK is set, read() shall [...] set errno to [EAGAIN],", not EWOULDBLOCK. Well, excepted if we are using different implementations of it.Pergrim
The "correct" value is EWOULDBLOCK. Modern POSIX allows either, and allows both error macros to have the same value, which they do on Linux. See pubs.opengroup.org/onlinepubs/9699919799/functions/read.html Strictly speaking, a portable application should test for errno==EAGAIN || errno==EWOULDBLOCK.Diffident
Oh alright. I wasn't aware of that. Yet it's written in my manfile too... Thanks :)Pergrim
Yeah, you're right. I'm gonna do the same. And I voted you up since you are obviously more knowledgeable on the topic. ;)Pergrim
And likewise on yours, since you actually covered how to use nonblocking mode.Diffident
Isn't it a shame that the argument named nfds has a different meaning for poll() and for select()?Singular
@Palo: Depending on your interpretation, maybe, but select is unsafe and should be considered deprecated so it's not really important except for reading historical sources. Not sure how that comment ties into this answer, though...Diffident
thanks for pointing that out, so I should use poll() instead of select(). Not much connection to the original answer, just clarifying the meaning of the argument for the two releated calls, but it helped me to see I should not use select() so thanks again.Singular
P
4

You can check if there is data to be read with the read() function. From read(3):

When attempting to read from an empty pipe or FIFO:

* If some process has the pipe open for writing and
O_NONBLOCK is set, read() shall return -1 and set
errno to [EAGAIN].

* If some process has the pipe open for writing and
O_NONBLOCK  is  clear,  read() shall block the calling
thread until some data is written or the pipe is
closed by all processes that had the pipe open for
writing.

The read() function shall fail if:

EAGAIN or EWOULDBLOCK

    The file descriptor is for a socket, is marked
    O_NONBLOCK, and no data is waiting to be received.

So if you set O_NONBLOCK, you will be able to tell if something is to be read on the pipe, by simply calling read().

As a reminder, from open(3):

SYNOPSIS
    int open(const char *path, int oflag, ...  );

DESCRIPTION
    Values for oflag are constructed by a
    bitwise-inclusive OR of flags from the following
    list, defined in <fcntl.h>. Applications shall
    specify exactly one  of  the first three values
    (file access modes) below in the value of oflag:

    O_NONBLOCK [...]

I hope it helps.

Pergrim answered 11/12, 2012 at 0:35 Comment(0)
S
3

R..'s answer is good however poll returns the number of file descriptor structs that have flags set in "revents". This will be 1 if you can read from fd but will also be 1 if any of the error flags are set. This means R..'s answer will say the pipe is readable if it ever enters an error state. A more robust check could be something like this:

bool canReadFromPipe(){
    //file descriptor struct to check if POLLIN bit will be set
    //fd is the file descriptor of the pipe
    struct pollfd fds{ .fd = fd, .events = POLLIN };
    //poll with no wait time
    int res = poll(&fds, 1, 0);
    
    //if res < 0 then an error occurred with poll
    //POLLERR is set for some other errors
    //POLLNVAL is set if the pipe is closed
    if(res < 0 || fds.revents & (POLLERR | POLLNVAL))
    {
        //an error occurred, check errno
    }
    return fds.revents & POLLIN;
}
Schizogony answered 19/11, 2018 at 9:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.