Files
libucore/src/timers.c
T
2013-10-05 00:55:21 +02:00

171 lines
4.9 KiB
C

#include <assert.h>
#include "ucore/timers.h"
#include "ucore/clock.h"
#include "ucore/utils.h"
static int timers_cmp(const RBNode *a_, const RBNode *b_)
{
const struct UCTimer *a = UC_CONST_CONTAINER_OF(a_, struct UCTimer, rb_node);
const struct UCTimer *b = UC_CONST_CONTAINER_OF(b_, struct UCTimer, rb_node);
if (timercmp(&a->timeout, &b->timeout, <)) {
return -1;
} else if (timercmp(&a->timeout, &b->timeout, >)) {
return 1;
} else { //check sequence number
if (a->seq < b->seq) {
return -1;
} else if (a->seq > b->seq) {
return 1;
} else { //should never happen
assert(0);
return 0;
}
}
assert(0);
return 0; //silence compiler
}
static void uc_timers_real_add(struct UCTimers *timers, struct UCTimer *timer)
{
uc_timers_remove(timers, timer); //re schedule it if it's already running
timer->state = UC_TIMER_ACTIVE;
timer->seq = timers->seq++;
timer->rb_node.data = timer; //point the data in a RBNode back to the timer that contains it.
uc_rb_insert(&timers->timer_tree, &timer->rb_node);
}
void uc_timers_init(struct UCTimers *t)
{
uc_timers_init_ex(t, &uc_timeofday_clock);
}
void uc_timers_init_ex(struct UCTimers *t, struct UCoreClock *uclock)
{
t->timer_tree.node = NULL;
t->timer_tree.compare = timers_cmp;
t->uclock = uclock;
t->seq = 0;
TAILQ_INIT(&t->ready_list);
}
void uc_timers_add(struct UCTimers *timers, struct UCTimer *timer, int sec, int usec)
{
struct timeval now;
struct timeval tmp;
assert(timer->callback != NULL);
if (timer->callback == NULL) { //must be set.
return;
}
tmp.tv_sec = sec;
tmp.tv_usec = usec;
timers->uclock->now(timers->uclock, &now);
timeradd(&now, &tmp, &timer->timeout);
uc_timers_real_add(timers, timer);
}
void uc_timers_remove(struct UCTimers *timers, struct UCTimer *timer)
{
if (timer->state == UC_TIMER_INACTIVE) { //already removed , or never added.
//debugging bandaid
timer->ready_entry.tqe_next = (struct UCTimer *)104;
timer->ready_entry.tqe_prev = (struct UCTimer **)105;
return;
}
assert(timer->state == UC_TIMER_ACTIVE ||
timer->state == UC_TIMER_PENDING);
uc_rb_remove(&timers->timer_tree, &timer->rb_node); //remove it, safe for the
//callback to re_add it
if (timer->state == UC_TIMER_PENDING) {
//timer is pending for callback, remove it from the list
//so code in uc_timers_run does not touch it.
TAILQ_REMOVE(&timers->ready_list, timer, ready_entry);
}
timer->state = UC_TIMER_INACTIVE;
}
int uc_timers_first(const struct UCTimers *timers, struct timeval *first)
{
const RBNode *n = uc_rb_first(&timers->timer_tree);
if (n) {
struct UCTimer *timer = n->data;
*first = timer->timeout;
return 0;
}
return 1;
}
size_t uc_timers_count(const struct UCTimers *timers)
{
struct RBNode *n;
size_t count = 0;
for (n = uc_rb_first(&timers->timer_tree); n ; n = uc_rb_next(n)) {
count++;
}
return count;
}
int uc_timer_running(const struct UCTimer *timer)
{
return timer->state;
}
int uc_timers_run(struct UCTimers *timers)
{
RBNode *node;
int cnt = 0;
struct timeval now;
timers->uclock->now(timers->uclock, &now);
/* First find all the expired nodes, place them in a linked list.
* Callbacks cannot be performed here, as the callback might alter
* the rbtree and timers
*/
assert(TAILQ_EMPTY(&timers->ready_list)); //somethings serious wrong if it isn't already empty
TAILQ_INIT(&timers->ready_list);
for (node = uc_rb_first(&timers->timer_tree); node ; node = uc_rb_next(node)) {
struct UCTimer *t = node->data;
//Note, timercmp does not work for >=
if (!timercmp(&now, &t->timeout, <)) {
TAILQ_INSERT_TAIL(&timers->ready_list, t, ready_entry);
t->state = UC_TIMER_PENDING; //so uc_timers_remove knows
//it have to remove it from the ready_list
} else {
break;
}
}
/*run through the list and fire callbacks.
* It is ok for a callback to remove another
* timer that's either running or pending
* Need to always pick the first item in the tail queue
* since a timer callback could remove/free a timer
* we had in the list.
* uc_timers_remove will unlink the timer from this list.
*/
while (!TAILQ_EMPTY(&timers->ready_list)) {
struct UCTimer *t = TAILQ_FIRST(&timers->ready_list);
uc_timers_remove(timers, t);
assert(t->callback != NULL);
t->callback(timers, t); //fire
// callback might have free'd its timer.
// Do not touch 't' after this.
cnt++;
}
return cnt;
}