/* fiber.c - fiber management and scheduling * * Copyright (C) 2009 Timo Teräs * 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 #include #include TF_UCTX_H struct tf_scheduler { struct tf_list_head run_q; struct tf_list_head sleep_q; struct tf_fiber * active_fiber; int num_fibers; }; /* FIXME: should be in thread local storage */ static struct tf_scheduler *__scheduler; void *tf_fiber_create(tf_fiber_proc fiber_main, int private_size) { struct tf_scheduler *sched = __scheduler; struct tf_fiber *fiber; fiber = tf_uctx_create(fiber_main, private_size); /* The initial references for caller and scheduler */ *fiber = (struct tf_fiber) { .ref_count = 2, .queue_node = TF_LIST_INITIALIZER(fiber->queue_node), }; tf_list_add_tail(&fiber->queue_node, &sched->run_q); sched->num_fibers++; return fiber->data; } void __tf_fiber_destroy(struct tf_fiber *fiber) { tf_uctx_destroy(fiber); } void *tf_fiber_get(void *data) { struct tf_fiber *fiber = container_of(data, struct tf_fiber, data); tf_atomic_inc(fiber->ref_count); return data; } void tf_fiber_put(void *data) { struct tf_fiber *fiber = container_of(data, struct tf_fiber, data); if (tf_atomic_dec(fiber->ref_count) == 0) __tf_fiber_destroy(fiber); } static void run_fiber(void) { struct tf_scheduler *sched = __scheduler; struct tf_fiber *schedf = container_of((void*) __scheduler, struct tf_fiber, data); struct tf_fiber *f; if (tf_list_empty(&sched->run_q)) return; f = tf_list_first(&sched->run_q, struct tf_fiber, queue_node); tf_list_del(&f->queue_node); sched->active_fiber = f; switch (tf_uctx_transfer(schedf, f, 1)) { case EFAULT: /* Fiber is dead */ tf_fiber_put(f->data); sched->num_fibers--; break; case EAGAIN: /* Yielded, reshedule */ tf_list_add_tail(&f->queue_node, &sched->run_q); break; case EIO: /* Blocked, in sleep */ tf_list_add_tail(&f->queue_node, &sched->sleep_q); break; default: TF_BUG_ON("bad scheduler call from fiber"); } } int tf_main(tf_fiber_proc main_fiber) { struct tf_uctx *ctx = alloca(sizeof(struct tf_uctx) + sizeof(struct tf_scheduler)); struct tf_scheduler *sched = (struct tf_scheduler*) ctx->fiber.data; int stack_guard = STACK_GUARD; ctx->stack_guard = &stack_guard; *sched = (struct tf_scheduler){ .run_q = TF_LIST_HEAD_INITIALIZER(sched->run_q), .sleep_q = TF_LIST_HEAD_INITIALIZER(sched->sleep_q), }; __scheduler = sched; tf_fiber_put(tf_fiber_create(main_fiber, 0)); do { run_fiber(); } while (likely(sched->num_fibers)); __scheduler = NULL; return 0; } int tf_schedule(int err) { struct tf_scheduler *sched = __scheduler; struct tf_fiber *schedf = container_of((void*) __scheduler, struct tf_fiber, data); struct tf_fiber *f = sched->active_fiber; int r; r = tf_uctx_transfer(f, schedf, err); if (r == 1) return 0; return r; } void tf_kill(void *fiber) { }