bugfix timer bucket walk:
authorHannes Gredler <hannes@gredler.at>
Mon, 24 Nov 2008 14:28:55 +0000 (15:28 +0100)
committerHannes Gredler <hannes@gredler.at>
Mon, 24 Nov 2008 14:28:55 +0000 (15:28 +0100)
while walking a timer bucket it may happen that due to a stopped timer
killing other timers we may loose our walking context.
dequeue all timer entries to a temporary queue and mount it back at the end.
Idea inspired on a email exchange with Bernd Petrovitsch <bernd@firmix.at>

src/scheduler.c
src/scheduler.h

index 46ff2e5..3ce735e 100644 (file)
@@ -455,6 +455,7 @@ walk_timers(clock_t * last_run)
 {
   unsigned int total_timers_walked = 0, total_timers_fired = 0;
   unsigned int wheel_slot_walks = 0;
+  struct list_node *timer_node, tmp_head_node;
 
   /*
    * Check the required wheel slots since the last time a timer walk was invoked,
@@ -470,7 +471,19 @@ walk_timers(clock_t * last_run)
     struct timer_entry *timer;
 
     /* Walk all entries hanging off this hash bucket */
-    FOR_ALL_TIMER_ENTRIES(timer_head_node, timer) {
+    list_head_init(&tmp_head_node);
+    for (timer_node = timer_head_node->next;
+         timer_node != timer_head_node; /* circular list */
+         timer_node = timer_head_node->next) {
+
+      /*
+       * Dequeue and insert to a temporary list.
+       * We do this to avoid loosing our walking context when
+       * multiple timers fire.
+       */
+      list_remove(timer_node);
+      list_add_after(&tmp_head_node, timer_node);
+      timer = list2timer(timer_node);
       timers_walked++;
 
       /* Ready to fire ? */
@@ -505,7 +518,13 @@ walk_timers(clock_t * last_run)
 
        timers_fired++;
       }
-    } FOR_ALL_TIMER_ENTRIES_END(timer_head_node, timer);
+    }
+
+    /*
+     * Now mount the temporary list back to the old bucket.
+     */
+    list_add_after(timer_head_node, &tmp_head_node);
+    list_remove(&tmp_head_node);
 
     /* keep some statistics */
     total_timers_walked += timers_walked;
index 9f7c973..d103c08 100644 (file)
@@ -86,17 +86,6 @@ struct timer_entry {
 /* inline to recast from timer_list back to timer_entry */
 LISTNODE2STRUCT(list2timer, struct timer_entry, timer_list);
 
-/* macro to walk all timers hanging off a bucket */
-#define FOR_ALL_TIMER_ENTRIES(head, elem)      \
-{ \
-  struct list_node *elem_node, *next_elem_node; \
-  for (elem_node = (head)->next;                                \
-       elem_node != (head); /* circular list */         \
-       elem_node = next_elem_node) { \
-    next_elem_node = elem_node->next; \
-    elem = list2timer(elem_node);
-#define FOR_ALL_TIMER_ENTRIES_END(head, elem) }}
-
 #define OLSR_TIMER_ONESHOT    0        /* One shot timer */
 #define OLSR_TIMER_PERIODIC   1        /* Periodic timer */