Coroutines/CoroX.c
changeset 1 2475f871c218
parent 0 deb0ee0c5b21
     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