#include #include static int timers_cmp(const void *a_, const void *b_) { const struct UCTimer *a = a_; const struct UCTimer *b = b_; 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_timer_real_add(struct UCTimers *timers, struct UCTimer *timer) { uc_timer_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->timers, &timer->rb_node); } void uc_timers_init(struct UCTimers *t) { t->timers.node = NULL; t->timers.compare = timers_cmp; t->seq = 0; TAILQ_INIT(&t->ready_list); } void uc_timer_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; gettimeofday(&now, NULL); timeradd(&now, &tmp, &timer->timeout); uc_timer_real_add(timers, timer); } void uc_timer_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; } uc_rb_remove(&timers->timers, &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->timers); if(n) { struct UCTimer *timer = n->data; *first = timer->timeout; return 0; } return 1; } size_t uc_timer_count(const struct UCTimers *timers) { struct RBNode *n; size_t count = 0; for(n = uc_rb_first(&timers->timers); 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, const struct timeval *now) { RBNode *node; int cnt = 0; /* 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->timers); 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_timer_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_timer_remove will unlink the timer from this list. */ while(!TAILQ_EMPTY(&timers->ready_list)) { struct UCTimer *t = TAILQ_FIRST(&timers->ready_list); uc_timer_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; }