From 959645020619f02d6ab8bb4026b0e0121b3971da Mon Sep 17 00:00:00 2001 From: Timo Teras Date: Fri, 27 Nov 2009 18:42:11 +0200 Subject: libtf: implement x86 assembly fiber switching it's faster and has smaller context in the beginning of the fiber. it's also required since setjmp() seems to use mangled pointers in glibc so we could not schedule fibers in non-creation threads. additionally the setjmp() setup code has a race condition. --- src/uctx.h | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/uctx.h (limited to 'src/uctx.h') diff --git a/src/uctx.h b/src/uctx.h new file mode 100644 index 0000000..03750e9 --- /dev/null +++ b/src/uctx.h @@ -0,0 +1,134 @@ +/* uctx.h - assembly stack switching + * + * 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 +#ifdef VALGRIND +#include +#endif + +#define STACK_GUARD 0xbad57ac4 + +struct tf_uctx { + int *stack_guard; + void *alloc; + void *current_sp; +#ifdef VALGRIND + unsigned int stack_id; +#endif + struct tf_fiber fiber; +}; + +#if defined(__i386__) + +#define STACK_GROWS_UP + +#else +#error Your architecture is not supported. Send email to timo.teras@iki.fi. +#endif + +#if defined(STACK_GROWS_UP) +static inline void *stack_pointer(void *base, size_t size) +{ + return base + size; +} + +static inline void *stack_guard(void *base, size_t size) +{ + return base; +} + +static inline void *stack_push(void **stackptr, size_t size) +{ + (*stackptr) -= size; + return (*stackptr); +} +#else +#error Stack growing direction undefined. +#endif + +static inline void stack_push_ptr(void **stackptr, void *ptr) +{ + *(void**) stack_push(stackptr, sizeof(ptr)) = ptr; +} + + +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, *stack_base; + + /* Allocate new stack */ + stack_base = malloc(size); + if (stack_base == NULL) + return NULL; + + stack = stack_pointer(stack_base, size); + private_size += sizeof(struct tf_uctx); + + /* Construct inital frame for call the main function and if it + * happens to return, it'll jump back to tf_exit() which kills + * the fiber (cdecl calling convetion assumed) */ + uctx = stack_push(&stack, TF_ALIGN(private_size, 64)); + stack_push_ptr(&stack, uctx->fiber.data); + stack_push_ptr(&stack, &tf_exit); + stack_push_ptr(&stack, fiber_main); + uctx->current_sp = stack; + +#ifdef VALGRIND + uctx->stack_id = VALGRIND_STACK_REGISTER(stack_base, size); +#endif + uctx->alloc = stack_base; + uctx->stack_guard = stack_guard(stack_base, size); + *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); +} + +#define switch_fiber(oldspptr, newsp) \ + __asm__ __volatile__ ( \ + "push %%ebp ;" \ + "call .+5 ;" \ + "add $0x9,(%%esp) ;" \ + "mov %%esp, (%0) ;" \ + "mov %1, %%esp ;" \ + "ret ;" \ + "pop %%ebp" \ + : \ + : "a"(oldspptr), "d"(newsp) \ + : "%ebx", "%ecx", "%esi", "%edi", "memory", "cc"); + +static inline +void tf_uctx_transfer(struct tf_fiber *from, struct tf_fiber *to) +{ + + struct tf_uctx *ufrom = container_of(from, struct tf_uctx, fiber); + struct tf_uctx *uto = container_of(to, struct tf_uctx, fiber); + + /* Switch stack pointers */ + TF_BUG_ON(*ufrom->stack_guard != STACK_GUARD); + switch_fiber(&ufrom->current_sp, uto->current_sp); +} -- cgit v1.2.3