diff options
author | Timo Teras <timo.teras@iki.fi> | 2010-03-10 13:58:39 +0200 |
---|---|---|
committer | Timo Teras <timo.teras@iki.fi> | 2010-03-10 13:58:39 +0200 |
commit | 5ef38570315dc68d7ddf8d9475d9a8830528e8a4 (patch) | |
tree | f88fc542b5231614ac6c22a75baea90d82449d6c /src/scheduler.c | |
parent | 43e69b26126b8708b70680c6b4806eb3844386ab (diff) | |
download | libtf-5ef38570315dc68d7ddf8d9475d9a8830528e8a4.tar.bz2 libtf-5ef38570315dc68d7ddf8d9475d9a8830528e8a4.tar.xz |
libtf: separate scheduler fibre, change the core api
Diffstat (limited to 'src/scheduler.c')
-rw-r--r-- | src/scheduler.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/src/scheduler.c b/src/scheduler.c new file mode 100644 index 0000000..d287eca --- /dev/null +++ b/src/scheduler.c @@ -0,0 +1,132 @@ +/* scheduler.c - fiber scheduling + * + * Copyright (C) 2009-2010 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * See http://www.gnu.org/ for details. + */ + +#include <time.h> +#include <libtf/scheduler.h> +#include <libtf/io.h> + +/* FIXME: should be in thread local storage */ +struct tf_scheduler *__tf_scheduler; + +static void update_time(struct tf_scheduler *sched) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + sched->scheduler_time = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +static void process_heap(struct tf_scheduler *sched) +{ + struct tf_heap_node *node; + tf_mtime_t now = sched->scheduler_time; + + while (!tf_heap_empty(&sched->heap) && + tf_mtime_diff(now, tf_heap_get_value(&sched->heap)) >= 0) { + node = tf_heap_get_node(&sched->heap); + tf_heap_delete(node, &sched->heap); + __tf_fiber_wakeup_heapnode(node); + } +} + +void tf_scheduler_fiber(void *data) +{ + struct tf_scheduler *sched = (struct tf_scheduler *) data; + + do { + tf_mtime_diff_t timeout; + + update_time(sched); + if (!tf_list_empty(&sched->scheduled_q) || + !tf_list_empty(&sched->running_q)) { + timeout = 0; + } else if (!tf_heap_empty(&sched->heap)) { + timeout = tf_mtime_diff( + tf_heap_get_value(&sched->heap), + tf_scheduler_get_mtime()); + if (timeout < 0) + timeout = 0; + } else { + timeout = -1; + } + + if (tf_poll(timeout) == TF_WAKEUP_TIMEOUT && + timeout >= 0) { + sched->scheduler_time += timeout; + process_heap(sched); + } + + if (tf_fiber_yield() == TF_WAKEUP_KILL) { + do { + tf_fiber_put(sched->active_fiber); + sched->active_fiber = sched; + } while (__tf_fiber_schedule() == TF_WAKEUP_KILL); + } + } while (1); +} + +struct tf_scheduler *tf_scheduler_create(void) +{ + struct tf_scheduler *sched; + + sched = __tf_fiber_create(tf_scheduler_fiber, + sizeof(struct tf_scheduler)); + + *sched = (struct tf_scheduler) { + .scheduled_q = TF_LIST_HEAD_INITIALIZER(sched->scheduled_q), + .running_q = TF_LIST_HEAD_INITIALIZER(sched->running_q), + }; + + return sched; +} + +int tf_scheduler_enable(struct tf_scheduler *sched) +{ + struct tf_scheduler *s = sched; + + if (s == NULL) { + s = tf_scheduler_create(); + if (s == NULL) + return -ENOMEM; + } + if (s->main_fiber != NULL) + return -EBUSY; + + __tf_fiber_bind_scheduler(s); + __tf_scheduler = s; + tf_poll_init(); + update_time(s); + + if (sched != NULL) + tf_scheduler_get(sched); + + return 0; +} + +void tf_scheduler_disable(void) +{ + struct tf_scheduler *sched = __tf_scheduler; + + if (sched == NULL || + sched->main_fiber != sched->active_fiber) + return; + + /* sleep until no others */ + while (sched->num_fibers > 1) + __tf_fiber_schedule(); + + tf_poll_close(); + __tf_scheduler = NULL; + __tf_fiber_release_scheduler(sched); + tf_heap_destroy(&sched->heap); + tf_fiber_put(sched); +} |