diff options
Diffstat (limited to 'kernel/util/timer.c')
-rw-r--r-- | kernel/util/timer.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/kernel/util/timer.c b/kernel/util/timer.c new file mode 100644 index 0000000..f1be4a2 --- /dev/null +++ b/kernel/util/timer.c @@ -0,0 +1,121 @@ +#include "util/timer.h" +#include "proc/spinlock.h" +#include "util/time.h" + +static timer_t *timer_running = NULL; +static uint64_t timer_next_expiry = -1; +static list_t timers_primary = LIST_INITIALIZER(timers_primary); +static list_t timers_secondary = LIST_INITIALIZER(timers_secondary); +static int timers_firing = 0; + +void timer_init(timer_t *timer) +{ + timer->expires = -1; + list_link_init(&timer->link); +} + +void timer_add(timer_t *timer) { timer_mod(timer, timer->expires); } + +int __timer_del(timer_t *timer) +{ + int ret = 0; + if (list_link_is_linked(&timer->link)) + { + list_remove(&timer->link); + ret = 1; + } + return ret; +} + +int timer_del(timer_t *timer) +{ + int ret = __timer_del(timer); + + return ret; +} + +void __timer_add(timer_t *timer) +{ + KASSERT(!list_link_is_linked(&timer->link)); + list_t *list = timers_firing ? &timers_secondary : &timers_primary; + list_insert_head(list, &timer->link); +} + +int timer_mod(timer_t *timer, int expires) +{ + + timer->expires = expires; + int ret = __timer_del(timer); + __timer_add(timer); + timer_next_expiry = MIN(timer_next_expiry, timer->expires); + + return ret; +} + +int timer_pending(timer_t *timer) +{ + int ret = list_link_is_linked(&timer->link); + return ret; +} + +int timer_del_sync(timer_t *timer) +{ + /* Not great performance wise... */ + while (timer_running == timer) + { + sched_yield(); + } + + int ret = __timer_del(timer); + + return ret; +} + +/* Note: using a linked-list rather than some priority is terribly inefficient + * Also this implementation is just bad. Sorry. + */ +int ready = 0; +void __timers_fire() +{ + if (curthr && !preemption_enabled()) + { + return; + } + + timers_firing = 1; + + //dbg(DBG_PRINT, "next expiry: %d\n", timer_next_expiry); + if (jiffies < timer_next_expiry) + { + timers_firing = 0; + return; + } + + uint64_t min_expiry = 0; + + list_iterate(&timers_primary, timer, timer_t, link) + { + if (jiffies >= timer->expires) + { + list_remove(&timer->link); + timer_running = timer; + timer->function(timer->data); + timer_running = NULL; + } + else + { + min_expiry = MIN(min_expiry, timer->expires); + } + } + + /* migrate from the backup list to the primary list */ + list_iterate(&timers_secondary, timer, timer_t, link) + { + min_expiry = MIN(min_expiry, timer->expires); + list_remove(&timer->link); + list_insert_head(&timers_primary, &timer->link); + } + + timer_next_expiry = min_expiry; + timers_firing = 0; +} |