1.1 --- a/Coroutines/CoroX.c Tue Apr 29 17:05:32 2008 -0700
1.2 +++ b/Coroutines/CoroX.c Wed Apr 30 14:18:49 2008 -0700
1.3 @@ -5,9 +5,6 @@
1.4 * Created by Jens Alfke on 4/29/08.
1.5 * Adapted from Steve Dekorte's libCoroutine:
1.6 * <http://www.dekorte.com/projects/opensource/libCoroutine/>
1.7 - * by putting it on a piece of wood and banging a few nails through it.
1.8 - * No, actually I removed all the stuff for cross-platform support, leaving only the simple
1.9 - * code that works on Mac OS X 10.5, and then cleaned things up a bit.
1.10 *
1.11 * Copyright 2008 Jens Alfke. All rights reserved.
1.12 * Copyright (c) 2002, 2003 Steve Dekorte. All rights reserved.
1.13 @@ -15,142 +12,142 @@
1.14 *
1.15 */
1.16
1.17 +#define _XOPEN_SOURCE /* decl of ucontext_t in <sys/_structs.h> is wrong unless this is defined */
1.18 +
1.19 #include "CoroX.h"
1.20 +#include <stddef.h>
1.21 +#include <assert.h>
1.22 +#include <pthread.h>
1.23 #include <ucontext.h>
1.24 -#include <stddef.h>
1.25 -#include <stdio.h>
1.26 -#include <assert.h>
1.27
1.28 +// Experimentally, the coroutine bus-errors when its stack space drops below about 10k,
1.29 +// implying that the kernel unmaps the very end of it. So allow extra room:
1.30 +#define STACK_OVERHEAD (12*1024)
1.31
1.32 -#define CORO_DEFAULT_STACK_SIZE (65536*4)
1.33 -#define CORO_STACK_SIZE_MIN 8192
1.34 +// Even with that overhead, stack sizes less than this cause an immediate crash:
1.35 +const size_t kCoroX_minStackSize = 20*1024;
1.36
1.37
1.38 -struct Coro
1.39 +struct CoroX
1.40 {
1.41 size_t stackSize;
1.42 void *stack;
1.43 + CoroEntryPoint entryPoint;
1.44 + void *userData;
1.45 ucontext_t env;
1.46 - // The following field works around bug(?) in sys/_structs.h. This field should be part of ucontext_t:
1.47 - _STRUCT_MCONTEXT env_registers;
1.48 - unsigned char isMain;
1.49 };
1.50
1.51
1.52 -typedef struct CallbackBlock
1.53 +CoroX *CoroX_new(CoroEntryPoint entryPoint, void *userData)
1.54 {
1.55 - void *context;
1.56 - CoroStartCallback *func;
1.57 -} CallbackBlock;
1.58 -
1.59 -
1.60 -Coro *Coro_new(void)
1.61 -{
1.62 - Coro *self = (Coro *)calloc(1, sizeof(Coro));
1.63 - self->stackSize = CORO_DEFAULT_STACK_SIZE;
1.64 - self->stack = NULL;
1.65 + CoroX *self = (CoroX *)calloc(1, sizeof(CoroX));
1.66 + self->stackSize = CoroX_getDefaultStackSize();
1.67 + self->entryPoint = entryPoint;
1.68 + self->userData = userData;
1.69 return self;
1.70 }
1.71
1.72 -void Coro_free(Coro *self)
1.73 +void CoroX_free(CoroX *self)
1.74 {
1.75 - if (self->stack)
1.76 - free(self->stack);
1.77 - free(self);
1.78 + if( self ) {
1.79 + if (self->stack)
1.80 + free(self->stack);
1.81 + free(self);
1.82 + }
1.83 }
1.84
1.85 +void* CoroX_userData(CoroX *self) {return self->userData;}
1.86 +bool CoroX_isMain(CoroX *self) {return self->entryPoint==NULL;}
1.87
1.88 -void *Coro_stack(Coro *self)
1.89 +
1.90 +#pragma mark STACK:
1.91 +
1.92 +
1.93 +void* CoroX_stack(CoroX *self) {return self->stack;}
1.94 +size_t CoroX_stackSize(CoroX *self) {return self->stackSize;}
1.95 +
1.96 +void CoroX_setStackSize_(CoroX *self, size_t sizeInBytes)
1.97 {
1.98 - return self->stack;
1.99 -}
1.100 -
1.101 -size_t Coro_stackSize(Coro *self)
1.102 -{
1.103 - return self->stackSize;
1.104 -}
1.105 -
1.106 -void Coro_setStackSize_(Coro *self, size_t sizeInBytes)
1.107 -{
1.108 + if( sizeInBytes < kCoroX_minStackSize )
1.109 + sizeInBytes = kCoroX_minStackSize;
1.110 self->stackSize = sizeInBytes;
1.111 }
1.112
1.113 -uint8_t *Coro_CurrentStackPointer(void) __attribute__ ((noinline));
1.114
1.115 -uint8_t *Coro_CurrentStackPointer(void)
1.116 +static size_t sDefaultStackSize;
1.117 +
1.118 +size_t CoroX_getDefaultStackSize(void)
1.119 +{
1.120 + if( sDefaultStackSize == 0 ) {
1.121 + pthread_attr_t pthreadAttrs; // Find out default pthread stack size
1.122 + int err = pthread_attr_init(&pthreadAttrs);
1.123 + if( ! err )
1.124 + err = pthread_attr_getstacksize(&pthreadAttrs, &sDefaultStackSize);
1.125 + if (err)
1.126 + sDefaultStackSize = 512*1024;
1.127 + }
1.128 + return sDefaultStackSize;
1.129 +}
1.130 +
1.131 +void CoroX_setDefaultStackSize( size_t size )
1.132 +{
1.133 + sDefaultStackSize = size;
1.134 +}
1.135 +
1.136 +
1.137 + __attribute__ ((noinline))
1.138 +static uint8_t *CoroX_CurrentStackPointer(void)
1.139 {
1.140 uint8_t a;
1.141 uint8_t *b = &a;
1.142 return b;
1.143 }
1.144
1.145 -size_t Coro_bytesLeftOnStack(Coro *self)
1.146 +size_t CoroX_bytesLeftOnStack(CoroX *self)
1.147 {
1.148 uint8_t dummy;
1.149 ptrdiff_t p1 = (ptrdiff_t)(&dummy);
1.150 - ptrdiff_t p2 = (ptrdiff_t)Coro_CurrentStackPointer();
1.151 + ptrdiff_t p2 = (ptrdiff_t)CoroX_CurrentStackPointer();
1.152 int stackMovesUp = p2 > p1;
1.153 - ptrdiff_t start = ((ptrdiff_t)self->stack);
1.154 + ptrdiff_t start = ((ptrdiff_t)self->stack) ;
1.155 ptrdiff_t end = start + self->stackSize;
1.156
1.157 if (stackMovesUp)
1.158 - {
1.159 - return end - p1;
1.160 - }
1.161 + return end - p1 - STACK_OVERHEAD;
1.162 else
1.163 - {
1.164 - return p1 - start;
1.165 - }
1.166 + return p1 - start - STACK_OVERHEAD;
1.167 }
1.168
1.169 -int Coro_stackSpaceAlmostGone(Coro *self)
1.170 +int CoroX_stackSpaceAlmostGone(CoroX *self)
1.171 {
1.172 - return Coro_bytesLeftOnStack(self) < CORO_STACK_SIZE_MIN;
1.173 + return CoroX_bytesLeftOnStack(self) < 2048;
1.174 }
1.175
1.176
1.177 -void Coro_initializeMainCoro(Coro *self)
1.178 -{
1.179 - self->isMain = 1;
1.180 -}
1.181 +#pragma mark SETUP & SWITCH:
1.182
1.183
1.184 -static void Coro_StartWithArg(CallbackBlock *block)
1.185 -{
1.186 - (block->func)(block->context);
1.187 - fprintf(stderr,"CoroX error: returned from coro start function\n");
1.188 - abort();
1.189 -}
1.190 -
1.191 typedef void (*makecontext_func)(void);
1.192
1.193 -static void Coro_setup(Coro *self, void *arg)
1.194 +static void CoroX_setup_(CoroX *self)
1.195 {
1.196 - ucontext_t *ucp = (ucontext_t *) &self->env;
1.197 - getcontext(ucp);
1.198 + size_t stackSize = self->stackSize + STACK_OVERHEAD;
1.199 + self->stack = valloc(stackSize);
1.200 + assert(self->stack);
1.201
1.202 - ucp->uc_stack.ss_sp = Coro_stack(self);
1.203 - ucp->uc_stack.ss_size = Coro_stackSize(self);
1.204 - ucp->uc_stack.ss_flags = 0;
1.205 - ucp->uc_link = NULL;
1.206 -
1.207 - makecontext(ucp, (makecontext_func)Coro_StartWithArg, 1, arg);
1.208 + getcontext(&self->env);
1.209 + self->env.uc_stack.ss_sp = self->stack;
1.210 + self->env.uc_stack.ss_size = stackSize;
1.211 + self->env.uc_stack.ss_flags = 0;
1.212 + self->env.uc_link = NULL;
1.213 + makecontext(&self->env, (makecontext_func)self->entryPoint, 1, self);
1.214 }
1.215
1.216
1.217 -void Coro_startCoro_(Coro *self, Coro *other, void *context, CoroStartCallback *callback)
1.218 +void CoroX_switchTo_(CoroX *self, CoroX *next)
1.219 {
1.220 - assert(other->stack==NULL);
1.221 - other->stack = calloc(1, other->stackSize + 16);
1.222 -
1.223 - CallbackBlock callbackBlock = {context,callback};
1.224 - Coro_setup(other, &callbackBlock);
1.225 -
1.226 - Coro_switchTo_(self, other);
1.227 -}
1.228 -
1.229 -void Coro_switchTo_(Coro *self, Coro *next)
1.230 -{
1.231 + if( ! next->stack && next->entryPoint )
1.232 + CoroX_setup_(next);
1.233 swapcontext(&self->env, &next->env);
1.234 }
1.235