summaryrefslogtreecommitdiffstats
path: root/src/scheduler.c
diff options
context:
space:
mode:
authorTimo Teras <timo.teras@iki.fi>2010-03-10 13:58:39 +0200
committerTimo Teras <timo.teras@iki.fi>2010-03-10 13:58:39 +0200
commit5ef38570315dc68d7ddf8d9475d9a8830528e8a4 (patch)
treef88fc542b5231614ac6c22a75baea90d82449d6c /src/scheduler.c
parent43e69b26126b8708b70680c6b4806eb3844386ab (diff)
downloadlibtf-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.c132
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);
+}