]> granicus.if.org Git - libevent/commitdiff
If time has jumped so we'd reschedule a periodic event in the past, schedule it for...
authorNick Mathewson <nickm@torproject.org>
Thu, 19 Apr 2012 04:25:12 +0000 (00:25 -0400)
committerNick Mathewson <nickm@torproject.org>
Thu, 19 Apr 2012 22:25:59 +0000 (18:25 -0400)
Fixes an issue reported on libevent-users in the thread "a dead
looping bug when changing system time backward". Previously, if time
jumped forward 1 hour[*] and we had a one-second periodic timer event,
that event would get invoked 3600 times.  That's almost certainly not
what anybody wants.

In a future version of Libevent, we should expose the amount of time
that the callbac kwould have been invoked somehow.

[*] Forward time jumps can happen with nonmonotonic clocks, or with
clocks that jump on suspend/resume.  It can also happen from
Libevent's point of view if the user exits from event_base_loop() and
doesn't call it again for a while.

event.c
test/regress.c

diff --git a/event.c b/event.c
index 3adbc089f3e04d430bcc5dd8fe26eedc99bcfd4f..df104554ff1a98e900a654c595f89af27d679c09 100644 (file)
--- a/event.c
+++ b/event.c
@@ -1280,9 +1280,17 @@ event_persist_closure(struct event_base *base, struct event *ev)
                        } else {
                                relative_to = now;
                        }
-
                }
                evutil_timeradd(&relative_to, &delay, &run_at);
+               if (evutil_timercmp(&run_at, &now, <)) {
+                       /* Looks like we missed at least one invocation due to
+                        * a clock jump, not running the event loop for a
+                        * while, really slow callbacks, or
+                        * something. Reschedule relative to now.
+                        */
+                       evutil_timeradd(&now, &delay, &run_at);
+               }
+               run_at.tv_usec |= usec_mask;
                event_add_internal(ev, &run_at, 1);
        }
        EVBASE_RELEASE_LOCK(base, th_base_lock);
index 2d48583d2d43f8a419e285357c570dd1a7544494..3d97d0b2cb429c0c9eaa3c795c9331df30921018 100644 (file)
@@ -627,6 +627,27 @@ test_persistent_timeout(void)
        event_del(&ev);
 }
 
+static void
+test_persistent_timeout_jump(void *ptr)
+{
+       struct basic_test_data *data = ptr;
+       struct event ev;
+       int count = 0;
+       struct timeval msec100 = { 0, 100 * 1000 };
+       struct timeval msec50 = { 0, 50 * 1000 };
+
+       event_assign(&ev, data->base, -1, EV_PERSIST, periodic_timeout_cb, &count);
+       event_add(&ev, &msec100);
+       /* Wait for a bit */
+       sleep(1);
+       event_base_loopexit(data->base, &msec50);
+       event_base_dispatch(data->base);
+       tt_int_op(count, ==, 1);
+
+end:
+       event_del(&ev);
+}
+
 struct persist_active_timeout_called {
        int n;
        short events[16];
@@ -2338,8 +2359,8 @@ struct testcase_t main_testcases[] = {
        BASIC(bad_assign, TT_FORK|TT_NEED_BASE|TT_NO_LOGS),
        BASIC(bad_reentrant, TT_FORK|TT_NEED_BASE|TT_NO_LOGS),
 
-       /* These are still using the old API */
        LEGACY(persistent_timeout, TT_FORK|TT_NEED_BASE),
+       { "persistent_timeout_jump", test_persistent_timeout_jump, TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
        { "persistent_active_timeout", test_persistent_active_timeout,
          TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
        LEGACY(priorities, TT_FORK|TT_NEED_BASE),