4

I am new to unix programming and today I am trying epoll but getting stuck in a problem.

Under level-triggered mode, I think each new input event including Ctrl-D will cause epoll_wait to return. It works fine. But when I type somethings like aaa, follow with Ctrl-D, read blocks. When I type Ctrl-D, it doesn't.

Could you please explain what happens?

And should I read off all data when epoll_wait is done and according fd is ready?

Thanks!

#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

int main(int argc, const char *argv[]) {
    // create event
    struct epoll_event stdin_ev, events[10];

    // set event
    stdin_ev.events = EPOLLIN;
    stdin_ev.data.fd = STDIN_FILENO;

    // create epoll
    int epfd = epoll_create(1), i, rcnt;
    char c;

    // set monitoring STDIN_FILENO 
    epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev);

    while(1) {
        int ret = epoll_wait(epfd, events, 1, 1000);

        // timeout or failed
        if(ret == 0) {
            fprintf(stdout, "timeout\n");
            continue;
        } else if (ret < 0) {
            perror("ret<0");
            exit(EXIT_FAILURE);
        }

        // readable
        fprintf(stdout, "%d event(s) happened...\n", ret);
        for(i=0; i < ret; i++) {
            if(events[i].data.fd == STDIN_FILENO &&\
               events[i].events&EPOLLIN) {
                // read a char
                rcnt = read(STDIN_FILENO, &c, 1); 
                // if read 0 char, EOF?
                if(rcnt != 1) {
                    fprintf(stdout, "read %d byte\n", rcnt);
                    continue;
                }
                // else print ascii
                fprintf(stdout, "ascii code: %d\n", c);
            }
        } 
    }
    close(epfd);
    return 0;
}

input: aaa+Ctrl-D, result:

timeout
aaa // <-- `aaa`+`Ctrl-D`
1 event(s) happened...
ascii code: 97
1 event(s) happened...
ascii code: 97
1 event(s) happened...
ascii code: 97
1 event(s) happened...
^C // <-- read block here, `Ctrl-C` to kill

Then I try setting STDIN_FILENO to be nonblocking and I find that epoll_wait still tells there is a readable event although read() returns -1. But if I just type Ctrl-D, read() return 0.

#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

int set_nonblock(int sfd) {
    int flags, s;
    flags = fcntl(sfd, F_GETFL, 0);
    if(flags == -1) {
        perror("fcntl");
        return -1;
    }
    flags |= O_NONBLOCK;
    s = fcntl(sfd, F_SETFL, flags);
    if(s == -1) {
        perror("fcntl");
        return -1;
    }
    return 0;
}

int main(int argc, const char *argv[]) {
    // create event
    struct epoll_event stdin_ev, events[10];

    // set event
    stdin_ev.events = EPOLLIN;
    stdin_ev.data.fd = STDIN_FILENO;

    // create epoll
    int epfd = epoll_create(1), i, rcnt;
    char c;

    // set nonblocking
    if(set_nonblock(STDIN_FILENO) != 0) {
        exit(EXIT_FAILURE);
    };

    // set monitoring STDIN_FILENO 
    epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev);

    while(1) {
        int ret = epoll_wait(epfd, events, 1, 1000);

        // timeout or failed
        if(ret == 0) {
            fprintf(stdout, "timeout\n");
            continue;
        } else if (ret < 0) {
            perror("ret<0");
            exit(EXIT_FAILURE);
        }

        // readable
        fprintf(stdout, "%d event(s) happened...\n", ret);
        for(i=0;i < ret;i++) {
            if(events[i].data.fd == STDIN_FILENO &&\
               events[i].events&EPOLLIN) {
                // read a char
                rcnt = read(STDIN_FILENO, &c, 1); 
                // if read 0 char, EOF?
                if(rcnt != 1) {
                    fprintf(stdout, "read %d byte\n", rcnt);
                    continue;
                }
                // else print ascii
                fprintf(stdout, "ascii code: %d\n", c);
            }
        } 
    }
    close(epfd);
    return 0;
}

result:

timeout
1 event(s) happened... // <-- `Ctrl-D`
read 0 byte // <-- read() -> 0
timeout
timeout
aaa // `aaa`+`Ctrl-D`
1 event(s) happened...
ascii code: 97
1 event(s) happened...
ascii code: 97
1 event(s) happened...
ascii code: 97
read -1 byte // `EPOLLIN` still happens.
timeout
^C
Hayes Pan
  • 585
  • 1
  • 4
  • 19
  • 1
    Does [Ctrl+D for ending terminal line input](http://unix.stackexchange.com/a/177661) answer help? – neeru Apr 19 '15 at 08:41
  • @neeru thanks! I know that when type `Ctrl+D`, 3 characters(`aaa`) will be sent. So I think `epoll` should return 3 times and print `97` per loop. But what confused me is that it returns 4 times and the last read call blocks. – Hayes Pan Apr 19 '15 at 08:56
  • I am not an expert in `epoll` but I think that this can be due to the specificity of the standard input. Stdin is not a file like others and it probably confuses `epoll`. – Marian Apr 19 '15 at 09:51
  • handle the error event (-1) properly. – Karoly Horvath Apr 19 '15 at 09:51
  • @KarolyHorvath But I don't monitor error event. – Hayes Pan Apr 19 '15 at 09:54
  • @HayesPan: "read -1 byte" – Karoly Horvath Apr 19 '15 at 09:59
  • @KarolyHorvath OK. What really confuses is that there are only 3 characters(`aaa`) but `epoll_wait` returns 4 times(`EPOLLIN`). – Hayes Pan Apr 19 '15 at 10:04
  • Tries setting the `O_NONBLOCK`? It is a bad idea to mix file descriptors without `O_NONBLOCK` and async facilities. Spurious wake-ups happen and should be handled (OS bugs, particular device quirks, etc). Blocking file descriptors do not allow graceful handling for that. – Dummy00001 Apr 19 '15 at 12:05
  • @Dummy00001 thanks~ I've tried the `O_NONBLOCK` and it works(nonblock anymore) while the werid behavior of `epoll` still confuses. – Hayes Pan Apr 19 '15 at 12:21
  • Interesting is that `select` has the same behavior. – Marian Apr 19 '15 at 13:07
  • @Marian I really suspect that it may be a specific rule... – Hayes Pan Apr 19 '15 at 13:20
  • When read() returns -1 : inspect errno. Could be EINTR / EAGAIN. – joop Apr 24 '15 at 10:26
  • @joop. It is EAGAIN. It so happens that CTRL-D is an event for which epoll wakes up. But since its a "signalling" message, and no data, `read` returns -1 – Prabhu Apr 24 '15 at 11:55

2 Answers2

1

As I understand the behavior:

If instead of ctrl-D you type 'ENTER', 4 events are reported as with CTRL-D. We see ascii code:10 for the line feed. With CTRL-D read blocks.

CTRL-D isn't signalling EOF but rather flushing out the data so far input. CTRL-D itself being recognized as an event. But actually no data to be pulled out on that fd. And given that the socket is blocking, we end up in case where read does not return until another set of events happen.

Now if CTRL-D is the first event, it is recognised with read yeilding zero. Signaling a EOF condition. This does not happen if there were something to be flushed.

When you made the socket non-blocking, CTRL-D read returns -1, with errno set to EAGAIN. Which means 'no data to be read now. try later'.

Prabhu
  • 3,443
  • 15
  • 26
0

From the manual (epoll_ctl):

EPOLLERR
    Error condition happened on the associated file descriptor.
    epoll_wait(2) will always wait for this event;
    it is not necessary to set it in events.

As you can see, error conditions are always monitored.

Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176