timer: Organize timer_try_set_next() with priority for repeat timers

Organize the code flow to optimize for repeat timers.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2017-03-21 17:49:02 -04:00
parent efbfc2b1ab
commit 14340ac4df
1 changed files with 27 additions and 20 deletions

View File

@ -133,32 +133,39 @@ timer_periodic(void)
uint8_t
timer_try_set_next(uint32_t target)
{
uint16_t next = target, now = timer_get();
int16_t diff = next - now;
if (diff > TIMER_MIN_TRY_TICKS)
// Schedule next timer normally.
goto done;
// Next timer is in the past or near future - can't reschedule to it
if (!(TIFR1 & (1<<OCF1B))) {
// Can run more timers from this irq; briefly allow irqs
uint16_t next = target;
int16_t diff = next - timer_get();
if (likely(diff < 0)) {
// Another timer is pending - briefly allow irqs to fire
irq_enable();
asm("nop");
if (unlikely(TIFR1 & (1<<OCF1B)))
// Too many repeat timers - must exit irq handler
goto force_pause;
irq_disable();
while (diff >= 0) {
// Next timer is in the near future - wait for time to occur
now = timer_get();
irq_enable();
diff = next - now;
irq_disable();
}
return 0;
}
if (diff < (int16_t)(-timer_from_us(1000)))
goto fail;
if (likely(diff > TIMER_MIN_TRY_TICKS))
// Schedule next timer normally
goto done;
// Next timer in very near future - wait for it to be ready
for (;;) {
irq_enable();
if (unlikely(TIFR1 & (1<<OCF1B)))
break;
irq_disable();
diff = next - timer_get();
if (diff < 0)
return 0;
}
force_pause:
// Too many repeat timers - force a pause so tasks aren't starved
irq_disable();
uint16_t now = timer_get();
if ((int16_t)(next - now) < (int16_t)(-timer_from_us(1000)))
goto fail;
timer_repeat_set(now + TIMER_REPEAT_TICKS);
next = now + TIMER_DEFER_REPEAT_TICKS;