Question

I have a problem with ev_timer of libev, please look at the code below:

static void timeout_cb(struct ev_loop *loop, ev_timer *timer, int revent) {
    printf("got an timeout event, current time %s\n", get_current_time());
}

int main(int argc, char *argv[]) {
    struct ev_loop *loop = ev_loop_new(0);
    ev_timer_init(&timer, timeout_cb, 5.0, 0.0);
    ev_timer_start(loop, &timer);
    ev_run(loop, EVRUN_NOWAIT);  // callback should not be called
    ev_timer_stop(loop, &timer); // stop the timer
    sleep(5);  // sleep 5 seconds, 5 is also the timer's timeout value
    // restart timer
    ev_timer_init(&timer, timeout_cb, 5.0, 0.0);
    ev_timer_start(loop, &timer);
    printf("timer start at: %s\n", get_current_time());
    printf("timer remaining: %f\n", ev_timer_remaining(loop, &timer));
    ev_run(loop, EVRUN_NOWAIT);
    return 0;
}

The output is:

timer start at: 14:53:49:137
timer remaining: 5.0000
got an timeout event, current time 14:53:49:137

This is strange, because after restart the timer the timer is trigger immediately, but it should be 5 second later. I found the reason is sleep(5). I change it to sleep(4) then the timer callback will not be called. I'm confused about the liebev's timer functionality. Do I have misunderstand about the timer? And how to let timer callback called after timeout elapse after timer is restart?

Was it helpful?

Solution 2

I don't claim to be an expert in libev, but I found that using the ev_suspend(loop) and ev_resume(loop) will prevent the behaviour you are seeing. From the man page:

ev_suspend (loop)
ev_resume (loop)
    These two functions suspend and resume an event loop, for use when
    the loop is not used for a while and timeouts should not be
    processed.

E.g.:

ev_run(loop, EVRUN_NOWAIT);  // callback should not be called
ev_timer_stop(loop, &timer); // stop the timer
ev_suspend(loop); // suspend timer processing
sleep(5);  // sleep 5 seconds, 5 is also the timer's timeout value
ev_resume(loop); // resume timer processing
// restart timer
ev_timer_init(&timer, timeout_cb, 5.0, 0.0);
ev_timer_start(loop, &timer);

Also I found that another possible solution is to use ev_timer_again() instead of ev_timer_start() which also prevents the behaviour you reported.

E.g.:

ev_timer_stop(loop, &timer); // stop the timer
sleep(5);  // sleep 5 seconds, 5 is also the timer's timeout value
// restart timer
ev_timer_again(loop, &timer);

OTHER TIPS

You should change the code

ev_timer_init(&timer, timeout_cb, 5.0, 0.0);

to

ev_timer_init(&timer, timeout_cb, ev_time() - ev_now() + 5.0, 0.0);

because libev will cache the timestamp for performance reason, and only update the timestamp before or after every event loop. After you sleep five seconds, the current real time will be T + 5, that is ev_time, but the current timestamp of libev is still T, that is ev_now. so then you init a timer like this

ev_timer_init(&timer, timeout_cb, 5.0, 0.0);

libev will set triggered time of the timer at current timestamp plus five, that is T + 5, also is current real time, so it will be triggered immediately. but when you init the timer like this

ev_timer_init(&timer, timeout_cb, ev_time() - ev_now() + 5.0, 0.0);

libev will set triggered time of the timer at current time stamp puls ev_time() - ev_now() + 5.0, that is T + T + 5 - T + 5 = T + 5 + 5 = real time + 5. so it will be triggered after five seconds.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top