diff options
Diffstat (limited to 'src/uctx-setjmp.h')
-rw-r--r-- | src/uctx-setjmp.h | 109 |
1 files changed, 109 insertions, 0 deletions
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; +} |