3 // HigherOrderMessaging
5 // Created by Ofri Wolfus on 25/05/07.
6 // Copyright 2007 Ofri Wolfus. All rights reserved.
8 // Redistribution and use in source and binary forms, with or without modification,
9 // are permitted provided that the following conditions are met:
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;
44 @implementation NSInvocation (DPMessageSupport)
46 + (id)invocationWithMessage:(DPMessage *)msg receiver:(id)target {
47 NSMethodSignature *sig = nil;
48 NSInvocation *i = nil;
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?");
55 sig = [target methodSignatureForSelector:[msg selector]];
57 // Apparently +[NSInvocation invocationWithMethodSignature:] doesn't
58 // assert for nil signature, so we do it instead.
60 [NSException raise:NSInternalInconsistencyException
61 format:@"Can't get method signature for [%@ %@].",
62 NSStringFromClass([target class]), NSStringFromSelector([msg selector])];
64 i = [NSInvocation invocationWithMethodSignature:sig];
66 // No worries, NSInvocation takes care of copying the frame.
67 [i _setArgFrame:[msg arguments]];
69 // This must come after _setArgFrame: so that the invocation
70 // can update its frame for the new target.
78 @interface DPInvocationQueue (private)
79 - (void)_stopCurrentRunLoop;
80 - (BOOL)_theApocalypseArrived;
81 - (void)_startTheApocalypes;
84 @interface NSThread (DPPrivate)
85 - (NSRunLoop *)_DPRunLoop;
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];
97 NSRunLoop *rl = [NSRunLoop currentRunLoop];
99 // Set up all the needed sources
100 [rl addSource:queue forMode:NSDefaultRunLoopMode];
101 [rl addSource:stack forMode:NSDefaultRunLoopMode];
103 // Set the results pointer
104 [cond signalValue:thread];
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);
112 // Keep a reference to our runloop
113 [[thread threadDictionary] setObject:rl forKey:@"DPRunLoop"];
115 // Run our runloop until someone stops it
117 while (![queue _theApocalypseArrived])
118 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2.0, YES);
125 + (id)detachNewWorkerThread {
126 DPConditionVariable *cond = [[[DPConditionVariable alloc] init] autorelease];
128 // Launch the new thread
129 [self detachNewThreadSelector:@selector(setupNewWorkThread:)
133 // Wait for the thread to return us itself
137 - (DPInvocationQueue *)defaultInvocationQueue {
138 return [[self threadDictionary] objectForKey:@"DPInvocationQueue"];
141 - (void)setDefaultInvocationQueue:(DPInvocationQueue *)queue {
142 NSMutableDictionary *dict = [self threadDictionary];
144 @synchronized(dict) {
145 [dict setObject:queue forKey:@"DPInvocationQueue"];
149 - (NSRunLoop *)_DPRunLoop {
150 return [[self threadDictionary] objectForKey:@"DPRunLoop"];
154 /*DPInvocationQueue *q = [self defaultInvocationQueue];
157 CFRunLoopRef rl = [self _DPRunLoop];
158 [q appendInvocation:[NSInvocation invocationWithMessage:MSG(_stopCurrentRunLoop)
163 [[self defaultInvocationQueue] _startTheApocalypes];
166 - (id)sendMessage:(DPMessage *)msg to:(id)obj {
167 DPInvocationQueue *q = [self defaultInvocationQueue];
168 NSRunLoop *rl = [self _DPRunLoop];
171 // Make sure we got a queue
173 [NSException raise:NSInternalInconsistencyException
174 format:@"Thread <%@> has no invocation queue.", self];
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
182 // Wake up our runloop
183 CFRunLoopWakeUp([rl getCFRunLoop]);
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 {
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];
208 [rl addSource:[DPInvocationQueue defaultQueue]
209 forMode:NSDefaultRunLoopMode];
210 [[[NSThread currentThread] threadDictionary] setObject:rl
211 forKey:@"DPRunLoop"];
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.
230 + (id)allocWithZone:(NSZone *)zone;
235 // These are thread safe
239 - (u_int32_t)retainCount;
243 // Must be overridden by subclasses
244 - (id)forward:(SEL)sel :(marg_list)args;
248 @implementation DPProxy
250 // Every base class must implement +initialize
254 + (id)allocWithZone:(NSZone *)zone {
255 DPProxy *p = (DPProxy *)NSAllocateObject(self, 0, zone);
261 return [self allocWithZone:NULL];
265 return NSZoneFromPointer(self);
269 if (!objc_collecting_enabled()) {
270 OSAtomicIncrement32Barrier((int32_t *)&refCount);
277 [NSAutoreleasePool addObject:self];
282 if (!objc_collecting_enabled()) {
283 if (OSAtomicDecrement32Barrier((int32_t *)&refCount) == 0U)
289 if (!objc_collecting_enabled())
290 NSDeallocateObject((id)self);
293 - (u_int32_t)retainCount {
298 - (id)forward:(SEL)sel :(marg_list)args {
299 //DP_SUBCLASS_MUST_IMPLEMENT;
303 // Let's play nice with Cocoa, that assumes *everything*
304 // (yes, even classes!) is ref counted. Argh!
316 + (id)forward:(SEL)sel :(marg_list)args {
324 * This DPProxy subclass implements the basic behaviour
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.
333 @interface DPFastProxy : DPProxy {
338 - (id)initWithTarget:(id)target weakRef:(BOOL)shouldRetainTarget;
342 @implementation DPFastProxy
344 - (id)initWithTarget:(id)t weakRef:(BOOL)flag {
346 target = weakRef ? t : [t retain];
358 - (id)forward:(SEL)sel :(marg_list)args {
359 // dp_msgSendv() takes care for everything for us
360 return dp_msgSendv(target, sel, args);
365 return [super retain];
369 [target autorelease];
370 return [super autorelease];
381 @interface DPConditionVariable (private)
386 @interface DPFuture : DPFastProxy {
388 DPConditionVariable *_condition;
391 // For DPInvocationQueue's usage only
392 - (id)initWithCondition:(DPConditionVariable *)cond;
396 @implementation DPFuture
398 static Class _DPFastProxyCls = Nil;
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");
408 _condition = [cond retain];
409 // Set our target to nil so retain/release will just
410 // message nil (which has no effect).
417 [_condition release]; _condition = nil;
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
431 self->isa = _DPFastProxyCls;
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;
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 = {
470 /* the following 3 functions work fine with
471 ObjC objects, thanks to toll-free bridging */
472 CFCopyDescription, // copy description
475 _DPSchedule, // schedule
477 _DPPerform // perform
481 _src = CFRunLoopSourceCreate(kCFAllocatorDefault, priority, &cont);
488 return [self initWithPriority:0];
493 CFRelease(_src); _src = NULL;
497 // CF Runloop sources are thread safe, so we should be too
499 OSAtomicIncrement32Barrier(&_refCount);
504 if (OSAtomicDecrement32Barrier(&_refCount) == 0)
508 - (unsigned)retainCount {
510 return (unsigned)_refCount;
517 CFRunLoopSourceSignal(_src);
521 CFRunLoopSourceInvalidate(_src);
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 {
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],
561 #endif //__UITESTINGKIT__
564 @implementation DPQueue
567 if ((self = [super init])) {
569 queue = [[NSMutableArray alloc] init];
570 queueLock = OS_SPINLOCK_INIT;
577 [queue release]; queue = nil;
581 // We need thread-safe ref counting.
582 // This might actually be faster than NSObject's implementation.
584 OSAtomicIncrement32Barrier(&_refCount);
589 if (OSAtomicDecrement32Barrier(&_refCount) == 0)
593 - (unsigned)retainCount {
595 return (unsigned)_refCount;
598 - (void)enqueu:(id)obj {
599 OSSpinLockLock(&queueLock);
600 [queue addObject:obj];
601 OSSpinLockUnlock(&queueLock);
607 OSSpinLockLock(&queueLock);
609 obj = [[queue objectAtIndex:0] retain];
610 [queue removeObjectAtIndex:0];
612 OSSpinLockUnlock(&queueLock);
614 return [obj autorelease];
620 OSSpinLockLock(&queueLock);
621 count = [queue count];
622 OSSpinLockUnlock(&queueLock);
630 @implementation DPInvocationQueue
633 NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
634 DPInvocationQueue *q = [dict objectForKey:@"DPInvocationQueue"];
637 q = [[DPInvocationQueue alloc] init];
638 @synchronized (dict) {
639 [dict setObject:q forKey:@"DPInvocationQueue"];
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;
660 [queue release]; queue = nil;
661 //[queueLock unlock];
662 //[queueLock release]; queueLock = nil;
666 - (id)appendInvocation:(NSInvocation *)invocation {
667 const char *retType = [[invocation methodSignature] methodReturnType];
668 DPFuture *future = nil;
669 DPConditionVariable *condition = nil;
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];
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];
685 //[queue addObject:dict];
686 //[queueLock unlock];
695 /*- (void)removeInvocation:(NSInvocation *)invocation {
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];
709 - (void)pushAllInvocations {
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"];
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.
725 // Get the return value (which is an object (pointer)).
726 [invocation getReturnValue:&val];
727 [cond broadcastValue:val];
731 // Clean up the queues
732 [queue removeAllObjects];
734 [queueLock unlock];*/
736 while ([queue count]) {
737 NSDictionary *dict = [queue dequeue];
738 NSInvocation *invocation = [dict objectForKey:@"invocation"];
739 id cond = [dict objectForKey:@"condition"];
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.
747 // Get the return value (which is an object (pointer)).
748 [invocation getReturnValue:&val];
749 [cond broadcastValue:val];
755 NSDictionary *dict = [queue dequeue];
758 // Stop now if the queue is empty
759 if ([queue count] == 0) {
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];
768 [queueLock unlock];*/
773 // Signal so the runloop won't go to sleep
776 // Get the invocation and condition variable from the entry
777 NSInvocation *invocation = [dict objectForKey:@"invocation"];
778 id cond = [dict objectForKey:@"condition"];
782 // If we have a condition variable it means somewhere
783 // there's a future waiting on it (or not).
786 [invocation getReturnValue:&val];
787 [cond broadcastValue:val];
799 - (void)_stopCurrentRunLoop {
800 CFRunLoopStop(CFRunLoopGetCurrent());
803 - (BOOL)_theApocalypseArrived {
805 return _theApocalypseArrived != 0;
808 - (void)_startTheApocalypes {
809 _theApocalypseArrived = YES;
810 //OSAtomicCompareAndSwap32Barrier(0, 1, &_theApocalypseArrived);
816 @implementation DPConditionVariable
819 if ((self = [super init])) {
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");
824 _conditionValue = -1;
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");
836 // We need thread-safe ref counting.
837 // This might actually be faster than NSObject's implementation.
839 OSAtomicIncrement32Barrier(&_refCount);
844 if (OSAtomicDecrement32Barrier(&_refCount) == 0)
848 - (unsigned)retainCount {
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 {
859 pthread_mutex_lock(&_lock);
861 while (_conditionValue != cond)
862 pthread_cond_wait(&_condition, &_lock);
864 val = (void *)_value;
865 pthread_mutex_unlock(&_lock);
871 return [self waitForCondition:1];
874 - (void)signalCondition:(int)cond withValue:(void *)val {
875 pthread_mutex_lock(&_lock);
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);
891 _conditionValue = cond;
892 pthread_mutex_unlock(&_lock);
893 pthread_cond_broadcast(&_condition);
896 - (void)broadcastValue:(void *)cond {
897 [self broadcastCondition:1 withValue:cond];
901 pthread_mutex_lock(&_lock);
902 _conditionValue = -1;
905 pthread_mutex_unlock(&_lock);
910 return _conditionValue;
915 #include <sys/sysctl.h>
917 @implementation DPThreadPool
921 unsigned long length = sizeof(num);
923 if (sysctlbyname("hw.activecpu", &num, &length, NULL, 0))
924 num = -1; // An error had occured
929 + (id)poolWithNumberOfThreads:(unsigned)threadsCount {
930 DPThreadPool *pool = [[self alloc] init];
933 for (i = 0; i < threadsCount; i++)
936 return [pool autorelease];
939 + (void)_stopRunLoop:(CFRunLoopRef)rl {
944 if ((self = [super init])) {
946 queue = [[DPInvocationQueue alloc] init];
947 coroStack = [[DPCoroutineStack alloc] init];
948 threads = [[NSMutableArray alloc] init];
954 // We need thread-safe ref counting.
955 // This might actually be faster than NSObject's implementation.
957 OSAtomicIncrement32Barrier(&_refCount);
962 if (OSAtomicDecrement32Barrier(&_refCount) == 0)
966 - (unsigned)retainCount {
968 return (unsigned)_refCount;
972 [queue _startTheApocalypes];
973 [coroStack release]; coroStack = nil;
974 [threads release]; threads = nil;
975 [queue release]; queue = nil;
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];
989 // Add our queue as the default queue
990 [thread setDefaultInvocationQueue:queue];
991 [rl addSource:queue forMode:NSDefaultRunLoopMode];
993 // Add the coroutine stack
994 [DPCoroutineStack setCurrentStack:stack];
995 [rl addSource:stack forMode:NSDefaultRunLoopMode];
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);
1003 // Keep a reference to our runloop
1004 [[thread threadDictionary] setObject:rl forKey:@"DPRunLoop"];
1007 [cond signalValue:thread];
1009 // Run our runloop until someone stops it
1011 while (![queue _theApocalypseArrived] || [stack hasWork])
1012 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2.0, true);
1014 [rl removeSource:stack forMode:NSDefaultRunLoopMode];
1015 [rl removeSource:queue forMode:NSDefaultRunLoopMode];
1027 - (void)spawnThread {
1028 DPConditionVariable *cond = [[DPConditionVariable alloc] init];
1029 NSArray *info = [[NSArray alloc] initWithObjects:queue, cond, coroStack, nil];
1032 [NSThread detachNewThreadSelector:@selector(_setUpThread:)
1033 toTarget:[self class]
1036 thread = [cond wait];
1038 @synchronized (threads) {
1039 [threads addObject:thread];
1044 - (void)terminateThread {
1045 [self sendMessage:MSG(_stopCurrentRunLoop)
1049 - (unsigned)numberOfThreads {
1050 return [threads count];
1053 - (unsigned)activeThreads {
1054 unsigned i, count = [threads count], r = 0;
1056 for (i = 0; i < count; i++)
1057 r += !CFRunLoopIsWaiting([[[threads objectAtIndex:i] _DPRunLoop] getCFRunLoop]);
1062 - (id)sendMessage:(DPMessage *)msg to:(id)target {
1063 unsigned i, count = [threads count];
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
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];
1076 if (rl && CFRunLoopIsWaiting([rl getCFRunLoop])) {
1077 CFRunLoopWakeUp([rl getCFRunLoop]);
1085 - (void)sendCoroutineMessage:(DPMessage *)msg to:(id)target {
1086 unsigned i, count = [threads count];
1088 // Add the coro to the stack
1089 DPCoroutine *coro = [[DPCoroutine alloc] initWithMessage:msg
1091 [coroStack addCoro:coro];
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];
1099 if (rl && CFRunLoopIsWaiting([rl getCFRunLoop])) {
1100 CFRunLoopWakeUp([rl getCFRunLoop]);
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);
1126 ID = [[self alloc] init];
1132 DPThreadID *ID = pthread_getspecific(_DPThreadIDKey);
1134 // Prevent people from creating extra instances
1140 if ((self = [super init])) {
1141 threadId = pthread_self();
1142 pthread_setspecific(_DPThreadIDKey, ID);
1148 - (pthread_t)getThreadID {
1152 extern unsigned DPIntHash(void *ptr);
1155 return DPIntHash(self);
1158 - (id)copyWithZone:(NSZone *)zone {
1159 return [self retain];
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
1176 - (void)_DPFutureThread:(NSArray *)info {
1177 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1178 DPMessage *msg = [info objectAtIndex:0];
1179 DPConditionVariable *cond = [info objectAtIndex:1];
1181 [cond signalValue:[msg sendTo:self]];
1182 // They count on us to release the condition variable and the message
1188 - (id)future:(DPMessage *)msg {
1189 DPConditionVariable *cond = [[DPConditionVariable alloc] init];
1190 DPFuture *future = [[(DPFuture *)[DPFuture alloc] initWithCondition:cond] autorelease];
1192 [NSThread detachNewThreadSelector:@selector(_DPFutureThread:)
1194 // Note that we must copy the message to ensure it won't go away.
1195 withObject:[NSArray arrayWithObjects:[msg copy], cond, nil]];
1202 #ifndef __UITESTINGKIT__
1203 static void _DPMaintainAutoreleasePool(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
1204 NSAutoreleasePool **pool = info;
1206 *((NSAutoreleasePool **)info) = [[NSAutoreleasePool alloc] init];
1209 static void _DPReleasePool(const void *info) {
1210 NSAutoreleasePool **pool = (void *)info;
1215 void DPSetUpAutoreleasePool(NSString *runLoopMode) {
1216 CFRunLoopRef rl = CFRunLoopGetCurrent();
1217 CFStringRef mode = runLoopMode ? CFRetain((CFStringRef)runLoopMode) : CFRunLoopCopyCurrentMode(rl);
1220 NSAutoreleasePool **p = malloc(sizeof(NSAutoreleasePool **));
1221 CFRunLoopObserverContext context = {
1225 _DPReleasePool, // release
1226 NULL // copy description
1228 *p = [[NSAutoreleasePool alloc] init];
1229 CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
1230 kCFRunLoopEntry, true, 0,
1231 _DPMaintainAutoreleasePool,
1234 CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, mode);
1238 #endif //__UITESTINGKIT__