According to Linux Programmer's Manual, poll
can wait for one of a set of file descriptors to become ready to perform I/O.
According to my understanding, if I add POLLIN
to events
, poll
will return with a > 0
integer, when there is at least one fd which is ready to be read.
Consider the following code, In this code, I want the program echos my input immediately after I typed the character \n
.
int main(){
char buffer[maxn];
while (true) {
struct pollfd pfd[1];
std::memset(pfd, 0, sizeof pfd);
pfd[0].fd = STDIN_FILENO;
pfd[0].events = POLLIN;
int ret = poll(pfd, 1, 1000);
if (ret < 0) {
}
else if (ret == 0) {
}
else {
if ((pfd[0].revents & POLLIN) == POLLIN) {
int n;
n = fscanf(stdin, "%s", &buffer);
if(n > 0){
printf("data from stdin: %s\n", buffer);
}
}else if((pfd[1].revents & POLLHUP) == POLLHUP){
break;
}
}
}
}
When I type
aa bb cc dd
I thought fscanf
hasn't retrieved all data from stdin, because it only reads aa
. So when the loop restarts, stdin's fd should still be ready. As a consequence, (pfd[0].revents & POLLIN) == POLLIN
still stands, so I thought we can see the following output
data from stdin: aa
data from stdin: bb
data from stdin: cc
data from stdin: dd
However, actually only the first line is printed. I got strange here, I think this is similar with epoll
's Edge-triggered mode. However, poll
is level-triggered.
So can you explain why this happens with fscanf
?
Polling works at the file descriptor level while fscanf
works at the higher file handle level.
At the higher level, the C runtime library is free to cache the input stream in such a way that it would affect what you can see at the lower level.
For example (and this is probably what's happening here), the first time you fscanf
your word aa
, the entire line is read from the file descriptor and cached, before that first word is handed back to you.
A subsequent fscanf
(with no intervening poll
) would first check the cache to get the next word and, if it weren't there, it would go back to the file descriptor to get more input.
Unfortunately, the fact that you're checking for a poll event before doing this is causing problems. As far as the file descriptor level goes, the entire line has been read by your first fscanf
so no further input is available - poll
will therefore wait until such information does become available.
You can see this in action if you change:
n = fscanf(stdin, "%s", buffer);
into:
n = read(STDIN_FILENO, buffer, 3);
and change the printf
to:
printf("data from stdin: %*.*s\n", n, n, buffer);
In that case, you do get the output you expect as soon as you press the ENTER key:
data from stdin: aa
data from stdin: bb
data from stdin: cc
data from stdin: dd
Just keep in mind that sample code is reading up to three characters (like aa<space>
) rather than a word. It's more to illustrate what the problem is rather than give you the solution (to match your question "Can you explain why this happens?").
The solution is not to mix descriptor and handle based I/O when the caching of the latter can affect the former.