clibeventlibev

ev_timer callback is called earlier than it should be


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?


Solution

  • 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);