3 // HigherOrderMessaging
5 // Created by Ofri Wolfus on 01/08/07.
6 // Copyright 2007 Ofri Wolfus. All rights reserved.
9 #import "DPCoroutine.h"
10 #include <libkern/OSAtomic.h>
13 @interface DPCoroutineStack (private)
15 - (DPCoroutine *)mainCoro;
16 - (DPCoroutine *)newCoroutineWithMessage:(DPMessage *)msg target:(id)t;
22 @interface DPCoroutine (private)
24 + (void)setCurrentCoroutine:(id)coro;
31 - (void)resetWithMessage:(DPMessage *)msg;
32 - (void)setTarget:(id)target;
38 - (void)setParent:(DPCoroutine *)co;
42 @implementation DPCoroutine
44 #define DPCURRENT_COROUTINE_KEY @"DPCurrentCoroutine"
46 + (id)currentCoroutine {
47 return [[[NSThread currentThread] threadDictionary] objectForKey:DPCURRENT_COROUTINE_KEY];
50 + (void)setCurrentCoroutine:(id)coro {
52 [[[NSThread currentThread] threadDictionary] setObject:coro forKey:DPCURRENT_COROUTINE_KEY];
54 [[[NSThread currentThread] threadDictionary] removeObjectForKey:DPCURRENT_COROUTINE_KEY];
58 if ((self = [super init])) {
68 - (id)initWithMessage:(DPMessage *)msg target:(id)t {
69 // Can't initialize without a message or a targe
75 if ((self = [self init])) {
84 [target release]; target = nil;
85 [message release]; message = nil;
91 OSAtomicIncrement32Barrier(&_refCount);
96 if (OSAtomicDecrement32Barrier(&_refCount) == 0)
100 - (unsigned)retainCount {
102 return (unsigned)_refCount;
106 if (!_coro->isMain) {
107 Coro_initializeMainCoro(_coro);
113 return _coro->isMain;
116 - (void)resetWithMessage:(DPMessage *)msg {
119 message = [msg retain];
122 - (void)setTarget:(id)t {
131 if (__builtin_expect(Coro_stackSpaceAlmostGone(_coro), 0)) {
132 NSLog(@"Coroutine %@'s stack is almost full. Allocating some more in hope not to crash.", self);
133 Coro_setStackSize_(_coro, Coro_stackSize(_coro) * 2);
136 Coro_switchTo_(_coro, [parent _coro]);
149 static void _DPCoroEntry(DPCoroutine *coro) {
150 coro->isActive = YES;
151 [coro->message sendTo:coro->target];
160 NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
161 [dict setObject:self forKey:DPCURRENT_COROUTINE_KEY];
162 Coro_startCoro_([parent _coro], _coro, self, (void *)_DPCoroEntry);
163 [dict setObject:parent forKey:DPCURRENT_COROUTINE_KEY];
168 NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
170 [dict setObject:self forKey:DPCURRENT_COROUTINE_KEY];
171 Coro_switchTo_([parent _coro], _coro);
172 [dict setObject:parent forKey:DPCURRENT_COROUTINE_KEY];
179 - (void)setParent:(DPCoroutine *)co {
185 @implementation DPCoroutineStack
187 static DPCoroutineStack *_mainStack = nil;
189 static void __attribute__((constructor)) _DPSetupMainCoroStack(void) {
190 _mainStack = [[DPCoroutineStack alloc] init];
191 [DPCoroutineStack setCurrentStack:_mainStack];
192 [[NSRunLoop currentRunLoop] addSource:_mainStack
193 forMode:NSDefaultRunLoopMode];
201 return [[[NSThread currentThread] threadDictionary] objectForKey:@"DPCoroutineStack"];
204 + (void)setCurrentStack:(id)stack {
206 [[[NSThread currentThread] threadDictionary] setObject:stack
207 forKey:@"DPCoroutineStack"];
209 [[[NSThread currentThread] threadDictionary] removeObjectForKey:@"DPCoroutineStack"];
213 if ((self = [super init])) {
214 mainCoros = [[NSMutableDictionary alloc] init];
215 mainCorosLock = OS_SPINLOCK_INIT;
216 stack = [[DPQueue alloc] init];
217 unusedCoroutines = [[DPQueue alloc] init];
225 [unusedCoroutines release]; unusedCoroutines = nil;
226 [stack release]; stack = nil;
227 [mainCoros release]; mainCoros = nil;
231 - (DPCoroutine *)newCoroutineWithMessage:(DPMessage *)msg target:(id)target {
232 DPCoroutine *coro = [unusedCoroutines dequeue];
235 [coro resetWithMessage:msg];
236 [coro setTarget:target];
238 coro = [DPCoroutine new];
241 return [coro autorelease];
244 - (void)addCoro:(DPCoroutine *)coro {
246 [coro setParent:[self mainCoro]];
253 OSAtomicIncrement32Barrier(&activeRoutines);
254 coro = [stack dequeue];
263 if ([coro isActive]) {
264 shouldContinue = [coro continue];
267 shouldContinue = [coro isActive];
270 // There's no active coroutine
271 [DPCoroutine setCurrentCoroutine:nil];
274 shouldContinue ? [stack enqueu:coro] : [unusedCoroutines enqueu:coro];
279 OSAtomicDecrement32Barrier(&activeRoutines);
284 - (DPCoroutine *)mainCoro {
285 DPThreadID *trID = [DPThreadID currentIdentifier];
286 OSSpinLockLock(&mainCorosLock);
287 DPCoroutine *coro = [mainCoros objectForKey:trID];
290 coro = [[DPCoroutine alloc] init];
292 [mainCoros setObject:coro forKey:trID];
295 OSSpinLockUnlock(&mainCorosLock);
300 while ([self hasWork])
309 return [stack count] + activeRoutines > 0;
314 @implementation NSObject (DPCoroutine)
316 - (void)coroutine:(DPMessage *)msg {
317 DPCoroutine *coro = [[DPCoroutine alloc] initWithMessage:msg
319 [[DPCoroutineStack currentStack] addCoro:coro];