[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [Libevent-users] a dead looping bug when changing system time backward




On Apr 19, 2012, at 7:30 AM, Nick Mathewson wrote:

On Tue, Apr 10, 2012 at 7:21 PM, Nir Soffer <nirsof@xxxxxxxxx> wrote:

On Apr 10, 2012, at 6:15 PM, Nick Mathewson wrote:

And there's a third way for libevent to see a big jump forward in
time: if the program calls event_base_loop() sporadically, it is free
to wait as long as it wants between invocations.

So, what's the right behavior for periodic events in these cases? If there is an event that's supposed to run every 5 seconds, and time has
jumped forward by 16 seconds, it seems reasonable to run the event 1
time, or maybe even 3 times... but if time has jumped forward by one
day, it seems unlikely that the programmer really wants us to run the
event 17280 times.

Perhaps this argues for a cap on how far into the past we should be
willing to reschedule a periodic event, or how many "missed firings"
we'll compensate for before we drop some on the floor?


Forward jumps should simply skip events in the past. It is just like events
skipped because the event loop was blocked.

If you should run every 5 seconds, but woke up 21 seconds late, you cannot fix the past - you just missed the events at +5, +10, +15 and +20. Run the
next event at +25 according to the plan.

Other event loops like Cocoa NSRunloop and GNUStep NSRunloop use this logic.

I've implemented approximately this logic in branch
"20_periodic_event_overload" in my github repository.  (That's where I
do most of my work before merging it onto an official branch; it lives
at https://github.com/nmathewson/libevent .)

If I understand the code correctly, it does fix the unwanted loop, but is also changes the schedule of the timer, which may be unwanted.

If the timer missed few events (ev_timeout + dealy < now) then the next timeout (run_at) is now + delay.

So if we take the example of running every 5 seconds and then we miss the 20 seconds event and wake up at 21, the current code will schedule the next event at 26. Assuming that we have another delay at 42, the total run may look like this:

5	10	15	21	26	31	42	47 ...

What I expect from such timer is to keep running according to the original schedule when possible.

5	10	15	21	25	30	42	45 ...

Maybe it is not the best example for libevent, but lets say I'm writing a clock that should play a tone on each hour. According to the current code, after the first delay where I missed my deadline and played the tone few seconds later, I will continue to miss the deadline on each hour.

So a more correct implementation would be something like this (assuming that all values are integers instead of timevals):

        elapsed = now - ev->ev_timeout;
        intervals = elapsed / delay + 1;
        delta = intervals * delay;
        run_at = ev->ev_timeout + delta;

I think that most users will prefer this logic. If I wanted to wait at least 5 seconds after each run, I would use a non-persistent event, adding the event with a timeout after each run.

What do you think?

***********************************************************************
To unsubscribe, send an e-mail to majordomo@xxxxxxxxxxxxx with
unsubscribe libevent-users    in the body.