tevent: optimize adding new timer events
authorStefan Metzmacher <metze@samba.org>
Fri, 22 Feb 2013 11:45:39 +0000 (12:45 +0100)
committerStefan Metzmacher <metze@samba.org>
Thu, 28 Feb 2013 07:56:00 +0000 (08:56 +0100)
There're two cases:

1. Adding a timer with a zero timestamp.
   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.

2. Adding a timer with a real timestamp.
   As this timestamps typically get higher:-)
   it's better to traverse the existing list from
   the tail.

This is not completely optimal, but it should be better
than before.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
lib/tevent/tevent.c
lib/tevent/tevent_internal.h
lib/tevent/tevent_timed.c

index 3b273d6c531101954bf752f4d408ae7de88f4b5b..96a00a9d153d8f473870528bcc751c775d390345 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 8433333558366af2c754823ce2df9cdf830ce28f..398359b5eea49925381d7bc3a4d4b0d0931a7c56 100644 (file)
@@ -234,6 +234,7 @@ struct tevent_context {
 
        /* list of timed events - used by common code */
        struct tevent_timer *timer_events;
+       struct tevent_timer *last_zero_timer;
 
        /* list of immediate events - used by common code */
        struct tevent_immediate *immediate_events;
index f7c39697d9d11ab25e873fd202cc64d64c97b601..af0efcec33f27bdeae7565d11cc9aed47b77d8f2 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;
 }
@@ -160,7 +165,8 @@ struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, TALLOC_C
                                             const char *handler_name,
                                             const char *location)
 {
-       struct tevent_timer *te, *last_te, *cur_te;
+       struct tevent_timer *te;
+       struct tevent_timer *prev_te = NULL;
 
        te = talloc(mem_ctx?mem_ctx:ev, struct tevent_timer);
        if (te == NULL) return NULL;
@@ -173,18 +179,53 @@ 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 */
-       last_te = NULL;
-       for (cur_te = ev->timer_events; cur_te; cur_te = cur_te->next) {
-               /* if the new event comes before the current one break */
-               if (tevent_timeval_compare(&te->next_event, &cur_te->next_event) < 0) {
+       prev_te = NULL;
+       if (tevent_timeval_is_zero(&te->next_event)) {
+               /*
+                * 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.
+                */
+               prev_te = ev->last_zero_timer;
+               ev->last_zero_timer = te;
+       } else {
+               struct tevent_timer *cur_te = NULL;
+               int ret;
+
+               /*
+                * 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))
+               {
+                       /*
+                        * 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;
                }
 
-               last_te = cur_te;
+               prev_te = cur_te;
        }
 
-       DLIST_ADD_AFTER(ev->timer_events, te, last_te);
+       DLIST_ADD_AFTER(ev->timer_events, te, prev_te);
 
        talloc_set_destructor(te, tevent_common_timed_destructor);
 
@@ -242,6 +283,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);
 
        /*