Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why is this select() blocking?

I’m getting blocked at a select() while working with a pipe. The first select() will unblock when we get data on stdin (I just type one letter and press Enter).

Then I write data to the write-end of a pipe, but the select() doesn’t recognize any data on the pipe’s read fd. So it blocks indefinitely.

int    fd_pipe[2];
char   buf[20];
fd_set set;

pipe(fd_pipe);

FD_ZERO(&set);
FD_SET(fd_pipe[0], &set); // Pipe read fd
FD_SET(0         , &set); // stdin fd

select(fd_pipe[0]+1, &set, NULL, NULL, NULL); // I type 1 char + ENTER to get past this point

if (FD_ISSET(0, &set))
{
    read(0, buf, 20);          // Can confirm we do get here
    write(fd_pipe[1], buf, 1); // Lets put just one character in the pipe
}

select(fd_pipe[0]+1, &set, NULL, NULL, NULL); // <-- We get stuck here

if (FD_ISSET(fd_pipe[0], &set))
{
    char d;
    read(0, &d, 1);
    assert(d == buf[0]);
}

If I do this without select(), things work fine:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

int fd_pipe[2];
pipe(fd_pipe);

char c = 'x';
write( fd_pipe[1], &c, 1);

c = 'a';
read( fd_pipe[0], &c, 1);

assert(c == 'x');

In the larger picture, I have a multi-threaded program which is using select() and pipe() to emulate canceling a read() operation. I write to the pipe with a single character with the intent of forcing select() to return so I can shutdown the operation instead of trying to send a signal to cancel a blocking read() on the main FD. But select() doesn’t return.

>Solution :

The select function modifies the sets you pass to it. When select return the sets will contain only the active descriptors.

In your case only STDIN_FILENO will be set, so the second call to select will not have fd_pipe[0] in it.

The solution is actually not to re-add the pipe to the set and call select a second time, but to only call select once:

FD_ZERO(&set);
FD_SET(fd_pipe[0], &set); // Pipe read fd
FD_SET(0         , &set); // stdin fd

select(fd_pipe[0]+1, &set, NULL, NULL, NULL); // I type 1 char + ENTER to get past this point

if (FD_ISSET(0, &set))
{
    read(0, buf, 20);          // Can confirm we do get here
    write(fd_pipe[1], buf, 1); // Lets put just one character in the pipe
}
else if (FD_ISSET(fd_pipe[0], &set))
{
    char d;
    read(fd_pipe[0], &d, 1);
    assert(d == buf[0]);
}

You also need to check what select actually returns. It could return -1 which means there’s an error.


If you do need to call select more than once. Then you’ll need to re-zero/re-set the fd_set.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading