sched: Use a sentinel timer at the end of the timer_list

Introduce a dummy sentinel timer object that is always the last item
on timer_list.  This optimizes the timer_list walking code as it no
longer needs to check for NULL when traversing the list.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2017-03-10 15:11:59 -05:00
parent 16e3dbb18c
commit cb286ede9d
1 changed files with 29 additions and 9 deletions

View File

@ -19,21 +19,41 @@
static uint16_t millis; static uint16_t millis;
static struct timer ms_timer, sentinel_timer;
// Default millisecond timer. This timer counts milliseconds. It // Default millisecond timer. This timer counts milliseconds. It
// also simplifies the timer code by ensuring there is always at least // also simplifies the timer code by ensuring there is always a timer
// one timer on the timer list and that there is always a timer not // on the timer list and that there is always a timer not more than
// more than 1 ms in the future. // 1ms in the future.
static uint_fast8_t static uint_fast8_t
ms_event(struct timer *t) ms_event(struct timer *t)
{ {
millis++; millis++;
timer_periodic(); timer_periodic();
t->waketime += sched_from_us(1000); ms_timer.waketime += sched_from_us(1000);
sentinel_timer.waketime = ms_timer.waketime + 0x80000000;
return SF_RESCHEDULE; return SF_RESCHEDULE;
} }
static struct timer ms_timer = { static struct timer ms_timer = {
.func = ms_event .func = ms_event,
.next = &sentinel_timer,
};
// The sentinel timer is always the last timer on timer_list - its
// presence allows the code to avoid checking for NULL while
// traversing timer_list. Since sentinel_timer.waketime is always
// equal to (ms_timer.waketime + 0x80000000) any added timer must
// always have a waketime less than one of these two timers.
static uint_fast8_t
sentinel_event(struct timer *t)
{
shutdown("sentinel timer called");
}
static struct timer sentinel_timer = {
.func = sentinel_event,
.waketime = 0x80000000,
}; };
// Check if ready for a recurring periodic event // Check if ready for a recurring periodic event
@ -91,7 +111,7 @@ sched_timer(struct timer *add)
} else { } else {
// Find position in list and insert // Find position in list and insert
struct timer *pos = timer_list; struct timer *pos = timer_list;
while (pos->next && !sched_is_before(waketime, pos->next->waketime)) while (!sched_is_before(waketime, pos->next->waketime))
pos = pos->next; pos = pos->next;
add->next = pos->next; add->next = pos->next;
pos->next = add; pos->next = add;
@ -128,7 +148,7 @@ reschedule_timer(struct timer *t)
{ {
struct timer *pos = t->next; struct timer *pos = t->next;
uint32_t minwaketime = t->waketime + 1; uint32_t minwaketime = t->waketime + 1;
if (!pos || !sched_is_before(pos->waketime, minwaketime)) if (!sched_is_before(pos->waketime, minwaketime))
// Timer is still the first - no insertion needed // Timer is still the first - no insertion needed
return t; return t;
@ -141,7 +161,7 @@ reschedule_timer(struct timer *t)
// micro optimization for AVR - reduces register pressure // micro optimization for AVR - reduces register pressure
asm("" : "+r"(prev) : : "memory"); asm("" : "+r"(prev) : : "memory");
pos = pos->next; pos = pos->next;
if (!pos || !sched_is_before(pos->waketime, minwaketime)) if (!sched_is_before(pos->waketime, minwaketime))
break; break;
} }
t->next = pos; t->next = pos;
@ -180,7 +200,7 @@ static void
timer_shutdown(void) timer_shutdown(void)
{ {
timer_list = &ms_timer; timer_list = &ms_timer;
ms_timer.next = NULL; ms_timer.next = &sentinel_timer;
timer_set_next(timer_list->waketime); timer_set_next(timer_list->waketime);
} }
DECL_SHUTDOWN(timer_shutdown); DECL_SHUTDOWN(timer_shutdown);