171 lines
4.9 KiB
C
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;
|
|
}
|
|
|