epoll with timerfd

Tengchao picture Tengchao · Aug 24, 2012 · Viewed 10k times · Source

I want to use the it_interval of newValue to set the interval of the timeout.
But in my example, I can only print timeout once.
What happened? How can I set the interval?

This is my code:

int main()
{
int efd =epoll_create(256);             
setnonblock(efd);
struct epoll_event ev,events[256];

int tfd;//timer fd

if((tfd= timerfd_create(CLOCK_MONOTONIC,TFD_NONBLOCK)) < 0)
  cout<<"timerfd create error"<<endl;

struct itimerspec newValue;
struct itimerspec oldValue;
bzero(&newValue,sizeof(newValue));  
bzero(&oldValue,sizeof(oldValue));
struct timespec ts;
ts.tv_sec = 5;
ts.tv_nsec = 0;

    //both interval and value have been set
    newValue.it_value = ts; 
    newValue.it_interval = ts;
    if( timerfd_settime(tfd,0,&newValue,&oldValue) <0)
    {
        cout<<"settime error"<<strerror(errno)<<endl;
    }   

    ev.data.fd = tfd;
    ev.events = EPOLLIN | EPOLLET;

    if( epoll_ctl(efd,EPOLL_CTL_ADD,tfd,&ev) < 0)
        cout<<"epoll_ctl error"<<endl;

    int num = 0;
    while(1)
    {
       if((num=epoll_wait(efd,events,256,1000)) > 0)
       {//justice
            for(int i=0;i<num;i++)
            {
                if(events[i].data.fd == tfd)
                {
                    cout<<"timeout"<<endl;
                }
        }       
    }
    }   
return 0;
}

Answer

Armin Rigo picture Armin Rigo · Sep 2, 2012

It is because you are using EPOLLET and not read()ing the data produced into the tfd. The expiration of a timer "writes" 8 bytes of data that need to be read: you really need to read it. Add this when you print "timeout":

uint64_t value;
read(tfd, &value, 8);

In more details: EPOLLET asks for Edge Triggering, which means that epoll_wait() will say only once "data is ready for input" on the file descritor tfd until you read that data. In other words, as long as you didn't read that data, future calls to epoll_wait() will not return the same descriptor again. This behavior is useful with normal sockets, e.g. if you do epoll_wait() in the main thread, notice some data is ready, then fire up another thread to read it. The main thread goes immediately back to epoll_wait(). But we don't want it to wake up immediately again, even though the data from the file descriptor was probably not read yet.

Note that I guess your example without EPOLLET would be wrong too, differently: because you don't read(), the tfd is always readable after the initial delay, and so it would print "timeout" as fast as possible after the initial delay expires.