tevent: optimize adding new zero timer events
authorStefan Metzmacher <metze@samba.org>
Fri, 22 Feb 2013 11:45:39 +0000 (12:45 +0100)
committerKarolin Seeger <kseeger@samba.org>
Wed, 6 Mar 2013 09:12:02 +0000 (10:12 +0100)
Such events were used before we had immediate events.
It's likely that there're a lot of this events
and we need to add new ones in fifo order.

The tricky part is that tevent_common_add_timer()
should not use the optimization as it's used
by broken Samba versions, which don't use
tevent_common_loop_timer_delay() in source3/lib/events.c.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
lib/tevent/tevent.c
lib/tevent/tevent_epoll.c
lib/tevent/tevent_internal.h
lib/tevent/tevent_poll.c
lib/tevent/tevent_select.c
lib/tevent/tevent_timed.c

index 63d5f1536c9410e4133b483ee6853d2abee9700c..be0afd453bb4d5e9018cafbb6c413c3f6dbf1578 100644 (file)
@@ -190,6 +190,7 @@ int tevent_common_context_destructor(struct tevent_context *ev)
                DLIST_REMOVE(ev->fd_events, fd);
        }
 
+       ev->last_zero_timer = NULL;
        for (te = ev->timer_events; te; te = tn) {
                tn = te->next;
                te->event_ctx = NULL;
index 1ec97c5f7193867e332257f5487ff90ed7232365..599c190658cc5bd846f4f5d4fe82cc3cdc2f0bd6 100644 (file)
@@ -937,7 +937,7 @@ static const struct tevent_ops epoll_event_ops = {
        .set_fd_close_fn        = tevent_common_fd_set_close_fn,
        .get_fd_flags           = tevent_common_fd_get_flags,
        .set_fd_flags           = epoll_event_set_fd_flags,
-       .add_timer              = tevent_common_add_timer,
+       .add_timer              = tevent_common_add_timer_v2,
        .schedule_immediate     = tevent_common_schedule_immediate,
        .add_signal             = tevent_common_add_signal,
        .loop_once              = epoll_event_loop_once,
index 8433333558366af2c754823ce2df9cdf830ce28f..b239e7403a77c37ca78d3ab7f4e0b3814eb5a5bf 100644 (file)
@@ -263,6 +263,13 @@ struct tevent_context {
                tevent_trace_callback_t callback;
                void *private_data;
        } tracing;
+
+       /*
+        * an optimization pointer into timer_events
+        * used by used by common code via
+        * tevent_common_add_timer_v2()
+        */
+       struct tevent_timer *last_zero_timer;
 };
 
 const struct tevent_ops *tevent_find_ops_byname(const char *name);
@@ -292,6 +299,13 @@ struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev,
                                             void *private_data,
                                             const char *handler_name,
                                             const char *location);
+struct tevent_timer *tevent_common_add_timer_v2(struct tevent_context *ev,
+                                               TALLOC_CTX *mem_ctx,
+                                               struct timeval next_event,
+                                               tevent_timer_handler_t handler,
+                                               void *private_data,
+                                               const char *handler_name,
+                                               const char *location);
 struct timeval tevent_common_loop_timer_delay(struct tevent_context *);
 
 void tevent_common_schedule_immediate(struct tevent_immediate *im,
index 0175cae531a4d98f4b703fb9010ed79cc5fd2d30..92fcc441ac9aa1e8e6a35c470742a6104f148b4f 100644 (file)
@@ -694,7 +694,7 @@ static const struct tevent_ops poll_event_ops = {
        .set_fd_close_fn        = tevent_common_fd_set_close_fn,
        .get_fd_flags           = tevent_common_fd_get_flags,
        .set_fd_flags           = poll_event_set_fd_flags,
-       .add_timer              = tevent_common_add_timer,
+       .add_timer              = tevent_common_add_timer_v2,
        .schedule_immediate     = tevent_common_schedule_immediate,
        .add_signal             = tevent_common_add_signal,
        .loop_once              = poll_event_loop_once,
@@ -712,7 +712,7 @@ static const struct tevent_ops poll_event_mt_ops = {
        .set_fd_close_fn        = tevent_common_fd_set_close_fn,
        .get_fd_flags           = tevent_common_fd_get_flags,
        .set_fd_flags           = poll_event_set_fd_flags,
-       .add_timer              = tevent_common_add_timer,
+       .add_timer              = tevent_common_add_timer_v2,
        .schedule_immediate     = poll_event_schedule_immediate,
        .add_signal             = tevent_common_add_signal,
        .loop_once              = poll_event_loop_once,
index 5e265692763edc56bedf4569c22901392cc1cfdc..bfce246e22dbfc07760a4df51e7aeb8aecd04402 100644 (file)
@@ -264,7 +264,7 @@ static const struct tevent_ops select_event_ops = {
        .set_fd_close_fn        = tevent_common_fd_set_close_fn,
        .get_fd_flags           = tevent_common_fd_get_flags,
        .set_fd_flags           = tevent_common_fd_set_flags,
-       .add_timer              = tevent_common_add_timer,
+       .add_timer              = tevent_common_add_timer_v2,
        .schedule_immediate     = tevent_common_schedule_immediate,
        .add_signal             = tevent_common_add_signal,
        .loop_once              = select_event_loop_once,
index fd954f42d834cab3bc123c63e9972eeb9d6bd1ba..920d39fe063a102c9a4a8391b9eeb43745478e58 100644 (file)
@@ -133,13 +133,18 @@ struct timeval tevent_timeval_current_ofs(uint32_t secs, uint32_t usecs)
 */
 static int tevent_common_timed_destructor(struct tevent_timer *te)
 {
+       if (te->event_ctx == NULL) {
+               return 0;
+       }
+
        tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
                     "Destroying timer event %p \"%s\"\n",
                     te, te->handler_name);
 
-       if (te->event_ctx) {
-               DLIST_REMOVE(te->event_ctx->timer_events, te);
+       if (te->event_ctx->last_zero_timer == te) {
+               te->event_ctx->last_zero_timer = DLIST_PREV(te);
        }
+       DLIST_REMOVE(te->event_ctx->timer_events, te);
 
        return 0;
 }
@@ -153,12 +158,15 @@ static int tevent_common_timed_deny_destructor(struct tevent_timer *te)
   add a timed event
   return NULL on failure (memory allocation error)
 */
-struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
-                                            struct timeval next_event,
-                                            tevent_timer_handler_t handler,
-                                            void *private_data,
-                                            const char *handler_name,
-                                            const char *location)
+static struct tevent_timer *tevent_common_add_timer_internal(
+                                       struct tevent_context *ev,
+                                       TALLOC_CTX *mem_ctx,
+                                       struct timeval next_event,
+                                       tevent_timer_handler_t handler,
+                                       void *private_data,
+                                       const char *handler_name,
+                                       const char *location,
+                                       bool optimize_zero)
 {
        struct tevent_timer *te, *prev_te, *cur_te;
 
@@ -173,32 +181,50 @@ struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, TALLOC_C
        te->location            = location;
        te->additional_data     = NULL;
 
+       if (ev->timer_events == NULL) {
+               ev->last_zero_timer = NULL;
+       }
+
        /* keep the list ordered */
        prev_te = NULL;
-       /*
-        * we traverse the list from the tail
-        * because it's much more likely that
-        * timers are added at the end of the list
-        */
-       for (cur_te = DLIST_TAIL(ev->timer_events);
-            cur_te != NULL;
-            cur_te = DLIST_PREV(cur_te))
-       {
-               int ret;
-
+       if (optimize_zero && tevent_timeval_is_zero(&te->next_event)) {
                /*
-                * if the new event comes before the current
-                * we continue searching
+                * Some callers use zero tevent_timer
+                * instead of tevent_immediate events.
+                *
+                * As these can happen very often,
+                * we remember the last zero timer
+                * in the list.
                 */
-               ret = tevent_timeval_compare(&te->next_event,
-                                            &cur_te->next_event);
-               if (ret < 0) {
-                       continue;
+               prev_te = ev->last_zero_timer;
+               ev->last_zero_timer = te;
+       } else {
+               /*
+                * we traverse the list from the tail
+                * because it's much more likely that
+                * timers are added at the end of the list
+                */
+               for (cur_te = DLIST_TAIL(ev->timer_events);
+                    cur_te != NULL;
+                    cur_te = DLIST_PREV(cur_te))
+               {
+                       int ret;
+
+                       /*
+                        * if the new event comes before the current
+                        * we continue searching
+                        */
+                       ret = tevent_timeval_compare(&te->next_event,
+                                                    &cur_te->next_event);
+                       if (ret < 0) {
+                               continue;
+                       }
+
+                       break;
                }
 
-               break;
+               prev_te = cur_te;
        }
-       prev_te = cur_te;
 
        DLIST_ADD_AFTER(ev->timer_events, te, prev_te);
 
@@ -210,6 +236,44 @@ struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, TALLOC_C
        return te;
 }
 
+struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev,
+                                            TALLOC_CTX *mem_ctx,
+                                            struct timeval next_event,
+                                            tevent_timer_handler_t handler,
+                                            void *private_data,
+                                            const char *handler_name,
+                                            const char *location)
+{
+       /*
+        * do not use optimization, there are broken Samba
+        * versions which use tevent_common_add_timer()
+        * without using tevent_common_loop_timer_delay(),
+        * it just uses DLIST_REMOVE(ev->timer_events, te)
+        * and would leave ev->last_zero_timer behind.
+        */
+       return tevent_common_add_timer_internal(ev, mem_ctx, next_event,
+                                               handler, private_data,
+                                               handler_name, location,
+                                               false);
+}
+
+struct tevent_timer *tevent_common_add_timer_v2(struct tevent_context *ev,
+                                               TALLOC_CTX *mem_ctx,
+                                               struct timeval next_event,
+                                               tevent_timer_handler_t handler,
+                                               void *private_data,
+                                               const char *handler_name,
+                                               const char *location)
+{
+       /*
+        * Here we turn on last_zero_timer optimization
+        */
+       return tevent_common_add_timer_internal(ev, mem_ctx, next_event,
+                                               handler, private_data,
+                                               handler_name, location,
+                                               true);
+}
+
 /*
   do a single event loop using the events defined in ev
 
@@ -258,6 +322,9 @@ struct timeval tevent_common_loop_timer_delay(struct tevent_context *ev)
        /* We need to remove the timer from the list before calling the
         * handler because in a semi-async inner event loop called from the
         * handler we don't want to come across this event again -- vl */
+       if (ev->last_zero_timer == te) {
+               ev->last_zero_timer = DLIST_PREV(te);
+       }
        DLIST_REMOVE(ev->timer_events, te);
 
        tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,