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:
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.
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
.