diff -r deb0ee0c5b21 -r 2475f871c218 Coroutines/CoroX.c --- a/Coroutines/CoroX.c Tue Apr 29 17:05:32 2008 -0700 +++ b/Coroutines/CoroX.c Wed Apr 30 14:18:49 2008 -0700 @@ -5,9 +5,6 @@ * Created by Jens Alfke on 4/29/08. * Adapted from Steve Dekorte's libCoroutine: * - * by putting it on a piece of wood and banging a few nails through it. - * No, actually I removed all the stuff for cross-platform support, leaving only the simple - * code that works on Mac OS X 10.5, and then cleaned things up a bit. * * Copyright 2008 Jens Alfke. All rights reserved. * Copyright (c) 2002, 2003 Steve Dekorte. All rights reserved. @@ -15,142 +12,142 @@ * */ +#define _XOPEN_SOURCE /* decl of ucontext_t in is wrong unless this is defined */ + #include "CoroX.h" +#include +#include +#include #include -#include -#include -#include +// Experimentally, the coroutine bus-errors when its stack space drops below about 10k, +// implying that the kernel unmaps the very end of it. So allow extra room: +#define STACK_OVERHEAD (12*1024) -#define CORO_DEFAULT_STACK_SIZE (65536*4) -#define CORO_STACK_SIZE_MIN 8192 +// Even with that overhead, stack sizes less than this cause an immediate crash: +const size_t kCoroX_minStackSize = 20*1024; -struct Coro +struct CoroX { size_t stackSize; void *stack; + CoroEntryPoint entryPoint; + void *userData; ucontext_t env; - // The following field works around bug(?) in sys/_structs.h. This field should be part of ucontext_t: - _STRUCT_MCONTEXT env_registers; - unsigned char isMain; }; -typedef struct CallbackBlock +CoroX *CoroX_new(CoroEntryPoint entryPoint, void *userData) { - void *context; - CoroStartCallback *func; -} CallbackBlock; - - -Coro *Coro_new(void) -{ - Coro *self = (Coro *)calloc(1, sizeof(Coro)); - self->stackSize = CORO_DEFAULT_STACK_SIZE; - self->stack = NULL; + CoroX *self = (CoroX *)calloc(1, sizeof(CoroX)); + self->stackSize = CoroX_getDefaultStackSize(); + self->entryPoint = entryPoint; + self->userData = userData; return self; } -void Coro_free(Coro *self) +void CoroX_free(CoroX *self) { - if (self->stack) - free(self->stack); - free(self); + if( self ) { + if (self->stack) + free(self->stack); + free(self); + } } +void* CoroX_userData(CoroX *self) {return self->userData;} +bool CoroX_isMain(CoroX *self) {return self->entryPoint==NULL;} -void *Coro_stack(Coro *self) + +#pragma mark STACK: + + +void* CoroX_stack(CoroX *self) {return self->stack;} +size_t CoroX_stackSize(CoroX *self) {return self->stackSize;} + +void CoroX_setStackSize_(CoroX *self, size_t sizeInBytes) { - return self->stack; -} - -size_t Coro_stackSize(Coro *self) -{ - return self->stackSize; -} - -void Coro_setStackSize_(Coro *self, size_t sizeInBytes) -{ + if( sizeInBytes < kCoroX_minStackSize ) + sizeInBytes = kCoroX_minStackSize; self->stackSize = sizeInBytes; } -uint8_t *Coro_CurrentStackPointer(void) __attribute__ ((noinline)); -uint8_t *Coro_CurrentStackPointer(void) +static size_t sDefaultStackSize; + +size_t CoroX_getDefaultStackSize(void) +{ + if( sDefaultStackSize == 0 ) { + pthread_attr_t pthreadAttrs; // Find out default pthread stack size + int err = pthread_attr_init(&pthreadAttrs); + if( ! err ) + err = pthread_attr_getstacksize(&pthreadAttrs, &sDefaultStackSize); + if (err) + sDefaultStackSize = 512*1024; + } + return sDefaultStackSize; +} + +void CoroX_setDefaultStackSize( size_t size ) +{ + sDefaultStackSize = size; +} + + + __attribute__ ((noinline)) +static uint8_t *CoroX_CurrentStackPointer(void) { uint8_t a; uint8_t *b = &a; return b; } -size_t Coro_bytesLeftOnStack(Coro *self) +size_t CoroX_bytesLeftOnStack(CoroX *self) { uint8_t dummy; ptrdiff_t p1 = (ptrdiff_t)(&dummy); - ptrdiff_t p2 = (ptrdiff_t)Coro_CurrentStackPointer(); + ptrdiff_t p2 = (ptrdiff_t)CoroX_CurrentStackPointer(); int stackMovesUp = p2 > p1; - ptrdiff_t start = ((ptrdiff_t)self->stack); + ptrdiff_t start = ((ptrdiff_t)self->stack) ; ptrdiff_t end = start + self->stackSize; if (stackMovesUp) - { - return end - p1; - } + return end - p1 - STACK_OVERHEAD; else - { - return p1 - start; - } + return p1 - start - STACK_OVERHEAD; } -int Coro_stackSpaceAlmostGone(Coro *self) +int CoroX_stackSpaceAlmostGone(CoroX *self) { - return Coro_bytesLeftOnStack(self) < CORO_STACK_SIZE_MIN; + return CoroX_bytesLeftOnStack(self) < 2048; } -void Coro_initializeMainCoro(Coro *self) -{ - self->isMain = 1; -} +#pragma mark SETUP & SWITCH: -static void Coro_StartWithArg(CallbackBlock *block) -{ - (block->func)(block->context); - fprintf(stderr,"CoroX error: returned from coro start function\n"); - abort(); -} - typedef void (*makecontext_func)(void); -static void Coro_setup(Coro *self, void *arg) +static void CoroX_setup_(CoroX *self) { - ucontext_t *ucp = (ucontext_t *) &self->env; - getcontext(ucp); + size_t stackSize = self->stackSize + STACK_OVERHEAD; + self->stack = valloc(stackSize); + assert(self->stack); - ucp->uc_stack.ss_sp = Coro_stack(self); - ucp->uc_stack.ss_size = Coro_stackSize(self); - ucp->uc_stack.ss_flags = 0; - ucp->uc_link = NULL; - - makecontext(ucp, (makecontext_func)Coro_StartWithArg, 1, arg); + getcontext(&self->env); + self->env.uc_stack.ss_sp = self->stack; + self->env.uc_stack.ss_size = stackSize; + self->env.uc_stack.ss_flags = 0; + self->env.uc_link = NULL; + makecontext(&self->env, (makecontext_func)self->entryPoint, 1, self); } -void Coro_startCoro_(Coro *self, Coro *other, void *context, CoroStartCallback *callback) +void CoroX_switchTo_(CoroX *self, CoroX *next) { - assert(other->stack==NULL); - other->stack = calloc(1, other->stackSize + 16); - - CallbackBlock callbackBlock = {context,callback}; - Coro_setup(other, &callbackBlock); - - Coro_switchTo_(self, other); -} - -void Coro_switchTo_(Coro *self, Coro *next) -{ + if( ! next->stack && next->entryPoint ) + CoroX_setup_(next); swapcontext(&self->env, &next->env); }