Skip to content
Snippets Groups Projects
Commit 751cfe64 authored by Ludwig Knüpfer's avatar Ludwig Knüpfer
Browse files

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.
parent b0d51d32
No related branches found
No related tags found
No related merge requests found
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <signal.h> #include <signal.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <err.h> #include <err.h>
#include "hwtimer.h" #include "hwtimer.h"
...@@ -56,6 +57,41 @@ static int native_hwtimer_isset[ARCH_MAXTIMERS]; ...@@ -56,6 +57,41 @@ static int native_hwtimer_isset[ARCH_MAXTIMERS];
static int next_timer = -1; static int next_timer = -1;
static void (*int_handler)(int); 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 * sets timeval to given ticks
*/ */
...@@ -114,8 +150,25 @@ void schedule_timer(void) ...@@ -114,8 +150,25 @@ void schedule_timer(void)
} }
/* next pending timer is in slot next_timer */ /* next pending timer is in slot next_timer */
if (setitimer(ITIMER_REAL, &native_hwtimer[next_timer], NULL) == -1) { struct timeval now;
err(EXIT_FAILURE, "schedule_timer"); 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 { else {
DEBUG("schedule_timer(): set next timer (%i).\n", next_timer); DEBUG("schedule_timer(): set next timer (%i).\n", next_timer);
...@@ -184,19 +237,8 @@ void hwtimer_arch_set(unsigned long offset, short 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); DEBUG("hwtimer_arch_set(%lu, \033[31m%i\033[0m)\n", offset, timer);
if (offset < HWTIMERMINOFFSET) { offset += native_hwtimer_now;
offset = HWTIMERMINOFFSET; hwtimer_arch_set_absolute(offset, timer);
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();
return; return;
} }
...@@ -204,9 +246,15 @@ void hwtimer_arch_set(unsigned long offset, short timer) ...@@ -204,9 +246,15 @@ void hwtimer_arch_set(unsigned long offset, short timer)
void hwtimer_arch_set_absolute(unsigned long value, short timer) void hwtimer_arch_set_absolute(unsigned long value, short timer)
{ {
DEBUG("hwtimer_arch_set_absolute(%lu, %i)\n", value, 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; return;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment