Skip to content

Commit

Permalink
xtimer: xtimer_periodic_wakeup
Browse files Browse the repository at this point in the history
1. interrupt save.
2. Change targed passed criteria.
3. Recalculate last_wakeup to be a multiple of periods after now.
   This skips missed targets and avoids a race condition.
  • Loading branch information
Josar committed Jul 24, 2018
1 parent 9daf9ee commit dbe13f2
Showing 1 changed file with 52 additions and 11 deletions.
63 changes: 52 additions & 11 deletions sys/xtimer/xtimer.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/*
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
* Copyright (C) 2016 Eistec AB
*
* 2016 Eistec AB
* 2018 Josua Arndt
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
Expand All @@ -15,6 +16,7 @@
* @brief xtimer convenience functionality
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
* @author Josua Arndt <jarndt@ias.rwth-aachen.de>
* @}
*/

Expand Down Expand Up @@ -71,33 +73,53 @@ void _xtimer_tsleep(uint32_t offset, uint32_t long_offset)
}

void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {

xtimer_t timer;
mutex_t mutex = MUTEX_INIT;
uint32_t mult;

timer.callback = _callback_unlock_mutex;
timer.arg = (void*) &mutex;

uint32_t target = (*last_wakeup) + period;


/* wait for hardware timer overflow */
uint32_t max = _xtimer_lltimer_mask(0xFFFFFFFF) - XTIMER_BACKOFF;
/* make sure the timer counter arrives in the next timer period */
while( _xtimer_lltimer_now() >= max ){}

unsigned state = irq_disable();
uint32_t now = _xtimer_now();

/* make sure we're not setting a value in the past */
if (now < (*last_wakeup)) {
/* base timer overflowed between last_wakeup and now */
if (!((now < target) && (target < (*last_wakeup)))) {
/* last_wakeup < target, base overflowed but target not, target passed. */
/* target <= now , both overflowed, target passed. */
if ( (*last_wakeup < target) || (target <= now) ) {
/* target time has already passed */
*last_wakeup = now;
now = _xtimer_now();
/* now - target, will always be the difference. (modulo power of two) */
mult = (now - target)/ period;
*last_wakeup = (mult * period) + target;
irq_restore(state);
return;
}
}
else {
/* base timer did not overflow */
if ((((*last_wakeup) <= target) && (target <= now))) {
/* last_wakeup < now, base timer did not overflow */
/* target <= now AND target did not overflow, target passed */
if ( (target <= now) && ((*last_wakeup) <= target) ) {
/* target time has already passed */
*last_wakeup = now;
now = _xtimer_now();
mult = (now - target)/ period;
*last_wakeup = (mult * period) + target;
irq_restore(state);
return;
}
}

/*
/* For very small offsets, spin.
* For large offsets, set an absolute target time.
* As that might cause an underflow, for small offsets, set a relative
* target time.
Expand All @@ -115,11 +137,15 @@ void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {
* tl;dr Don't return too early!
*/
uint32_t offset = target - now;
DEBUG("xps, now: %9" PRIu32 ", tgt: %9" PRIu32 ", off: %9" PRIu32 "\n", now, target, offset);
DEBUG("xps, now: %9" PRIu32 ", tgt: %9" PRIu32 ", off: %9" PRIu32 ", backoff: %9" PRIu32 "\n",
now, target, offset, (uint32_t)XTIMER_PERIODIC_SPIN);

if (offset < XTIMER_PERIODIC_SPIN) {
irq_restore(state);
_xtimer_spin(offset);
}
else {
else
{
if (offset < XTIMER_PERIODIC_RELATIVE) {
/* NB: This will overshoot the target by the amount of time it took
* to get here from the beginning of xtimer_periodic_wakeup()
Expand All @@ -130,9 +156,24 @@ void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {
}
mutex_lock(&mutex);
DEBUG("xps, abs: %" PRIu32 "\n", target);
irq_restore(state);
_xtimer_set_absolute(&timer, target);
mutex_lock(&mutex);
}
/*
* Note: last_wakeup _must never_ specify a time in the future after
* _xtimer_periodic_sleep returns.
* If this happens, last_wakeup may specify a time in the future when the
* next call to _xtimer_periodic_sleep is made, which in turn will trigger
* the overflow logic above and make the next timer fire too early, causing
* last_wakeup to point even further into the future, leading to a chain
* reaction.
*
* tl;dr Don't return too early!
*
* This is ensured in xtimer_core _timer_callback with setting
* XTIMER_SHOOT_OVERHEAD
*/

*last_wakeup = target;
}
Expand Down

0 comments on commit dbe13f2

Please sign in to comment.