Skip to content

Commit

Permalink
xtimer.c: periodic_wakeup skip missed targets
Browse files Browse the repository at this point in the history
1. Change targed passed criteria.
2. 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 Nov 15, 2018
1 parent 836fe3d commit 3518b88
Showing 1 changed file with 40 additions and 17 deletions.
57 changes: 40 additions & 17 deletions sys/xtimer/xtimer.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* 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
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,30 +73,46 @@ 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;

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

uint32_t target = (*last_wakeup) + period;
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)))) {
/* target time has already passed */
goto out;
/* last_wakeup < target, now overflowed but target not, target passed.
* now < last_wakeup < target */
/* target <= now , both overflowed, target passed.
* target < now < last_wakeup */
if ((*last_wakeup < target) || (target <= now)) {
/* now - target, will always be the difference. (modulo power of two) */
mult = ((uint32_t)(now - target)) / period;
/* Skip missed targets */
*last_wakeup = target + (mult * period);
return;
}
}
else {
/* base timer did not overflow */
if ((((*last_wakeup) <= target) && (target <= now))) {
/* target time has already passed */
goto out;
/* last_wakeup < now, now did not overflow
* target <= now AND target did not overflow, target passed
* last_wakeup <= target <= now */
/* The special case (*last_wakeup) = target happens when a period smaller
* then the minimum resolution of the xtimer_hz is requested. */
if ((target <= now) && ((*last_wakeup) <= target)) {
/* now - target, will always be the difference. (modulo power of two) */
mult = ((uint32_t)(now - target))/ period;
/* Skip missed targets */
*last_wakeup = target + (mult * period);
return;
}
}

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

/*
* For large offsets, set an absolute target time.
* As that might cause an underflow, for small offsets, set a relative
Expand All @@ -113,11 +131,16 @@ 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);
if (offset < XTIMER_PERIODIC_SPIN) {
_xtimer_spin(offset);
DEBUG("xps, now: %9" PRIu32 ", tgt: %9" PRIu32 ", lst_wkp: %9" PRIu32 ", off: %9" PRIu32 "\n",
now, target, *last_wakeup, offset);
if (offset < XTIMER_PERIODIC_SPIN ) {
/* Skip for zero*/
if(offset != 0) {
_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 @@ -131,7 +154,7 @@ void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) {
_xtimer_set_absolute(&timer, target);
mutex_lock(&mutex);
}
out:

*last_wakeup = target;
}

Expand Down

0 comments on commit 3518b88

Please sign in to comment.