Rewrote CoroX. Simplified APIs. Improved stack size checks.
1.1 --- a/Actors.xcodeproj/project.pbxproj Tue Apr 29 17:05:32 2008 -0700
1.2 +++ b/Actors.xcodeproj/project.pbxproj Wed Apr 30 14:18:49 2008 -0700
1.3 @@ -86,9 +86,9 @@
1.4 children = (
1.5 2703CFE20DC796DC00DD026B /* MYCoroutine.h */,
1.6 2703CFE30DC796DC00DD026B /* MYCoroutine.m */,
1.7 - 2703D28A0DC7EC5A00DD026B /* MYCoroutineTest.m */,
1.8 2703D1BC0DC7DAD100DD026B /* CoroX.h */,
1.9 2703D1BD0DC7DAD100DD026B /* CoroX.c */,
1.10 + 2703D28A0DC7EC5A00DD026B /* MYCoroutineTest.m */,
1.11 );
1.12 path = Coroutines;
1.13 sourceTree = "<group>";
2.1 --- a/Coroutines/CoroX.c Tue Apr 29 17:05:32 2008 -0700
2.2 +++ b/Coroutines/CoroX.c Wed Apr 30 14:18:49 2008 -0700
2.3 @@ -5,9 +5,6 @@
2.4 * Created by Jens Alfke on 4/29/08.
2.5 * Adapted from Steve Dekorte's libCoroutine:
2.6 * <http://www.dekorte.com/projects/opensource/libCoroutine/>
2.7 - * by putting it on a piece of wood and banging a few nails through it.
2.8 - * No, actually I removed all the stuff for cross-platform support, leaving only the simple
2.9 - * code that works on Mac OS X 10.5, and then cleaned things up a bit.
2.10 *
2.11 * Copyright 2008 Jens Alfke. All rights reserved.
2.12 * Copyright (c) 2002, 2003 Steve Dekorte. All rights reserved.
2.13 @@ -15,142 +12,142 @@
2.14 *
2.15 */
2.16
2.17 +#define _XOPEN_SOURCE /* decl of ucontext_t in <sys/_structs.h> is wrong unless this is defined */
2.18 +
2.19 #include "CoroX.h"
2.20 +#include <stddef.h>
2.21 +#include <assert.h>
2.22 +#include <pthread.h>
2.23 #include <ucontext.h>
2.24 -#include <stddef.h>
2.25 -#include <stdio.h>
2.26 -#include <assert.h>
2.27
2.28 +// Experimentally, the coroutine bus-errors when its stack space drops below about 10k,
2.29 +// implying that the kernel unmaps the very end of it. So allow extra room:
2.30 +#define STACK_OVERHEAD (12*1024)
2.31
2.32 -#define CORO_DEFAULT_STACK_SIZE (65536*4)
2.33 -#define CORO_STACK_SIZE_MIN 8192
2.34 +// Even with that overhead, stack sizes less than this cause an immediate crash:
2.35 +const size_t kCoroX_minStackSize = 20*1024;
2.36
2.37
2.38 -struct Coro
2.39 +struct CoroX
2.40 {
2.41 size_t stackSize;
2.42 void *stack;
2.43 + CoroEntryPoint entryPoint;
2.44 + void *userData;
2.45 ucontext_t env;
2.46 - // The following field works around bug(?) in sys/_structs.h. This field should be part of ucontext_t:
2.47 - _STRUCT_MCONTEXT env_registers;
2.48 - unsigned char isMain;
2.49 };
2.50
2.51
2.52 -typedef struct CallbackBlock
2.53 +CoroX *CoroX_new(CoroEntryPoint entryPoint, void *userData)
2.54 {
2.55 - void *context;
2.56 - CoroStartCallback *func;
2.57 -} CallbackBlock;
2.58 -
2.59 -
2.60 -Coro *Coro_new(void)
2.61 -{
2.62 - Coro *self = (Coro *)calloc(1, sizeof(Coro));
2.63 - self->stackSize = CORO_DEFAULT_STACK_SIZE;
2.64 - self->stack = NULL;
2.65 + CoroX *self = (CoroX *)calloc(1, sizeof(CoroX));
2.66 + self->stackSize = CoroX_getDefaultStackSize();
2.67 + self->entryPoint = entryPoint;
2.68 + self->userData = userData;
2.69 return self;
2.70 }
2.71
2.72 -void Coro_free(Coro *self)
2.73 +void CoroX_free(CoroX *self)
2.74 {
2.75 - if (self->stack)
2.76 - free(self->stack);
2.77 - free(self);
2.78 + if( self ) {
2.79 + if (self->stack)
2.80 + free(self->stack);
2.81 + free(self);
2.82 + }
2.83 }
2.84
2.85 +void* CoroX_userData(CoroX *self) {return self->userData;}
2.86 +bool CoroX_isMain(CoroX *self) {return self->entryPoint==NULL;}
2.87
2.88 -void *Coro_stack(Coro *self)
2.89 +
2.90 +#pragma mark STACK:
2.91 +
2.92 +
2.93 +void* CoroX_stack(CoroX *self) {return self->stack;}
2.94 +size_t CoroX_stackSize(CoroX *self) {return self->stackSize;}
2.95 +
2.96 +void CoroX_setStackSize_(CoroX *self, size_t sizeInBytes)
2.97 {
2.98 - return self->stack;
2.99 -}
2.100 -
2.101 -size_t Coro_stackSize(Coro *self)
2.102 -{
2.103 - return self->stackSize;
2.104 -}
2.105 -
2.106 -void Coro_setStackSize_(Coro *self, size_t sizeInBytes)
2.107 -{
2.108 + if( sizeInBytes < kCoroX_minStackSize )
2.109 + sizeInBytes = kCoroX_minStackSize;
2.110 self->stackSize = sizeInBytes;
2.111 }
2.112
2.113 -uint8_t *Coro_CurrentStackPointer(void) __attribute__ ((noinline));
2.114
2.115 -uint8_t *Coro_CurrentStackPointer(void)
2.116 +static size_t sDefaultStackSize;
2.117 +
2.118 +size_t CoroX_getDefaultStackSize(void)
2.119 +{
2.120 + if( sDefaultStackSize == 0 ) {
2.121 + pthread_attr_t pthreadAttrs; // Find out default pthread stack size
2.122 + int err = pthread_attr_init(&pthreadAttrs);
2.123 + if( ! err )
2.124 + err = pthread_attr_getstacksize(&pthreadAttrs, &sDefaultStackSize);
2.125 + if (err)
2.126 + sDefaultStackSize = 512*1024;
2.127 + }
2.128 + return sDefaultStackSize;
2.129 +}
2.130 +
2.131 +void CoroX_setDefaultStackSize( size_t size )
2.132 +{
2.133 + sDefaultStackSize = size;
2.134 +}
2.135 +
2.136 +
2.137 + __attribute__ ((noinline))
2.138 +static uint8_t *CoroX_CurrentStackPointer(void)
2.139 {
2.140 uint8_t a;
2.141 uint8_t *b = &a;
2.142 return b;
2.143 }
2.144
2.145 -size_t Coro_bytesLeftOnStack(Coro *self)
2.146 +size_t CoroX_bytesLeftOnStack(CoroX *self)
2.147 {
2.148 uint8_t dummy;
2.149 ptrdiff_t p1 = (ptrdiff_t)(&dummy);
2.150 - ptrdiff_t p2 = (ptrdiff_t)Coro_CurrentStackPointer();
2.151 + ptrdiff_t p2 = (ptrdiff_t)CoroX_CurrentStackPointer();
2.152 int stackMovesUp = p2 > p1;
2.153 - ptrdiff_t start = ((ptrdiff_t)self->stack);
2.154 + ptrdiff_t start = ((ptrdiff_t)self->stack) ;
2.155 ptrdiff_t end = start + self->stackSize;
2.156
2.157 if (stackMovesUp)
2.158 - {
2.159 - return end - p1;
2.160 - }
2.161 + return end - p1 - STACK_OVERHEAD;
2.162 else
2.163 - {
2.164 - return p1 - start;
2.165 - }
2.166 + return p1 - start - STACK_OVERHEAD;
2.167 }
2.168
2.169 -int Coro_stackSpaceAlmostGone(Coro *self)
2.170 +int CoroX_stackSpaceAlmostGone(CoroX *self)
2.171 {
2.172 - return Coro_bytesLeftOnStack(self) < CORO_STACK_SIZE_MIN;
2.173 + return CoroX_bytesLeftOnStack(self) < 2048;
2.174 }
2.175
2.176
2.177 -void Coro_initializeMainCoro(Coro *self)
2.178 -{
2.179 - self->isMain = 1;
2.180 -}
2.181 +#pragma mark SETUP & SWITCH:
2.182
2.183
2.184 -static void Coro_StartWithArg(CallbackBlock *block)
2.185 -{
2.186 - (block->func)(block->context);
2.187 - fprintf(stderr,"CoroX error: returned from coro start function\n");
2.188 - abort();
2.189 -}
2.190 -
2.191 typedef void (*makecontext_func)(void);
2.192
2.193 -static void Coro_setup(Coro *self, void *arg)
2.194 +static void CoroX_setup_(CoroX *self)
2.195 {
2.196 - ucontext_t *ucp = (ucontext_t *) &self->env;
2.197 - getcontext(ucp);
2.198 + size_t stackSize = self->stackSize + STACK_OVERHEAD;
2.199 + self->stack = valloc(stackSize);
2.200 + assert(self->stack);
2.201
2.202 - ucp->uc_stack.ss_sp = Coro_stack(self);
2.203 - ucp->uc_stack.ss_size = Coro_stackSize(self);
2.204 - ucp->uc_stack.ss_flags = 0;
2.205 - ucp->uc_link = NULL;
2.206 -
2.207 - makecontext(ucp, (makecontext_func)Coro_StartWithArg, 1, arg);
2.208 + getcontext(&self->env);
2.209 + self->env.uc_stack.ss_sp = self->stack;
2.210 + self->env.uc_stack.ss_size = stackSize;
2.211 + self->env.uc_stack.ss_flags = 0;
2.212 + self->env.uc_link = NULL;
2.213 + makecontext(&self->env, (makecontext_func)self->entryPoint, 1, self);
2.214 }
2.215
2.216
2.217 -void Coro_startCoro_(Coro *self, Coro *other, void *context, CoroStartCallback *callback)
2.218 +void CoroX_switchTo_(CoroX *self, CoroX *next)
2.219 {
2.220 - assert(other->stack==NULL);
2.221 - other->stack = calloc(1, other->stackSize + 16);
2.222 -
2.223 - CallbackBlock callbackBlock = {context,callback};
2.224 - Coro_setup(other, &callbackBlock);
2.225 -
2.226 - Coro_switchTo_(self, other);
2.227 -}
2.228 -
2.229 -void Coro_switchTo_(Coro *self, Coro *next)
2.230 -{
2.231 + if( ! next->stack && next->entryPoint )
2.232 + CoroX_setup_(next);
2.233 swapcontext(&self->env, &next->env);
2.234 }
2.235
3.1 --- a/Coroutines/CoroX.h Tue Apr 29 17:05:32 2008 -0700
3.2 +++ b/Coroutines/CoroX.h Wed Apr 30 14:18:49 2008 -0700
3.3 @@ -5,9 +5,6 @@
3.4 * Created by Jens Alfke on 4/29/08.
3.5 * Adapted from Steve Dekorte's libCoroutine:
3.6 * <http://www.dekorte.com/projects/opensource/libCoroutine/>
3.7 - * by putting it on a piece of wood and banging a few nails through it.
3.8 - * No, actually I removed all the stuff for cross-platform support, leaving only the simple
3.9 - * code that works on Mac OS X 10.5, and then cleaned things up a bit.
3.10 *
3.11 * Copyright 2008 Jens Alfke. All rights reserved.
3.12 * Copyright (c) 2002, 2003 Steve Dekorte. All rights reserved.
3.13 @@ -18,6 +15,8 @@
3.14 #pragma once
3.15 #include <stdlib.h>
3.16 #include <stdint.h>
3.17 +#include <stdbool.h>
3.18 +
3.19
3.20 /** C coroutine implementation for Mac OS X.
3.21 Based on, and API-compatible with, Steve Dekorte's libCoroutine.
3.22 @@ -28,30 +27,30 @@
3.23 extern "C" {
3.24 #endif
3.25
3.26 - typedef struct Coro Coro;
3.27 + typedef struct CoroX CoroX;
3.28
3.29 - Coro *Coro_new(void);
3.30 - void Coro_free(Coro *self);
3.31 + typedef void (*CoroEntryPoint)(CoroX*);
3.32 +
3.33 + CoroX* CoroX_new(CoroEntryPoint, void *userData); // use entryPoint==NULL to init main Coro
3.34 + void CoroX_free(CoroX*);
3.35
3.36 - // stack
3.37 + void CoroX_switchTo_(CoroX *current, CoroX *next);
3.38
3.39 - void *Coro_stack(Coro *self);
3.40 - size_t Coro_stackSize(Coro *self);
3.41 - void Coro_setStackSize_(Coro *self, size_t sizeInBytes);
3.42 - size_t Coro_bytesLeftOnStack(Coro *self);
3.43 - int Coro_stackSpaceAlmostGone(Coro *self);
3.44 + void* CoroX_userData(CoroX*);
3.45 + bool CoroX_isMain(CoroX*);
3.46
3.47 - // initialization
3.48 + // Stack:
3.49 +
3.50 + extern const size_t kCoroX_minStackSize; // Minimum useable stack size
3.51
3.52 - void Coro_initializeMainCoro(Coro *self);
3.53 -
3.54 - typedef void (CoroStartCallback)(void *);
3.55 -
3.56 - void Coro_startCoro_(Coro *self, Coro *other, void *context, CoroStartCallback *callback);
3.57 -
3.58 - // context-switch
3.59 -
3.60 - void Coro_switchTo_(Coro *self, Coro *next);
3.61 + size_t CoroX_getDefaultStackSize(void);
3.62 + void CoroX_setDefaultStackSize(size_t);
3.63 +
3.64 + void* CoroX_stack(CoroX*);
3.65 + size_t CoroX_stackSize(CoroX*);
3.66 + void CoroX_setStackSize_(CoroX*, size_t);
3.67 + size_t CoroX_bytesLeftOnStack(CoroX*);
3.68 + int CoroX_stackSpaceAlmostGone(CoroX*);
3.69
3.70 #ifdef __cplusplus
3.71 }
4.1 --- a/Coroutines/MYCoroutine.h Tue Apr 29 17:05:32 2008 -0700
4.2 +++ b/Coroutines/MYCoroutine.h Wed Apr 30 14:18:49 2008 -0700
4.3 @@ -15,7 +15,8 @@
4.4 @interface MYCoroutine : NSObject
4.5 {
4.6 @private
4.7 - struct Coro *_coro;
4.8 + struct CoroX *_coro;
4.9 + NSInvocation *_invocation;
4.10 NSString *_name;
4.11 }
4.12
4.13 @@ -33,42 +34,42 @@
4.14 /** Creates but does not start a coroutine. */
4.15 - (id) init;
4.16
4.17 -/** Starts a new coroutine, and performs the given invocation on it.
4.18 - The current coroutine will block (i.e. this call won't return)
4.19 - until some other coroutine tells it to resume. */
4.20 -- (void) startWithInvocation: (NSInvocation*)invocation;
4.21
4.22 -/** Starts a new coroutine, invoking its -main method as its body.
4.23 - Since the default implementation of -main is empty, this only makes sense to call
4.24 - on an instance of a subclass of MYCoroutine that's overridden -main.
4.25 - The current coroutine will block (i.e. this call won't return)
4.26 - until some other coroutine tells it to resume. */
4.27 -- (void) start;
4.28 +@property (retain) NSInvocation *invocation;
4.29 +
4.30 +/** The stack size of the coroutine. You can only change this before calling -start! */
4.31 +@property size_t stackSize;
4.32 +
4.33 +/** The coroutine's name. You can put anything you want here, as a debugging aid. */
4.34 +@property (copy) NSString* name;
4.35 +
4.36
4.37 /** The "main" method that will be called if the coroutine is started with -start.
4.38 The default implementation is empty, so a subclass using -start must override this. */
4.39 - (void) main;
4.40
4.41 -/** Returns control to an already-started (but blocked) coroutine.
4.42 +
4.43 +/** Returns control to a coroutine.
4.44 The most recent -resume call made from within that coroutine will return.
4.45 The current coroutine will block (i.e. this call won't return)
4.46 until some other coroutine tells it to resume. */
4.47 - (void) resume;
4.48
4.49 +- (id) call;
4.50
4.51 -/** The coroutine's name. You can put anything you want here, as a debugging aid. */
4.52 -@property (copy) NSString* name;
4.53 ++ (void) yieldToCaller: (id)value;
4.54 +
4.55
4.56 /** Returns YES if this is the currently executing coroutine. */
4.57 @property (readonly) BOOL isCurrent;
4.58
4.59 -/** The stack size of the coroutine. You can only change this before calling -start! */
4.60 -@property size_t stackSize;
4.61 -
4.62 /** The number of bytes of stack space left on this coroutine. */
4.63 @property (readonly) size_t bytesLeftOnStack;
4.64
4.65 /** Returns YES if this coroutine is almost out of stack space (less than 8k left) */
4.66 @property (readonly) BOOL stackSpaceAlmostGone;
4.67
4.68 +
4.69 ++ (void) setDefaultStackSize: (size_t)stackSize;
4.70 +
4.71 @end
5.1 --- a/Coroutines/MYCoroutine.m Tue Apr 29 17:05:32 2008 -0700
5.2 +++ b/Coroutines/MYCoroutine.m Wed Apr 30 14:18:49 2008 -0700
5.3 @@ -12,7 +12,7 @@
5.4
5.5
5.6 #ifndef LogTo
5.7 -#define kEnableLog 0 /* 1 enables logging, 0 disables it */
5.8 +#define kEnableLog 1 /* 1 enables logging, 0 disables it */
5.9 #define LogTo(DOMAIN,MSG,...) do{ if(kEnableLog) NSLog(@""#DOMAIN ": " MSG,__VA_ARGS__); }while(0)
5.10 #endif
5.11
5.12 @@ -21,20 +21,29 @@
5.13 #endif
5.14
5.15
5.16 +static void MYCoroutineStart( CoroX *coro );
5.17 +
5.18 +
5.19 @implementation MYCoroutine
5.20
5.21
5.22 static MYCoroutine *sMain, *sCurrent;
5.23
5.24 +static NSMutableArray *sCallers;
5.25 +static id sYieldResult;
5.26
5.27 -+ (void) initialize
5.28 +
5.29 +- (id) _initMain
5.30 {
5.31 - if( self == [MYCoroutine class] ) {
5.32 - sMain = [[self alloc] init];
5.33 - Coro_initializeMainCoro(sMain->_coro);
5.34 - sMain.name = @"MAIN";
5.35 - sCurrent = sMain;
5.36 + self = [super init];
5.37 + if (self != nil) {
5.38 + NSAssert(!sMain,@"Already created a main coroutine");
5.39 + _coro = CoroX_new(NULL,NULL);
5.40 + self.name = @"MAIN";
5.41 + sMain = self;
5.42 + LogTo(Coroutine,@"INIT %@ : _coro=%p",self,_coro);
5.43 }
5.44 + return self;
5.45 }
5.46
5.47
5.48 @@ -42,7 +51,7 @@
5.49 {
5.50 self = [super init];
5.51 if (self != nil) {
5.52 - _coro = Coro_new();
5.53 + _coro = CoroX_new(&MYCoroutineStart,self);
5.54 LogTo(Coroutine,@"INIT %@ : _coro=%p",self,_coro);
5.55 }
5.56 return self;
5.57 @@ -52,11 +61,20 @@
5.58 - (void) dealloc
5.59 {
5.60 LogTo(Coroutine,@"DEALLOC %@",self);
5.61 - Coro_free(_coro);
5.62 + CoroX_free(_coro);
5.63 [super dealloc];
5.64 }
5.65
5.66
5.67 ++ (void) initialize
5.68 +{
5.69 + if( self == [MYCoroutine class] ) {
5.70 + sCurrent = [[self alloc] _initMain];
5.71 + sCallers = [[NSMutableArray alloc] init];
5.72 + }
5.73 +}
5.74 +
5.75 +
5.76 - (NSString*) description
5.77 {
5.78 if( _name )
5.79 @@ -66,7 +84,7 @@
5.80 }
5.81
5.82
5.83 -@synthesize name=_name;
5.84 +@synthesize name=_name, invocation=_invocation;
5.85
5.86
5.87 + (MYCoroutine*) mainCoroutine {return sMain;}
5.88 @@ -74,70 +92,88 @@
5.89
5.90 - (BOOL) isCurrent {return self==sCurrent;}
5.91
5.92 -- (const void*) stack {return Coro_stack(_coro);}
5.93 -- (size_t) stackSize {return Coro_stackSize(_coro);}
5.94 -- (void) setStackSize: (size_t)size {Coro_setStackSize_(_coro,size);}
5.95 +- (const void*) stack {return CoroX_stack(_coro);}
5.96 +- (size_t) stackSize {return CoroX_stackSize(_coro);}
5.97 +- (void) setStackSize: (size_t)size {CoroX_setStackSize_(_coro,size);}
5.98
5.99 -- (size_t) bytesLeftOnStack {return Coro_bytesLeftOnStack(_coro);}
5.100 -- (BOOL) stackSpaceAlmostGone {return Coro_stackSpaceAlmostGone(_coro);}
5.101 ++ (void) setDefaultStackSize: (size_t)s {CoroX_setDefaultStackSize(s);}
5.102
5.103 +- (size_t) bytesLeftOnStack {return CoroX_bytesLeftOnStack(_coro);}
5.104 +- (BOOL) stackSpaceAlmostGone {return CoroX_stackSpaceAlmostGone(_coro);}
5.105
5.106 -static void startWithInvocation( void *context )
5.107 +- (BOOL) hasStarted {return CoroX_stack(_coro)!=NULL || self==sMain;}
5.108 +
5.109 +
5.110 +- (void) _start
5.111 {
5.112 - [(NSInvocation*)context invoke];
5.113 + if( _invocation )
5.114 + [_invocation invoke];
5.115 + else
5.116 + [self main];
5.117 + // If body returns, yield control to caller:
5.118 + LogTo(Coroutine,@"%@ finished",self);
5.119 + [MYCoroutine yieldToCaller: nil];
5.120 }
5.121
5.122 -static void startWithMain( void *context )
5.123 +static void MYCoroutineStart( CoroX *coro )
5.124 {
5.125 - [(MYCoroutine*)context main];
5.126 -}
5.127 -
5.128 -- (void) startWithInvocation: (NSInvocation*)invocation
5.129 -{
5.130 - LogTo(Coroutine,@"Starting %@ (currently in %@)",self,sCurrent);
5.131 - MYCoroutine *current = sCurrent;
5.132 - sCurrent = self;
5.133 -
5.134 - if( invocation )
5.135 - Coro_startCoro_(current->_coro, _coro, invocation, &startWithInvocation);
5.136 - else
5.137 - Coro_startCoro_(current->_coro, _coro, self, &startWithMain);
5.138 -
5.139 - sCurrent = current;
5.140 - LogTo(Coroutine,@"...resumed %@ after starting %@",sCurrent,self);
5.141 -}
5.142 -
5.143 -- (void) start
5.144 -{
5.145 - [self startWithInvocation: nil];
5.146 + MYCoroutine *self = CoroX_userData(coro);
5.147 + [self _start];
5.148 }
5.149
5.150
5.151 + (MYCoroutine*) startWithInvocation: (NSInvocation*)invocation
5.152 {
5.153 MYCoroutine *cr = [[self alloc] init];
5.154 - [cr startWithInvocation: invocation];
5.155 + cr.invocation = invocation;
5.156 + [cr resume];
5.157 return cr;
5.158 }
5.159
5.160
5.161 +- (void) main
5.162 +{
5.163 + // subclasses should override this if they don't use an invocation.
5.164 +}
5.165 +
5.166 +
5.167 +#pragma mark -
5.168 +#pragma mark CALLING:
5.169 +
5.170 +
5.171 - (void) resume
5.172 {
5.173 LogTo(Coroutine,@"Resuming %@ (currently in %@)",self,sCurrent);
5.174 MYCoroutine *current = sCurrent;
5.175 sCurrent = self;
5.176 - Coro_switchTo_(current->_coro,_coro);
5.177 + CoroX_switchTo_(current->_coro,_coro);
5.178 sCurrent = current;
5.179 LogTo(Coroutine,@"...resumed %@",sCurrent);
5.180 }
5.181
5.182
5.183 -- (void) main
5.184 +- (id) call
5.185 {
5.186 - // subclasses should override this.
5.187 + NSAssert(sCurrent!=self,@"Cannot call the current coroutine");
5.188 + [sCallers addObject: sCurrent];
5.189 + [self resume];
5.190 + id result = sYieldResult;
5.191 + sYieldResult = nil;
5.192 + return result;
5.193 }
5.194
5.195
5.196 ++ (void) yieldToCaller: (id)value
5.197 +{
5.198 + MYCoroutine *caller = sCallers.lastObject;
5.199 + NSAssert(caller, @"No caller to yield to");
5.200 + sYieldResult = value;
5.201 + [sCallers removeLastObject];
5.202 + [caller resume];
5.203 +}
5.204 +
5.205 +
5.206 +
5.207 @end
5.208
5.209
6.1 --- a/Coroutines/MYCoroutineTest.m Tue Apr 29 17:05:32 2008 -0700
6.2 +++ b/Coroutines/MYCoroutineTest.m Wed Apr 30 14:18:49 2008 -0700
6.3 @@ -7,6 +7,7 @@
6.4 //
6.5
6.6 #import "MYCoroutine.h"
6.7 +#import "CoroX.h"
6.8
6.9
6.10 @interface CoroTest1 : MYCoroutine
6.11 @@ -45,6 +46,17 @@
6.12
6.13 @synthesize value;
6.14
6.15 +- (void) regress: (int)depth
6.16 +{
6.17 + char useUpSpace[1024];
6.18 + useUpSpace[0] = 0;
6.19 + NSLog(@"infinite regress: depth=%i, stack space=%d", depth,self.bytesLeftOnStack);
6.20 + if( [[MYCoroutine currentCoroutine] stackSpaceAlmostGone] )
6.21 + NSLog(@"infinite regress: bailing out!");
6.22 + else
6.23 + [self regress: depth+1];
6.24 +}
6.25 +
6.26 - (void) main
6.27 {
6.28 int num = 0;
6.29 @@ -53,7 +65,6 @@
6.30 secondCoro = [[CoroTest2 alloc] init];
6.31 secondCoro.name = @"second";
6.32 secondCoro.value = 2;
6.33 - [secondCoro start];
6.34
6.35 while ( num < 100 )
6.36 {
6.37 @@ -63,26 +74,78 @@
6.38
6.39 [secondCoro release];
6.40
6.41 + NSLog(@"*** TESTING STACK LIMITS ***");
6.42 + [self regress: 1];
6.43 +
6.44 [[MYCoroutine mainCoroutine] resume];
6.45 }
6.46
6.47 ++ (void) test
6.48 +{
6.49 + NSLog(@"*** TESTING COROUTINES ***");
6.50 + firstCoro = [[CoroTest1 alloc] init];
6.51 + firstCoro.name = @"first";
6.52 + firstCoro.value = 1;
6.53 + [firstCoro resume];
6.54 +
6.55 + NSLog(@"Returned from coroutines; exiting");
6.56 +
6.57 + [firstCoro release];
6.58 +}
6.59 +
6.60 @end
6.61
6.62
6.63 +
6.64 +
6.65 +@interface Generator : MYCoroutine
6.66 +{ int _count; }
6.67 +- (id) initWithCount: (int)count;
6.68 +@end
6.69 +
6.70 +@implementation Generator
6.71 +
6.72 +- (id) initWithCount: (int)count
6.73 +{
6.74 + self = [super init];
6.75 + if (self != nil) {
6.76 + _count = count;
6.77 + }
6.78 + return self;
6.79 +}
6.80 +
6.81 +
6.82 +- (void) main
6.83 +{
6.84 + for( int i=1; i<=_count; i++ )
6.85 + [MYCoroutine yieldToCaller: [NSNumber numberWithInt: i]];
6.86 +}
6.87 +
6.88 ++ (void) test
6.89 +{
6.90 + NSLog(@"*** TESTING GENERATOR ***");
6.91 + Generator *g = [[Generator alloc] initWithCount: 10];
6.92 + id value;
6.93 + while( nil != (value = [g call]) )
6.94 + NSLog(@"Generator yielded %@",value);
6.95 + NSLog(@"Generator returned nil");
6.96 +}
6.97 +
6.98 +@end
6.99 +
6.100 +
6.101 +
6.102 int main()
6.103 {
6.104 NSAutoreleasePool *pool = [NSAutoreleasePool new];
6.105 + NSLog(@"Starting test...");
6.106
6.107 - NSLog(@"Starting test...");
6.108 - //[[[NSThread alloc] init] start];
6.109 - firstCoro = [[CoroTest1 alloc] init];
6.110 - firstCoro.name = @"first";
6.111 - firstCoro.value = 1;
6.112 - [firstCoro start];
6.113 + [MYCoroutine setDefaultStackSize: kCoroX_minStackSize];
6.114
6.115 - NSLog(@"Returned from coroutines; exiting");
6.116 + [CoroTest1 test];
6.117
6.118 - [firstCoro release];
6.119 + [Generator test];
6.120
6.121 + NSLog(@"FINISHED");
6.122 [pool drain];
6.123 }