Reputation: 51
I am using select system to wait for input.Also i am doing this in a loop. Here is the code.
int main()
{
fd_set rfds;
struct timeval tv;
FD_ZERO(&rfds);
FD_SET(0,&rfds);
tv.tv_sec = 5;
tv.tv_usec = 0;
while(1)
{
select(1,&rfds,NULL,NULL,&tv);
if(FD_ISSET(0,&rfds))
{
write(STDOUT_FILENO,"yes",3);
FD_CLR(0,&rfds);
}
tv.tv_sec = 5;
tv.tv_usec = 0;
}
return 0;
}
Now the problem is that the select call is working fine only for the first time.If I give input within the first 5 sec I get yes as output but then in the following iterations fd(0) remains unset irrespective of whether I provide any input or not.Any idea how I can solve this problem.
Upvotes: 1
Views: 3428
Reputation: 559
Once getting a data from stdin the code clears read file descriptor set(rfds) using FD_CLR(0,&rfds) command so in the next loop there your read file descriptor is empty
Upvotes: 0
Reputation: 1
On some implementations select(2) is allowed to modify the file descriptor sets and the timeout. So you should set these inside the loop just before the select
while(1) {
FD_ZERO(&rfds);
FD_SET(0,&rfds);
tv.tv_sec = 5;
tv.tv_usec = 0;
int ns = select(1,&rfds,NULL,NULL,&tv);
if (ns < 0 && errno == EINTR) continue;
else if (ns < 0) { perror("select"); exit(EXIT_FAILURE); };
You need to keep the result ns
of select
, and you need to handle error cases. You probably should inspect rfds
and tv
after your select
-when it is successful (ns>0
)...
As I commented, you should use poll(2) instead of select
(since poll
is more C10K problem friendly, and since the size of your system's fd_set
is compile-time limiting the highest file descriptor)
Notice that if your stdin is a tty things are quite complex (since usually tty-s are kernel buffered, see the tty demystified page).
Upvotes: 1
Reputation: 44063
There are two problems with your code.
select
on empty file descriptor setsThe first is that select
modifies the file descriptor sets it is given -- after select
returns, they contain the file descriptors that are ready for I/O. This means that if the timeout passes without any input on stdin, rfds
will be empty, and the next call to select
will wait for input on an empty file descriptor set -- where it will never find any.
Input on stdin would keep STDIN_FILENO
(which is 0
) in the set, but since in the event that it turns up you call
FD_CLR(0,&rfds);
to remove stdin from the set, select
will wait on an empty fd set in that case as well. I know why you put that there, though, and it is related to the second problem (see below). The fix for this first problem, in any case, is to put stdin back into the fd set before you call select
again:
FD_SET(0, &rfds);
The second problem is that while your program waits for input on stdin, it never consumes any. This means that if you fix the file descriptor set before every call to select
with FD_SET(0, &rfds);
, your program will end up printing "yes" over and over in an infinite loop.
This is because once input is waiting to be consumed in stdin, calling select
on that file descriptor will result in select
checking if there's input waiting, recognizing that yes, there is, telling you about that fact, whereupon you don't do anything with it and just ask select
to check if it is still there. Which it is, no matter how often you check.
I don't know how exactly you want to consume the input, so this next bit is guesswork. I'm assuming that you want the program to write "yes" if the user entered something. When a user entered something is not always clearly defined, but a common interpretation is to say that user input is typically line-based -- the user expects things to happen once he pressed return. One sane method, then, would be to discard until the next newline when data appeared, so that every press on the return button yields a "yes". That might look like this:
while ((c = getchar()) != EOF) && c != '\n'); // discard until end-of-line
with int c;
.
In summary, this might do what you want:
#include <sys/select.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
fd_set rfds;
struct timeval tv;
int c;
FD_ZERO(&rfds);
FD_SET(0,&rfds);
tv.tv_sec = 5;
tv.tv_usec = 0;
while(1)
{
select(1,&rfds,NULL,NULL,&tv);
if(FD_ISSET(0, &rfds))
{
puts("yes");
while((c = getchar()) != EOF && c != '\n'); // consume input
} else {
puts("no");
FD_SET(0, &rfds); // place stdin back in the fd set
}
tv.tv_sec = 5;
tv.tv_usec = 0;
}
return 0;
}
Upvotes: 3