DPWorkerThread now sets up a coroutine stack.
[hom.git] / Source / DPMultiThreading.m
blobb44a0ab5e75e8a6932e640769a348a3229070164
1 //
2 //  DPMultiThreading.m
3 //  HigherOrderMessaging
4 //
5 //  Created by Ofri Wolfus on 25/05/07.
6 //  Copyright 2007 Ofri Wolfus. All rights reserved.
7 //
8 //  Redistribution and use in source and binary forms, with or without modification,
9 //  are permitted provided that the following conditions are met:
10 //  
11 //  1. Redistributions of source code must retain the above copyright
12 //  notice, this list of conditions and the following disclaimer.
13 //  2. Redistributions in binary form must reproduce the above copyright
14 //  notice, this list of conditions and the following disclaimer in the
15 //  documentation and/or other materials provided with the distribution.
16 //  3. Neither the name of Ofri Wolfus nor the names of his contributors
17 //  may be used to endorse or promote products derived from this software
18 //  without specific prior written permission.
20 //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 //  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 //  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 //  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 //  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 //  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 //  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 //  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 //  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <libkern/OSAtomic.h>
32 #import "DPMultiThreading.h"
33 #import "DPCoroutine.h"
36 @interface NSInvocation (private)
37 // This is a private method that can be used to set
38 // a marg_list of an invocation.
39 // It's usually a bad idea to use private methods like this
40 // but there's no better alternative.
41 - (void)_setArgFrame:(void *)fp8;
42 @end
44 @implementation NSInvocation (DPMessageSupport)
46 + (id)invocationWithMessage:(DPMessage *)msg receiver:(id)target {
47         NSMethodSignature *sig = nil;
48         NSInvocation *i = nil;
49         
50         // Let's be on the safe side
51         NSAssert(msg != nil, @"Can't create an invocation from nil message!");
52         NSAssert(target != nil, @"Can't create an invocation with no target."
53                          " Isn't that what DPMessage is for?");
54         
55         sig = [target methodSignatureForSelector:[msg selector]];
56         
57         // Apparently +[NSInvocation invocationWithMethodSignature:] doesn't
58         // assert for  nil signature, so we do it instead.
59         if (!sig)
60                 [NSException raise:NSInternalInconsistencyException
61                                         format:@"Can't get method signature for [%@ %@].",
62                         NSStringFromClass([target class]), NSStringFromSelector([msg selector])];
63         
64         i = [NSInvocation invocationWithMethodSignature:sig];
65         
66         // No worries, NSInvocation takes care of copying the frame.
67         [i _setArgFrame:[msg arguments]];
68         
69         // This must come after _setArgFrame: so that the invocation
70         // can update its frame for the new target.
71         [i setTarget:target];
72         
73         return i;
76 @end
78 @interface DPInvocationQueue (private)
79 - (void)_stopCurrentRunLoop;
80 - (BOOL)_theApocalypseArrived;
81 - (void)_startTheApocalypes;
82 @end
84 @interface NSThread (DPPrivate)
85 - (NSRunLoop *)_DPRunLoop;
86 @end
88 @implementation NSThread (DPWorkerThread)
90 + (void)setupNewWorkThread:(DPConditionVariable *)cond {
91         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
92         NSThread *thread = [self currentThread];
93         DPInvocationQueue *queue = [DPInvocationQueue defaultQueue];
94         DPCoroutineStack *stack = [[DPCoroutineStack alloc] init];
95         
96         // Set up a runloop
97         NSRunLoop *rl = [NSRunLoop currentRunLoop];
98         
99         // Set up all the needed sources
100         [rl addSource:queue forMode:NSDefaultRunLoopMode];
101         [rl addSource:stack forMode:NSDefaultRunLoopMode];
102         
103         // Set the results pointer
104         [cond signalValue:thread];
105         
106         // Set up an autorelease pool
107         // NOTE: The DPSetUpAutoreleasePool() implementation included in
108         // this file handles nil values but the one in UITestingKit 1.0b1
109         // and 1.0rc1 will crash on nil.
110         DPSetUpAutoreleasePool(NSDefaultRunLoopMode);
111         
112         // Keep a reference to our runloop
113         [[thread threadDictionary] setObject:rl forKey:@"DPRunLoop"];
114         
115         // Run our runloop until someone stops it
116         //CFRunLoopRun();
117         while (![queue _theApocalypseArrived])
118                 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2.0, YES);
119         
120         // Clean up
121         [stack release];
122         [pool release];
125 + (id)detachNewWorkerThread {
126         DPConditionVariable *cond = [[[DPConditionVariable alloc] init] autorelease];
127         
128         // Launch the new thread
129         [self detachNewThreadSelector:@selector(setupNewWorkThread:)
130                                                  toTarget:self
131                                            withObject:cond];
132         
133         // Wait for the thread to return us itself
134         return [cond wait];
137 - (DPInvocationQueue *)defaultInvocationQueue {
138         return [[self threadDictionary] objectForKey:@"DPInvocationQueue"];
141 - (void)setDefaultInvocationQueue:(DPInvocationQueue *)queue {
142         NSMutableDictionary *dict = [self threadDictionary];
143         
144         @synchronized(dict) {
145                 [dict setObject:queue forKey:@"DPInvocationQueue"];
146         }
149 - (NSRunLoop *)_DPRunLoop {
150         return [[self threadDictionary] objectForKey:@"DPRunLoop"];
153 - (void)terminate {
154         /*DPInvocationQueue *q = [self defaultInvocationQueue];
155         
156         if (q) {
157                 CFRunLoopRef rl = [self _DPRunLoop];
158                 [q appendInvocation:[NSInvocation invocationWithMessage:MSG(_stopCurrentRunLoop)
159                                                                                                            receiver:q]];
160                 if (rl)
161                         CFRunLoopWakeUp(rl);
162         }*/
163         [[self defaultInvocationQueue] _startTheApocalypes];
166 - (id)sendMessage:(DPMessage *)msg to:(id)obj {
167         DPInvocationQueue *q = [self defaultInvocationQueue];
168         NSRunLoop *rl = [self _DPRunLoop];
169         id future = nil;
170         
171         // Make sure we got a queue
172         if (!q)
173                 [NSException raise:NSInternalInconsistencyException
174                                         format:@"Thread <%@> has no invocation queue.", self];
175         
176         // DPInvocationQueue returns a future for invocations with object
177         // return or nil, so we simply return that.
178         future = [q appendInvocation:[NSInvocation invocationWithMessage:msg
179                                                                                                                         receiver:obj]];
180         
181         if (rl)
182                 // Wake up our runloop
183                 CFRunLoopWakeUp([rl getCFRunLoop]);
184         
185         return future;
188 #if !defined(MAC_OS_X_VERSION_10_5) || MAC_OS_X_VERSION_10_5 > MAC_OS_X_VERSION_MAX_ALLOWED
189 static NSThread *_mainThread = nil;
191 static __attribute__((constructor)) void _DPInitMainThread(void) {
192         _mainThread = [[NSThread currentThread] retain];
195 + (NSThread *)mainThread {
196         return _mainThread;
198 #endif
200 // Set up the default invocation queue of the main thread
201 // and add it to the thread's runloop in the default mode.
202 // This allows us to easily do [[NSThread mainThread] sendMessage:msg to:obj]
203 // which is more flexible than -[obj preformSelectorOnMainThread:sel withObject:obj]
204 static __attribute__((constructor)) void _DPSetUpMainThreadInvocationQueue(void) {
205         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
206         NSRunLoop *rl = [NSRunLoop currentRunLoop];
207         
208         [rl addSource:[DPInvocationQueue defaultQueue]
209                   forMode:NSDefaultRunLoopMode];
210         [[[NSThread currentThread] threadDictionary] setObject:rl
211                                                                                                         forKey:@"DPRunLoop"];
212         [pool release];
215 @end
219  * This is a base class for true proxies.
220  * Unlike NSProxy, DPProxy implements only the basic (thread-safe)
221  * memory management, and nothing more. This is in contrast to NSProxy
222  * that under the hood implements tons of methods, making it hard to
223  * create a proxy based on it.
224  */
225 @interface DPProxy {
226         Class           isa;
227         u_int32_t       refCount;
230 + (id)allocWithZone:(NSZone *)zone;
231 + (id)alloc;
233 - (NSZone *)zone;
235 // These are thread safe
236 - (id)retain;
237 - (id)autorelease;
238 - (void)release;
239 - (u_int32_t)retainCount;
241 - (void)dealloc;
243 // Must be overridden by subclasses
244 - (id)forward:(SEL)sel :(marg_list)args;
246 @end
248 @implementation DPProxy
250 // Every base class must implement +initialize
251 + (void)initialize {
254 + (id)allocWithZone:(NSZone *)zone {
255         DPProxy *p = (DPProxy *)NSAllocateObject(self, 0, zone);
256         p->refCount = 1U;
257         return p;
260 + (id)alloc {
261         return [self allocWithZone:NULL];
264 - (NSZone *)zone {
265         return NSZoneFromPointer(self);
268 - (id)retain {
269         if (!objc_collecting_enabled()) {
270                 OSAtomicIncrement32Barrier((int32_t *)&refCount);
271         }
272         
273         return self;
276 - (id)autorelease {
277         [NSAutoreleasePool addObject:self];
278         return self;
281 - (void)release {
282         if (!objc_collecting_enabled()) {
283                 if (OSAtomicDecrement32Barrier((int32_t *)&refCount) == 0U)
284                         [self dealloc];
285         }
288 - (void)dealloc {
289         if (!objc_collecting_enabled())
290                 NSDeallocateObject((id)self);
293 - (u_int32_t)retainCount {
294         OSMemoryBarrier();
295         return refCount;
298 - (id)forward:(SEL)sel :(marg_list)args {
299         //DP_SUBCLASS_MUST_IMPLEMENT;
300         return self;
303 // Let's play nice with Cocoa, that assumes *everything*
304 // (yes, even classes!) is ref counted. Argh!
305 + (id)retain {
306         return self;
309 + (id)autorelease {
310         return self;
313 + (void)release {
316 + (id)forward:(SEL)sel :(marg_list)args {
317         return self;
320 @end
324  * This DPProxy subclass implements the basic behaviour
325  * a proxy needs.
326  * Given a target, it'll simply forward *ANY* message to it
327  * and return the target's result.
328  * NOTE: DPFastProxy always returns the result of its target
329  * even if the target returned itself (except for retain
330  * and autorelease). Override -forward:: and implement this
331  * behaviour as needed.
332  */
333 @interface DPFastProxy : DPProxy {
334         id      target;
335         BOOL weakRef;
338 - (id)initWithTarget:(id)target weakRef:(BOOL)shouldRetainTarget;
340 @end
342 @implementation DPFastProxy
344 - (id)initWithTarget:(id)t weakRef:(BOOL)flag {
345         weakRef = flag;
346         target = weakRef ? t : [t retain];
347         
348         return self;
351 - (void)dealloc {
352         if (!weakRef)
353                 [target release];
354         
355         [super dealloc];
358 - (id)forward:(SEL)sel :(marg_list)args {
359         // dp_msgSendv() takes care for everything for us
360         return dp_msgSendv(target, sel, args);
363 - (id)retain {
364         [target retain];
365         return [super retain];
368 - (id)autorelease {
369         [target autorelease];
370         return [super autorelease];
373 - (void)release {
374         [target release];
375         [super release];
378 @end
381 @interface DPConditionVariable (private)
382 - (int)_condition;
383 @end
386 @interface DPFuture : DPFastProxy {
387         @private
388         DPConditionVariable *_condition;
391 // For DPInvocationQueue's usage only
392 - (id)initWithCondition:(DPConditionVariable *)cond;
394 @end
396 @implementation DPFuture
398 static Class _DPFastProxyCls = Nil;
400 + (void)initialize {
401         _DPFastProxyCls = objc_getClass("DPFastProxy");
404 - (id)initWithCondition:(DPConditionVariable *)cond {
405         // Guard against NULL pointers
406         NSAssert(cond != nil, @"Can't init without a condition");
407         
408         _condition = [cond retain];
409         // Set our target to nil so retain/release will just
410         // message nil (which has no effect).
411         target = nil;
412         
413         return self;
416 - (void)dealloc {
417         [_condition release]; _condition = nil;
418         [super dealloc];
421 - (id)forward:(SEL)sel :(marg_list)args {
422         // We wait for the condition variable to become valid,
423         // and then initialize with its value (which is our target).
424         // NOTE: This is *NOT* thread safe as we assume a future lives
425         // in a single thread.
426         [self initWithTarget:[_condition wait] weakRef:NO];
427         // We no longer need the condition variable
428         [_condition release]; _condition = nil;
429         // Let's skip this check and just mutate ourself to
430         // a fast proxy.
431         self->isa = _DPFastProxyCls;
432         
433         // Now we can safely forward the message.
434         // Since we're actually a DPFastProxy instance at this point
435         // it's [self forward::] and not [super forward::].
436         return [self forward:sel :args];
439 BOOL DPObjectIsFuture(id obj) {
440         if (obj->isa == objc_getClass("DPFuture"))
441                 return [((DPFuture *)obj)->_condition _condition] == -1;
442         return NO;
445 @end
448 #ifndef __UITESTINGKIT__
449 @implementation DPRunLoopSource
451 static void _DPSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) {
452         [(DPRunLoopSource *)info scheduleWithRunLoop:rl forMode:(NSString *)mode];
455 static void _DPCancel(void *info, CFRunLoopRef rl, CFStringRef mode) {
456         [(DPRunLoopSource *)info cancelMode:(NSString *)mode forRunLoop:rl];
459 static void _DPPerform(void *info) {
460         [(DPRunLoopSource *)info fire];
463 - (id)initWithPriority:(unsigned)priority {
464         if ((self = [super init])) {
465                 CFRunLoopSourceContext cont = {
466                         0,                                      // version
467                         self,                           // info
468                         NULL,                           // retain
469                         NULL,                           // release
470                         /* the following 3 functions work fine with
471                            ObjC objects, thanks to toll-free bridging */
472                         CFCopyDescription,      // copy description
473                         CFEqual,                        // equal
474                         CFHash,                         // hash
475                         _DPSchedule,            // schedule
476                         _DPCancel,                      // cancel
477                         _DPPerform                      // perform
478                 };
479                 
480                 _refCount = 1;
481                 _src = CFRunLoopSourceCreate(kCFAllocatorDefault, priority, &cont);
482         }
483         
484         return self;
487 - (id)init {
488         return [self initWithPriority:0];
491 - (void)dealloc {
492         [self invalidate];
493         CFRelease(_src); _src = NULL;
494         [super dealloc];
497 // CF Runloop sources are thread safe, so we should be too
498 - (id)retain {
499         OSAtomicIncrement32Barrier(&_refCount);
500         return self;
503 - (void)release {
504         if (OSAtomicDecrement32Barrier(&_refCount) == 0)
505                 [self dealloc];
508 - (unsigned)retainCount {
509         OSMemoryBarrier();
510         return (unsigned)_refCount;
513 - (void)fire {
516 - (void)signal {
517         CFRunLoopSourceSignal(_src);
520 - (void)invalidate {
521         CFRunLoopSourceInvalidate(_src);
524 - (BOOL)isValid {
525         return CFRunLoopSourceIsValid(_src);
528 - (unsigned)priority {
529         return CFRunLoopSourceGetOrder(_src);
532 - (void)scheduleWithRunLoop:(CFRunLoopRef)rl forMode:(NSString *)mode {
535 - (void)cancelMode:(NSString *)mode forRunLoop:(CFRunLoopRef)rl {
538 - (CFRunLoopSourceRef)getCFRunLoopSource {
539         return _src;
542 @end
545 @implementation NSRunLoop (DPRunLoopSource)
547 - (void)addSource:(DPRunLoopSource *)src forMode:(NSString *)mode {
548         CFRunLoopAddSource([self getCFRunLoop], [src getCFRunLoopSource], (CFStringRef)mode);
551 - (void)removeSource:(DPRunLoopSource *)src forMode:(NSString *)mode {
552         CFRunLoopRemoveSource([self getCFRunLoop], [src getCFRunLoopSource], (CFStringRef)mode);
555 - (BOOL)containsSource:(DPRunLoopSource *)src inMode:(NSString *)mode {
556         return (BOOL)CFRunLoopContainsSource([self getCFRunLoop], [src getCFRunLoopSource],
557                                                                                  (CFStringRef)mode);
560 @end
561 #endif //__UITESTINGKIT__
564 @implementation DPQueue
566 - (id)init {
567         if ((self = [super init])) {
568                 _refCount = 1;
569                 queue = [[NSMutableArray alloc] init];
570                 queueLock = OS_SPINLOCK_INIT;
571         }
572         
573         return self;
576 - (void)dealloc {
577         [queue release]; queue = nil;
578         [super dealloc];
581 // We need thread-safe ref counting.
582 // This might actually be faster than NSObject's implementation.
583 - (id)retain {
584         OSAtomicIncrement32Barrier(&_refCount);
585         return self;
588 - (void)release {
589         if (OSAtomicDecrement32Barrier(&_refCount) == 0)
590                 [self dealloc];
593 - (unsigned)retainCount {
594         OSMemoryBarrier();
595         return (unsigned)_refCount;
598 - (void)enqueu:(id)obj {
599         OSSpinLockLock(&queueLock);
600         [queue addObject:obj];
601         OSSpinLockUnlock(&queueLock);
604 - (id)dequeue {
605         id obj = nil;
606         
607         OSSpinLockLock(&queueLock);
608         if ([queue count]) {
609                 obj = [[queue objectAtIndex:0] retain];
610                 [queue removeObjectAtIndex:0];
611         }
612         OSSpinLockUnlock(&queueLock);
613         
614         return [obj autorelease];
617 - (unsigned)count {
618         unsigned count;
619         
620         OSSpinLockLock(&queueLock);
621         count = [queue count];
622         OSSpinLockUnlock(&queueLock);
623         
624         return count;
627 @end
630 @implementation DPInvocationQueue
632 + (id)defaultQueue {
633         NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
634         DPInvocationQueue *q = [dict objectForKey:@"DPInvocationQueue"];
635         
636         if (!q) {
637                 q = [[DPInvocationQueue alloc] init];
638                 @synchronized (dict) {
639                         [dict setObject:q forKey:@"DPInvocationQueue"];
640                 }
641                 [q release];
642         }
643         
644         return q;
647 - (id)init {
648         if ((self = [super init])) {
649                 //queue = [[NSMutableArray allocWithZone:[self zone]] init];
650                 //queueLock = [[NSRecursiveLock allocWithZone:[self zone]] init];
651                 queue = [[DPQueue alloc] init];
652                 _theApocalypseArrived = 0;
653         }
654         
655         return self;
658 - (void)dealloc {
659         //[queueLock lock];
660         [queue release]; queue = nil;
661         //[queueLock unlock];
662         //[queueLock release]; queueLock = nil;
663         [super dealloc];
666 - (id)appendInvocation:(NSInvocation *)invocation {
667         const char *retType = [[invocation methodSignature] methodReturnType];
668         DPFuture *future = nil;
669         DPConditionVariable *condition = nil;
670         NSDictionary *dict;
671         
672         // For object values we'll create a condition variable and a future,
673         // and keep them in our queue together with the invocation.
674         if (strcmp(retType, @encode(id)) == 0 || strcmp(retType, @encode(Class)) == 0) {
675                 condition = [[[DPConditionVariable alloc] init] autorelease];
676                 future = [[(DPFuture *)[DPFuture alloc] initWithCondition:condition] autorelease];
677         }
678         
679         dict = [NSDictionary dictionaryWithObjectsAndKeys:invocation, @"invocation",
680                                 // This method stops when it sees nil, so if we condition == nil
681                                 // we'll end up just with the invocation.
682                                                                                   condition ?: nil, @"condition", nil];
683         
684         //[queueLock lock];
685         //[queue addObject:dict];
686         //[queueLock unlock];
687         [queue enqueu:dict];
688         
689         [self signal];
690         
691         // We're done
692         return future;
695 /*- (void)removeInvocation:(NSInvocation *)invocation {
696         [queueLock lock];
697         
698         unsigned i, count = [queue count];
699         for (i = 0; i < count; i++) {
700                 if ([[queue objectAtIndex:i] objectForKey:@"invocation"] == invocation) {
701                         [queue removeObjectAtIndex:i];
702                         break;
703                 }
704         }
705         
706         [queueLock unlock];
709 - (void)pushAllInvocations {
710         /*[queueLock lock];
711         
712         unsigned i, count = [queue count];
713         // Loop through all invocations and invoke them
714         for (i = 0U; i < count; i++) {
715                 NSDictionary *dict = [queue objectAtIndex:i];
716                 NSInvocation *invocation = [dict objectForKey:@"invocation"];
717                 id cond = [dict objectForKey:@"condition"];
718                 
719                 [invocation invoke];
720                 
721                 // If we have a memory block it means we're expecting an object reuturn.
722                 // In that case we'll copy the value from our invocation.
723                 if (cond) {
724                         id val;
725                         // Get the return value (which is an object (pointer)).
726                         [invocation getReturnValue:&val];
727                         [cond broadcastValue:val];
728                 }
729         }
730         
731         // Clean up the queues
732         [queue removeAllObjects];
733         
734         [queueLock unlock];*/
735         
736         while ([queue count]) {
737                 NSDictionary *dict = [queue dequeue];
738                 NSInvocation *invocation = [dict objectForKey:@"invocation"];
739                 id cond = [dict objectForKey:@"condition"];
740                 
741                 [invocation invoke];
742                 
743                 // If we have a memory block it means we're expecting an object reuturn.
744                 // In that case we'll copy the value from our invocation.
745                 if (cond) {
746                         id val;
747                         // Get the return value (which is an object (pointer)).
748                         [invocation getReturnValue:&val];
749                         [cond broadcastValue:val];
750                 }
751         }
754 - (BOOL)process {
755         NSDictionary *dict = [queue dequeue];
756         // Lock
757         /*[queueLock lock];
758         // Stop now if the queue is empty
759         if ([queue count] == 0) {
760                 [queueLock unlock];
761                 return NO;
762         }
763         // Get the first entry from the queue.
764         // Must retain as removing releases the entry
765         dict = [[queue objectAtIndex:0] retain];
766         [queue removeObjectAtIndex:0];
767         // We're good to do
768         [queueLock unlock];*/
769         
770         if (!dict)
771                 return NO;
772         
773         // Signal so the runloop won't go to sleep
774         [self signal];
775         
776         // Get the invocation and condition variable from the entry
777         NSInvocation *invocation = [dict objectForKey:@"invocation"];
778         id cond = [dict objectForKey:@"condition"];
779         
780         [invocation invoke];
781         
782         // If we have a condition variable it means somewhere
783         // there's a future waiting on it (or not).
784         if (cond) {
785                 id val;
786                 [invocation getReturnValue:&val];
787                 [cond broadcastValue:val];
788         }
789         
790         // We're done
791         //[dict release];
792         return YES;
795 - (void)fire {
796         [self process];
799 - (void)_stopCurrentRunLoop {
800         CFRunLoopStop(CFRunLoopGetCurrent());
803 - (BOOL)_theApocalypseArrived {
804         //OSMemoryBarrier();
805         return _theApocalypseArrived != 0;
808 - (void)_startTheApocalypes {
809         _theApocalypseArrived = YES;
810         //OSAtomicCompareAndSwap32Barrier(0, 1, &_theApocalypseArrived);
813 @end
816 @implementation DPConditionVariable
818 - (id)init {
819         if ((self = [super init])) {
820                 _refCount = 1;
821                 NSAssert(pthread_mutex_init(&_lock, NULL) == 0, @"Can't create pthread mutex");
822                 NSAssert(pthread_cond_init(&_condition, NULL) == 0, @"Can't create pthread condition variable");
823                 _value = NULL;
824                 _conditionValue = -1;
825         }
826         
827         return self;
830 - (void)dealloc {
831         NSAssert(pthread_cond_destroy(&_condition) == 0, @"Can't destroy pthread condition variable");
832         NSAssert(pthread_mutex_destroy(&_lock) == 0, @"Can't destroy pthread mutex");
833         [super dealloc];
836 // We need thread-safe ref counting.
837 // This might actually be faster than NSObject's implementation.
838 - (id)retain {
839         OSAtomicIncrement32Barrier(&_refCount);
840         return self;
843 - (void)release {
844         if (OSAtomicDecrement32Barrier(&_refCount) == 0)
845                 [self dealloc];
848 - (unsigned)retainCount {
849         OSMemoryBarrier();
850         return (unsigned)_refCount;
853 // TODO: Implement double-checked locking?
854 // The needed memory barriers are already in place
855 // in the write methods.
856 - (void *)waitForCondition:(int)cond {
857         void *val;
858         
859         pthread_mutex_lock(&_lock);
860         
861         while (_conditionValue != cond)
862                 pthread_cond_wait(&_condition, &_lock);
863         
864         val = (void *)_value;
865         pthread_mutex_unlock(&_lock);
866         
867         return val;
870 - (void *)wait {
871         return [self waitForCondition:1];
874 - (void)signalCondition:(int)cond withValue:(void *)val {
875         pthread_mutex_lock(&_lock);
876         _value = val;
877         OSMemoryBarrier();
878         _conditionValue = cond;
879         pthread_mutex_unlock(&_lock);
880         pthread_cond_signal(&_condition);
883 - (void)signalValue:(void *)val {
884         [self signalCondition:1 withValue:val];
887 - (void)broadcastCondition:(int)cond withValue:(void *)val {
888         pthread_mutex_lock(&_lock);
889         _value = val;
890         OSMemoryBarrier();
891         _conditionValue = cond;
892         pthread_mutex_unlock(&_lock);
893         pthread_cond_broadcast(&_condition);
896 - (void)broadcastValue:(void *)cond {
897         [self broadcastCondition:1 withValue:cond];
900 - (void)clear {
901         pthread_mutex_lock(&_lock);
902         _conditionValue = -1;
903         OSMemoryBarrier();
904         _value = NULL;
905         pthread_mutex_unlock(&_lock);
908 - (int)_condition {
909         OSMemoryBarrier();
910         return _conditionValue;
913 @end
915 #include <sys/sysctl.h>
917 @implementation DPThreadPool
919 + (int)activeCPUs {
920         int num;
921         unsigned long length = sizeof(num);
922         
923         if (sysctlbyname("hw.activecpu", &num, &length, NULL, 0))
924                 num = -1;       // An error had occured
925         
926         return num;
929 + (id)poolWithNumberOfThreads:(unsigned)threadsCount {
930         DPThreadPool *pool = [[self alloc] init];
931         unsigned i;
932         
933         for (i = 0; i < threadsCount; i++)
934                 [pool spawnThread];
935         
936         return [pool autorelease];
939 + (void)_stopRunLoop:(CFRunLoopRef)rl {
940         CFRunLoopStop(rl);
943 - (id)init {
944         if ((self = [super init])) {
945                 _refCount = 1;
946                 queue = [[DPInvocationQueue alloc] init];
947                 coroStack = [[DPCoroutineStack alloc] init];
948                 threads = [[NSMutableArray alloc] init];
949         }
950         
951         return self;
954 // We need thread-safe ref counting.
955 // This might actually be faster than NSObject's implementation.
956 - (id)retain {
957         OSAtomicIncrement32Barrier(&_refCount);
958         return self;
961 - (void)release {
962         if (OSAtomicDecrement32Barrier(&_refCount) == 0)
963                 [self dealloc];
966 - (unsigned)retainCount {
967         OSMemoryBarrier();
968         return (unsigned)_refCount;
971 - (void)dealloc {
972         [queue _startTheApocalypes];
973         [coroStack release]; coroStack = nil;
974         [threads release]; threads = nil;
975         [queue release]; queue = nil;
976         [super dealloc];
979 // Since NSThread retains its target, we make this a class method
980 // in orde to avoid the retain loop. Retaining our class is meaningless.
981 + (void)_setUpThread:(NSArray *)info {
982         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
983         NSRunLoop *rl = [NSRunLoop currentRunLoop];
984         NSThread *thread = [NSThread currentThread];
985         DPInvocationQueue *queue = [[info objectAtIndex:0] retain];
986         DPConditionVariable *cond = [[info objectAtIndex:1] retain];
987         DPCoroutineStack *stack = [[info objectAtIndex:2] retain];
988         
989         // Add our queue as the default queue
990         [thread setDefaultInvocationQueue:queue];
991         [rl addSource:queue forMode:NSDefaultRunLoopMode];
992         
993         // Add the coroutine stack
994         [DPCoroutineStack setCurrentStack:stack];
995         [rl addSource:stack forMode:NSDefaultRunLoopMode];
996         
997         // Set up an autorelease pool
998         // NOTE: The DPSetUpAutoreleasePool() implementation included in
999         // this file handles nil values but the one in UITestingKit 1.0b1
1000         // and 1.0rc1 will crash on nil.
1001         DPSetUpAutoreleasePool(NSDefaultRunLoopMode);
1002         
1003         // Keep a reference to our runloop
1004         [[thread threadDictionary] setObject:rl forKey:@"DPRunLoop"];
1005         
1006         // We're good to go
1007         [cond signalValue:thread];
1008         
1009         // Run our runloop until someone stops it
1010         //CFRunLoopRun();
1011         while (![queue _theApocalypseArrived] || [stack hasWork])
1012                 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2.0, true);
1013         
1014         [rl removeSource:stack forMode:NSDefaultRunLoopMode];
1015         [rl removeSource:queue forMode:NSDefaultRunLoopMode];
1016         
1017         //[stack empty];
1018         
1019         // Clean up
1020         [stack release];
1021         [cond release];
1022         [queue release];
1023         [info release];
1024         [pool release];
1027 - (void)spawnThread {
1028         DPConditionVariable *cond = [[DPConditionVariable alloc] init];
1029         NSArray *info = [[NSArray alloc] initWithObjects:queue, cond, coroStack, nil];
1030         NSThread *thread;
1031         
1032         [NSThread detachNewThreadSelector:@selector(_setUpThread:)
1033                                                          toTarget:[self class]
1034                                                    withObject:info];
1035         
1036         thread = [cond wait];
1037         
1038         @synchronized (threads) {
1039                 [threads addObject:thread];
1040         }
1041         [cond release];
1044 - (void)terminateThread {
1045         [self sendMessage:MSG(_stopCurrentRunLoop)
1046                                    to:queue];
1049 - (unsigned)numberOfThreads {
1050         return [threads count];
1053 - (unsigned)activeThreads {
1054         unsigned i, count = [threads count], r = 0;
1055         
1056         for (i = 0; i < count; i++)
1057                 r += !CFRunLoopIsWaiting([[[threads objectAtIndex:i] _DPRunLoop] getCFRunLoop]);
1058         
1059         return r;
1062 - (id)sendMessage:(DPMessage *)msg to:(id)target {
1063         unsigned i, count = [threads count];
1064         id future = nil;
1065         
1066         // Add a termination invocation to the queue. The first thread that's idle
1067         // will receive it and terminate.
1068         future = [queue appendInvocation:[NSInvocation invocationWithMessage:msg
1069                                                                                                                                 receiver:target]];
1070         
1071         // If there's a sleeping thread, wake it up so it can do some work.
1072         // There's no need to wake more than one thread for a single task.
1073         for (i = 0; i < count; i++) {
1074                 NSRunLoop *rl = [[threads objectAtIndex:i] _DPRunLoop];
1075                 
1076                 if (rl && CFRunLoopIsWaiting([rl getCFRunLoop])) {
1077                         CFRunLoopWakeUp([rl getCFRunLoop]);
1078                         break;
1079                 }
1080         }
1081         
1082         return future;
1085 - (void)sendCoroutineMessage:(DPMessage *)msg to:(id)target {
1086         unsigned i, count = [threads count];
1087         
1088         // Add the coro to the stack
1089         DPCoroutine *coro = [[DPCoroutine alloc] initWithMessage:msg
1090                                                                                                           target:target];
1091         [coroStack addCoro:coro];
1092         [coro release];
1093         
1094         // If there's a sleeping thread, wake it up so it can do some work.
1095         // There's no need to wake more than one thread for a single task.
1096         for (i = 0; i < count; i++) {
1097                 NSRunLoop *rl = [[threads objectAtIndex:i] _DPRunLoop];
1098                 
1099                 if (rl && CFRunLoopIsWaiting([rl getCFRunLoop])) {
1100                         CFRunLoopWakeUp([rl getCFRunLoop]);
1101                         break;
1102                 }
1103         }
1106 @end
1109 @implementation DPThreadID
1111 static pthread_key_t _DPThreadIDKey;
1113 static void _DPThreadIDDestroy(void *ID) {
1114         [(DPThreadID *)ID release];
1115         pthread_setspecific(_DPThreadIDKey, NULL);
1118 + (void)initialize {
1119         pthread_key_create(&_DPThreadIDKey, _DPThreadIDDestroy);
1122 + (id)currentIdentifier {
1123         DPThreadID *ID = pthread_getspecific(_DPThreadIDKey);
1124         
1125         if (!ID)
1126                 ID = [[self alloc] init];
1127         
1128         return ID;
1131 - (id)init {
1132         DPThreadID *ID = pthread_getspecific(_DPThreadIDKey);
1133         
1134         // Prevent people from creating extra instances
1135         if (ID) {
1136                 [self release];
1137                 return [ID retain];
1138         }
1139         
1140         if ((self = [super init])) {
1141                 threadId = pthread_self();
1142                 pthread_setspecific(_DPThreadIDKey, ID);
1143         }
1144         
1145         return self;
1148 - (pthread_t)getThreadID {
1149         return threadId;
1152 extern unsigned DPIntHash(void *ptr);
1154 - (unsigned)hash {
1155         return DPIntHash(self);
1158 - (id)copyWithZone:(NSZone *)zone {
1159         return [self retain];
1162 @end
1165 @implementation NSObject (DPAsyncMessaging)
1167 - (void)receiveMessage:(DPMessage *)msg afterDelay:(NSTimeInterval)sec {
1168         // If this isn't simple than what is?
1169         // Don't you love HOM? :)
1170         [NSTimer scheduledTimerWithTimeInterval:sec
1171                                                                  invocation:[NSInvocation invocationWithMessage:msg
1172                                                                                                                                            receiver:self]
1173                                                                         repeats:NO];
1176 - (void)_DPFutureThread:(NSArray *)info {
1177         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1178         DPMessage *msg = [info objectAtIndex:0];
1179         DPConditionVariable *cond = [info objectAtIndex:1];
1180         
1181         [cond signalValue:[msg sendTo:self]];
1182         // They count on us to release the condition variable and the message
1183         [cond release];
1184         [msg release];
1185         [pool release];
1188 - (id)future:(DPMessage *)msg {
1189         DPConditionVariable *cond = [[DPConditionVariable alloc] init];
1190         DPFuture *future = [[(DPFuture *)[DPFuture alloc] initWithCondition:cond] autorelease];
1191         
1192         [NSThread detachNewThreadSelector:@selector(_DPFutureThread:)
1193                                                          toTarget:self
1194                                                 // Note that we must copy the message to ensure it won't go away.
1195                                                    withObject:[NSArray arrayWithObjects:[msg copy], cond, nil]];
1196         return future;
1199 @end
1202 #ifndef __UITESTINGKIT__
1203 static void _DPMaintainAutoreleasePool(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
1204         NSAutoreleasePool **pool = info;
1205         [*pool release];
1206         *((NSAutoreleasePool **)info) = [[NSAutoreleasePool alloc] init];
1209 static void _DPReleasePool(const void *info) {
1210         NSAutoreleasePool **pool = (void *)info;
1211         [*pool release];
1212         free((void *)info);
1215 void DPSetUpAutoreleasePool(NSString *runLoopMode) {
1216         CFRunLoopRef rl = CFRunLoopGetCurrent();
1217         CFStringRef mode = runLoopMode ? CFRetain((CFStringRef)runLoopMode) : CFRunLoopCopyCurrentMode(rl);
1218         
1219         if (mode) {
1220                 NSAutoreleasePool **p = malloc(sizeof(NSAutoreleasePool **));
1221                 CFRunLoopObserverContext context = {
1222                         0,                              // version
1223                         p,                              // info
1224                         NULL,                   // retain
1225                         _DPReleasePool, // release
1226                         NULL                    // copy description
1227                 };
1228                 *p = [[NSAutoreleasePool alloc] init];
1229                 CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
1230                                                                                                                                 kCFRunLoopEntry, true, 0,
1231                                                                                                                                 _DPMaintainAutoreleasePool,
1232                                                                                                                                 &context);
1233                 
1234                 CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, mode);
1235                 CFRelease(mode);
1236         }
1238 #endif //__UITESTINGKIT__