diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/TFbuild | 5 | ||||
-rw-r--r-- | src/fiber.c | 131 | ||||
-rw-r--r-- | src/uctx-setjmp.h | 109 |
3 files changed, 245 insertions, 0 deletions
diff --git a/src/TFbuild b/src/TFbuild new file mode 100644 index 0000000..211b734 --- /dev/null +++ b/src/TFbuild @@ -0,0 +1,5 @@ +libs-y += libtf + +libtf-objs-y += fiber.o dheap.o + +CFLAGS_dheap.c += -funroll-all-loops diff --git a/src/fiber.c b/src/fiber.c new file mode 100644 index 0000000..0db2984 --- /dev/null +++ b/src/fiber.c @@ -0,0 +1,131 @@ +/* fiber.c - fiber management and scheduling + * + * Copyright (C) 2009 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 <errno.h> +#include <libtf/tf.h> +#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) +{ +} diff --git a/src/uctx-setjmp.h b/src/uctx-setjmp.h new file mode 100644 index 0000000..33e187e --- /dev/null +++ b/src/uctx-setjmp.h @@ -0,0 +1,109 @@ +/* uctx-setjmp.h - setjmp based user-space context switching + * + * Copyright (C) 2009 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 <libtf/fiber.h> + +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> +#ifdef VALGRIND +#include <valgrind/valgrind.h> +#endif + +#define STACK_GUARD 0xbad57ac4 + +struct tf_uctx { + int *stack_guard; + void *alloc; + jmp_buf jmpbuf; +#ifdef VALGRIND + unsigned int stack_id; +#endif + struct tf_fiber fiber; +}; + +#define set_stack(ptr) ({ __asm__("movl %0, %%esp" : : "r"(ptr)); }) +#define get_stack() ({ void *ptr; __asm__("movl %%esp, %0" : "=r"(ptr)); ptr; }) + +attribute_never_inline +static void *tf_uctx_start(tf_fiber_proc fiber_main, size_t private_size) +{ + struct tf_uctx *uctx; + + uctx = alloca(private_size); + if (setjmp(uctx->jmpbuf) != 0) { + fiber_main(uctx->fiber.data); + tf_exit(); + } + return uctx; +} + +static inline +struct tf_fiber *tf_uctx_create(tf_fiber_proc fiber_main, int private_size) +{ + size_t size = TF_STACK_SIZE; + struct tf_uctx *uctx; + void *stack, *old_stack, *stack_head, *stack_tail; +#ifdef VALGRIND + int stack_id; +#endif + + stack = malloc(size); + if (stack == NULL) + return NULL; +#ifdef VALGRIND + stack_id = VALGRIND_STACK_REGISTER(stack, size); +#endif + + /* Assumes stack grows up */ + stack_head = stack + size - 8; + stack_tail = stack; + + old_stack = get_stack(); + set_stack(stack_head); + uctx = tf_uctx_start(fiber_main, private_size + sizeof(struct tf_uctx)); + set_stack(old_stack); + +#ifdef VALGRIND + uctx->stack_id = stack_id; +#endif + uctx->alloc = stack; + uctx->stack_guard = stack_tail; + *uctx->stack_guard = STACK_GUARD; + + return &uctx->fiber; +} + +static inline +void tf_uctx_destroy(struct tf_fiber *fiber) +{ + struct tf_uctx *uctx = container_of(fiber, struct tf_uctx, fiber); +#ifdef VALGRIND + VALGRIND_STACK_DEREGISTER(uctx->stack_id); +#endif + free(uctx->alloc); +} + +static inline +int tf_uctx_transfer(struct tf_fiber *from, struct tf_fiber *to, int value) +{ + struct tf_uctx *ufrom = container_of(from, struct tf_uctx, fiber); + struct tf_uctx *uto = container_of(to, struct tf_uctx, fiber); + int r; + + TF_BUG_ON(unlikely(*ufrom->stack_guard != STACK_GUARD)); + + r = setjmp(ufrom->jmpbuf); + if (r == 0) + longjmp(uto->jmpbuf, value); + return r; +} |