1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
--- a/src/scheduler.c Mon Aug 25 20:23:24 2008 +0200
+++ b/src/scheduler.c Mon Aug 25 23:02:29 2008 +0200
@@ -63,7 +63,6 @@ clock_t now_times; /* current id
/* Hashed root of all timers */
struct list_node timer_wheel[TIMER_WHEEL_SLOTS];
clock_t timer_last_run; /* remember the last timeslot walk */
-struct list_node *timer_walk_list_node = NULL; /* used for timeslot walk */
/* Pool of timers to avoid malloc() churn */
struct list_node free_timer_list;
@@ -317,6 +316,35 @@ olsr_init_timers(void)
timers_running = 0;
}
+/*
+ * olsr_get_next_list_entry
+ *
+ * Get the next list node in a hash bucket.
+ * The listnode of the timer in may be subject to getting removed from
+ * this timer bucket in olsr_change_timer() and olsr_stop_timer(), which
+ * means that we can miss our walking context.
+ * By caching the previous node we can figure out if the current node
+ * has been removed from the hash bucket and compute the next node.
+ */
+static struct list_node *
+olsr_get_next_list_entry (struct list_node **prev_node,
+ struct list_node *current_node)
+{
+ if ((*prev_node)->next == current_node) {
+
+ /*
+ * No change in the list, normal traversal, update the previous node.
+ */
+ *prev_node = current_node;
+ return (current_node->next);
+ } else {
+
+ /*
+ * List change. Recompute the walking context.
+ */
+ return ((*prev_node)->next);
+ }
+}
/**
* Walk through the timer list and check if any timer is ready to fire.
@@ -326,7 +354,7 @@ olsr_walk_timers(clock_t * last_run)
olsr_walk_timers(clock_t * last_run)
{
static struct timer_entry *timer;
- struct list_node *timer_head_node;
+ struct list_node *timer_head_node, *timer_walk_node, *timer_walk_prev_node;
unsigned int timers_walked, timers_fired;
unsigned int total_timers_walked, total_timers_fired;
unsigned int wheel_slot_walks = 0;
@@ -347,12 +375,15 @@ olsr_walk_timers(clock_t * last_run)
/* Get the hash slot for this clocktick */
timer_head_node = &timer_wheel[*last_run & TIMER_WHEEL_MASK];
+ timer_walk_prev_node = timer_head_node;
/* Walk all entries hanging off this hash bucket */
- for (timer_walk_list_node = timer_head_node->next; timer_walk_list_node != timer_head_node; /* circular list */
- timer_walk_list_node = timer_walk_list_node->next) {
-
- timer = list2timer(timer_walk_list_node);
+ for (timer_walk_node = timer_head_node->next;
+ timer_walk_node != timer_head_node; /* circular list */
+ timer_walk_node = olsr_get_next_list_entry(&timer_walk_prev_node,
+ timer_walk_node)) {
+
+ timer = list2timer(timer_walk_node);
timers_walked++;
@@ -403,11 +434,6 @@ olsr_walk_timers(clock_t * last_run)
/* Increment the time slot and wheel slot walk iteration */
(*last_run)++;
wheel_slot_walks++;
-
- /*
- * Mark the timer walk context unused.
- */
- timer_walk_list_node = NULL;
}
#ifdef DEBUG
@@ -581,21 +607,6 @@ olsr_start_timer(unsigned int rel_time,
return timer;
}
-/*
- * Check if there is a timer walk in progress and advance the
- * walking context if so. Keep in mind we are about to delete
- * the timer from a list and this will destroy the walking context.
- */
-
-static inline void
-olsr_update_timer_walk_ctx(struct timer_entry *timer)
-{
- if (timer_walk_list_node == &timer->timer_list) {
- timer_walk_list_node = timer_walk_list_node->next;
- }
-}
-
-
/**
* Delete a timer.
*
@@ -615,8 +626,6 @@ olsr_stop_timer(struct timer_entry *time
olsr_cookie_name(timer->timer_cookie),
timer, timer->timer_cb_context);
#endif
-
- olsr_update_timer_walk_ctx(timer);
/*
* Carve out of the existing wheel_slot and return to the pool
@@ -658,8 +667,6 @@ olsr_change_timer(struct timer_entry *ti
timer->timer_clock = olsr_jitter(rel_time, jitter_pct, timer->timer_random);
timer->timer_jitter_pct = jitter_pct;
- olsr_update_timer_walk_ctx(timer);
-
/*
* Changes are easy: Remove timer from the exisiting timer_wheel slot
* and reinsert into the new slot.
|