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.
This commit is contained in:
parent
b0d51d326a
commit
751cfe6475
@ -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];
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user