diff --git a/daemon/timerthread.c b/daemon/timerthread.c index 609a8b5b0..312f47c60 100644 --- a/daemon/timerthread.c +++ b/daemon/timerthread.c @@ -15,6 +15,7 @@ static void timerthread_thread_init(struct timerthread_thread *tt, struct timert cond_init(&tt->cond); tt->parent = parent; ZERO(tt->next_wake); + tt->obj = NULL; } void timerthread_init(struct timerthread *tt, unsigned int num, void (*func)(void *)) { @@ -35,6 +36,8 @@ static int __tt_put_all(void *k, void *d, void *p) { static void timerthread_thread_destroy(struct timerthread_thread *tt) { g_tree_foreach(tt->tree, __tt_put_all, tt); g_tree_destroy(tt->tree); + if (tt->obj) + obj_put(tt->obj); mutex_destroy(&tt->lock); } @@ -56,24 +59,33 @@ static void timerthread_run(void *p) { while (!rtpe_shutdown) { gettimeofday(&rtpe_now, NULL); - /* lock our list and get the first element */ - struct timerthread_obj *tt_obj = g_tree_find_first(tt->tree, NULL, NULL); - /* scheduled to run? if not, we just go to sleep, otherwise we remove it from the tree, - * steal the reference and run it */ long long sleeptime = 10000000; - if (!tt_obj) - goto sleep; + // find the first element if we haven't determined it yet + struct timerthread_obj *tt_obj = tt->obj; + if (!tt_obj) { + tt_obj = g_tree_find_first(tt->tree, NULL, NULL); + if (!tt_obj) + goto sleep_now; + + // immediately steal reference + // XXX ideally we would have a tree_steal_first() function + g_tree_remove(tt->tree, tt_obj); + } + + // scheduled to run? if not, then we remember this object/reference and go to sleep sleeptime = timeval_diff(&tt_obj->next_check, &rtpe_now); - if (sleeptime > 0) + + if (sleeptime > 0) { + tt->obj = tt_obj; goto sleep; + } - // steal reference - g_tree_remove(tt->tree, tt_obj); // pretend we're running exactly at the scheduled time rtpe_now = tt_obj->next_check; ZERO(tt_obj->next_check); tt_obj->last_run = rtpe_now; ZERO(tt->next_wake); + tt->obj = NULL; mutex_unlock(&tt->lock); // run and release @@ -85,9 +97,10 @@ static void timerthread_run(void *p) { mutex_lock(&tt->lock); continue; -sleep:; +sleep: /* figure out how long we should sleep */ - sleeptime = MIN(10000000, sleeptime); /* 100 ms at the most */ + sleeptime = MIN(10000000, sleeptime); +sleep_now:; struct timeval tv = rtpe_now; timeval_add_usec(&tv, sleeptime); tt->next_wake = tv; @@ -113,13 +126,23 @@ void timerthread_obj_schedule_abs_nl(struct timerthread_obj *tt_obj, const struc if (tt_obj->next_check.tv_sec && timeval_cmp(&tt_obj->next_check, tv) <= 0) return; /* already scheduled sooner */ - if (!g_tree_remove(tt->tree, tt_obj)) - obj_hold(tt_obj); /* if it wasn't removed, we make a new reference */ + if (!g_tree_remove(tt->tree, tt_obj)) { + if (tt->obj == tt_obj) + tt->obj = NULL; + else + obj_hold(tt_obj); /* if it wasn't removed, we make a new reference */ + } tt_obj->next_check = *tv; g_tree_insert(tt->tree, tt_obj, tt_obj); // need to wake the thread? - if (tt->next_wake.tv_sec && timeval_cmp(tv, &tt->next_wake) < 0) + if (tt->next_wake.tv_sec && timeval_cmp(tv, &tt->next_wake) < 0) { + // make sure we can get picked first: move pre-picked object back into tree + if (tt->obj && tt->obj != tt_obj) { + g_tree_insert(tt->tree, tt->obj, tt->obj); + tt->obj = NULL; + } cond_signal(&tt->cond); + } } void timerthread_obj_deschedule(struct timerthread_obj *tt_obj) { @@ -134,6 +157,12 @@ void timerthread_obj_deschedule(struct timerthread_obj *tt_obj) { if (!tt_obj->next_check.tv_sec) goto nope; /* already descheduled */ gboolean ret = g_tree_remove(tt->tree, tt_obj); + if (!ret) { + if (tt->obj == tt_obj) { + tt->obj = NULL; + ret = TRUE; + } + } ZERO(tt_obj->next_check); if (ret) obj_put(tt_obj); diff --git a/include/timerthread.h b/include/timerthread.h index 58b65a34b..7c0e3de3d 100644 --- a/include/timerthread.h +++ b/include/timerthread.h @@ -15,6 +15,7 @@ struct timerthread_thread { mutex_t lock; cond_t cond; struct timeval next_wake; + struct timerthread_obj *obj; }; struct timerthread {