From a5bf1ccadcd8d5aaf3ea1bb8697589584eb6f2c5 Mon Sep 17 00:00:00 2001 From: Josarn Date: Wed, 18 Jul 2018 14:00:47 +0200 Subject: [PATCH] xtimer.c: periodic_wakeup skip missed targets 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. --- sys/xtimer/xtimer.c | 54 +++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/sys/xtimer/xtimer.c b/sys/xtimer/xtimer.c index ff6dccf0f4925..61c4eb69bd1ab 100644 --- a/sys/xtimer/xtimer.c +++ b/sys/xtimer/xtimer.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2015 Kaspar Schleiser - * 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 @@ -15,6 +16,7 @@ * @brief xtimer convenience functionality * @author Kaspar Schleiser * @author Joakim NohlgÄrd + * @author Josua Arndt * @} */ @@ -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 @@ -113,9 +131,13 @@ 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 { if (offset < XTIMER_PERIODIC_RELATIVE) { @@ -131,7 +153,7 @@ void _xtimer_periodic_wakeup(uint32_t *last_wakeup, uint32_t period) { _xtimer_set_absolute(&timer, target); mutex_lock(&mutex); } -out: + *last_wakeup = target; }