sched: Optimize timer list handling

Rework the timer list rescheduling to be more optimized.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2016-06-07 15:30:24 -04:00
parent a6de2184ba
commit bd07cd1193
1 changed files with 50 additions and 39 deletions

View File

@ -77,33 +77,27 @@ sched_is_before(uint32_t time1, uint32_t time2)
static struct timer *timer_list = &ms_timer; static struct timer *timer_list = &ms_timer;
// Add a timer to timer list.
static __always_inline void
add_timer(struct timer *add)
{
struct timer **timep = &timer_list, *t = timer_list;
while (t && !sched_is_before(add->waketime, t->waketime)) {
timep = &t->next;
t = t->next;
}
add->next = t;
*timep = add;
}
// Schedule a function call at a supplied time. // Schedule a function call at a supplied time.
void void
sched_timer(struct timer *add) sched_timer(struct timer *add)
{ {
uint32_t waketime = add->waketime;
uint8_t flag = irq_save(); uint8_t flag = irq_save();
add_timer(add); if (sched_is_before(waketime, timer_list->waketime)) {
// This timer is the next - insert at front of list and reschedule
// Reschedule timer if necessary. add->next = timer_list;
if (timer_list == add) { timer_list = add;
uint8_t ret = timer_set_next(add->waketime); uint8_t ret = timer_set_next(waketime);
if (ret) if (ret)
shutdown("Timer too close"); shutdown("Timer too close");
} else {
// Find position in list and insert
struct timer *pos = timer_list;
while (pos->next && !sched_is_before(waketime, pos->next->waketime))
pos = pos->next;
add->next = pos->next;
pos->next = add;
} }
irq_restore(flag); irq_restore(flag);
} }
@ -112,28 +106,45 @@ void
sched_del_timer(struct timer *del) sched_del_timer(struct timer *del)
{ {
uint8_t flag = irq_save(); uint8_t flag = irq_save();
if (timer_list == del) { if (timer_list == del) {
// Deleting the next active timer - delete and reschedule
timer_list = del->next; timer_list = del->next;
timer_set_next(timer_list->waketime); timer_set_next(timer_list->waketime);
irq_restore(flag); } else {
return; // Find and remove from timer list (if present)
} struct timer *pos;
for (pos = timer_list; pos->next; pos = pos->next) {
// Find and remove from timer list. if (pos->next == del) {
struct timer *prev = timer_list; pos->next = del->next;
for (;;) {
struct timer *t = prev->next;
if (!t)
break;
if (t == del) {
prev->next = del->next;
break; break;
} }
prev = t; }
}
irq_restore(flag);
} }
irq_restore(flag); // Move a rescheduled timer to its new location in the list. Returns
// the next timer to run.
static struct timer *
reschedule_timer(struct timer *t)
{
struct timer *pos = t->next;
uint32_t minwaketime = t->waketime + 1;
if (!pos || !sched_is_before(pos->waketime, minwaketime))
// Timer is still the first - no insertion needed
return t;
// Find new timer position and update list
timer_list = pos;
while (pos->next && sched_is_before(pos->next->waketime, minwaketime))
pos = pos->next;
t->next = pos->next;
pos->next = t;
if (CONFIG_MACH_AVR)
// micro optimization for AVR - reduces register pressure
barrier();
return timer_list;
} }
// Invoke timers - called from board timer irq code. // Invoke timers - called from board timer irq code.
@ -150,10 +161,10 @@ sched_timer_kick(void)
res = t->func(t); res = t->func(t);
// Update timer_list (rescheduling current timer if necessary) // Update timer_list (rescheduling current timer if necessary)
timer_list = t->next; if (unlikely(res == SF_DONE))
if (likely(res)) t = timer_list = t->next;
add_timer(t); else
t = timer_list; t = reschedule_timer(t);
// Schedule next timer event (or run next timer if it's ready) // Schedule next timer event (or run next timer if it's ready)
res = timer_try_set_next(t->waketime); res = timer_try_set_next(t->waketime);