From 751cfe6475d99150e9ca24ccf3f33d9fe482f3aa Mon Sep 17 00:00:00 2001 From: Ludwig Ortmann <ludwig.ortmann@fu-berlin.de> Date: Fri, 20 Dec 2013 16:52:32 +0100 Subject: [PATCH] fix native hwtimer The hardware timer used relative offsets that were never updated before. This leads to two problems: a) later timers will get pushed into the future by the amount of previous timers b) if a short timer is set continuously, a longer timer will never be called Example: a) Timer a with 500 ms is set, timer b with 600 ms is set. timer a expires after 500 ms, timer b will be set to expire in 600 ms which totals to 1100 ms. b) Timer a is set to 500 ms, timer b is set to 600 ms. Timer a expires and is set again. Now timer a will expire in 500 ms and timer b will be pushed further into the future. Repeating this will lead to b never expiring. --- cpu/native/hwtimer_cpu.c | 82 +++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/cpu/native/hwtimer_cpu.c b/cpu/native/hwtimer_cpu.c index 7a1532a2bd..ba23db9344 100644 --- a/cpu/native/hwtimer_cpu.c +++ b/cpu/native/hwtimer_cpu.c @@ -31,6 +31,7 @@ #include <signal.h> #include <stdint.h> #include <stdlib.h> +#include <string.h> #include <err.h> #include "hwtimer.h" @@ -56,6 +57,41 @@ static int native_hwtimer_isset[ARCH_MAXTIMERS]; static int next_timer = -1; static void (*int_handler)(int); + +/** + * Subtract the `struct timeval' values x and y, storing the result in + * result. + * Return 1 if the difference is negative, otherwise 0. + * + * Source: + * http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html + */ +int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) +{ + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_usec < y->tv_usec) { + int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + y->tv_usec -= 1000000 * nsec; + y->tv_sec += nsec; + } + if (x->tv_usec - y->tv_usec > 1000000) { + int nsec = (x->tv_usec - y->tv_usec) / 1000000; + y->tv_usec += 1000000 * nsec; + y->tv_sec -= nsec; + } + + /** + * Compute the time remaining to wait. + * tv_usec is certainly positive. + */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + + /** * sets timeval to given ticks */ @@ -114,8 +150,25 @@ void schedule_timer(void) } /* next pending timer is in slot next_timer */ - if (setitimer(ITIMER_REAL, &native_hwtimer[next_timer], NULL) == -1) { - err(EXIT_FAILURE, "schedule_timer"); + struct timeval now; + ticks2tv(native_hwtimer_now, &now); + + struct itimerval result; + memset(&result, 0, sizeof(result)); + + int retval = timeval_subtract(&result.it_value, &native_hwtimer[next_timer].it_value, &now); + if (retval || (tv2ticks(&result.it_value) < HWTIMERMINOFFSET)) { + /* the timeout has happened already, schedule an interrupt */ + int sig = SIGALRM; + if (real_write(_sig_pipefd[1], &sig, sizeof(int)) == -1) { + err(EXIT_FAILURE, "schedule_timer(): real_write()"); + } + _native_sigpend++; + return; + } + + if (setitimer(ITIMER_REAL, &result, NULL) == -1) { + err(EXIT_FAILURE, "schedule_timer: setitimer"); } else { DEBUG("schedule_timer(): set next timer (%i).\n", next_timer); @@ -184,19 +237,8 @@ void hwtimer_arch_set(unsigned long offset, short timer) { DEBUG("hwtimer_arch_set(%lu, \033[31m%i\033[0m)\n", offset, timer); - if (offset < HWTIMERMINOFFSET) { - offset = HWTIMERMINOFFSET; - DEBUG("hwtimer_arch_set: offset < MIN, set to: %lu\n", offset); - } - - native_hwtimer_isset[timer] = 1; - - ticks2tv(offset, &(native_hwtimer[timer].it_value)); - DEBUG("hwtimer_arch_set(): that is %lu s %lu us from now\n", - (unsigned long)native_hwtimer[timer].it_value.tv_sec, - (unsigned long)native_hwtimer[timer].it_value.tv_usec); - - schedule_timer(); + offset += native_hwtimer_now; + hwtimer_arch_set_absolute(offset, timer); return; } @@ -204,9 +246,15 @@ void hwtimer_arch_set(unsigned long offset, short timer) void hwtimer_arch_set_absolute(unsigned long value, short timer) { DEBUG("hwtimer_arch_set_absolute(%lu, %i)\n", value, timer); - value -= native_hwtimer_now; - hwtimer_arch_set(value, timer); + ticks2tv(value, &(native_hwtimer[timer].it_value)); + + DEBUG("hwtimer_arch_set_absolute(): that is %lu s %lu us from now\n", + (unsigned long)native_hwtimer[timer].it_value.tv_sec, + (unsigned long)native_hwtimer[timer].it_value.tv_usec); + + native_hwtimer_isset[timer] = 1; + schedule_timer(); return; } -- GitLab