#include #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; }