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 #import <Foundation/Foundation.h>
32 #import <CoreFoundation/CoreFoundation.h>
33 #import <AvailabilityMacros.h>
35 #include <libkern/OSAtomic.h> // For OSSpinLock
37 #if defined(__HOM_FRAMEWORK__)
38 #import <HigherOrderMessaging/DPObjCRuntime.h>
39 #elif defined(__UITESTINGKIT__)
40 #import <UITestingKit/DPObjCRuntime.h>
41 #import <UITestingKit/DPRunLoopSource.h>
42 #import <UITestingKit/DPUtilities.h> // For DPSetUpAutoreleasePool()
44 #import "DPObjCRuntime.h" // For DP_EXTERN
47 #if defined(__HOM_FRAMEWORK__)
48 #import <HigherOrderMessaging/DPMessage.h>
54 #ifndef __UITESTINGKIT__
56 * @abstract A base class for runloop sources.
57 * @discussion Subclass this class in order to create our own runloop source.
59 @interface DPRunLoopSource
: NSObject
{
62 CFRunLoopSourceRef _src
;
66 * @abstract Initializes a new runloop source with a given priority.
68 * @param priority A priority index indicating the order in which run loop sources
69 * are processed. When multiple run loop sources are firing in a single pass through
70 * the run loop, the sources are processed in increasing order of this parameter.
71 * If the run loop is set to process only one source per loop, only the highest
72 * priority source, the one with the lowest order value, is processed.
73 * Pass 0 unless there is a reason to do otherwise.
75 - (id
)initWithPriority
:(unsigned)priority
;
78 * @abstract Called by the runloop in order for the source to do its work.
79 * @discussion Override this method in your subclass to perform the actual work.
80 * The default implementation of this method does nothing.
85 * @abstract Signals the receiver, marking it as ready to fire.
86 * @discussion This method marks the receiver as ready to fire.
87 * The receiver's -fire will then be invoked by one of the runloops the receiver
88 * is added to, whichever happens to be available first.
89 * After firing, the receiver must be signaled again in order to fire again.
95 * @abstract Invalidates the receiver, stopping it from ever firing again.
96 * @discussion Once invalidated, the receiver will never fire again. This method
97 * automatically removes the receiver from all run loop modes in which it was registered.
102 * @abstract Returns whether the receiver is valid and able to fire.
107 * @abstract Returns the ordering parameter for the receiver, which the run loop uses to
108 * determine the order in which sources are processed when multiple sources are firing.
110 - (unsigned)priority
;
114 * @abstract A scheduling callback for the receiver.
115 * @discussion This method is invoked when the receiver is added to a run loop mode.
116 * The default implementation of this method does nothing.
118 - (void)scheduleWithRunLoop
:(CFRunLoopRef
)rl forMode
:(NSString
*)mode
;
121 * @abstract A cancel callback for the receiver.
122 * @discussion This method is invoked when the receiver is removed from a run loop mode.
123 * The default implementation of this method does nothing.
125 - (void)cancelMode
:(NSString
*)mode forRunLoop
:(CFRunLoopRef
)rl
;
129 * @abstract Returns the underlying CFRunLoopSourceRef powering the receiver.
131 - (CFRunLoopSourceRef
)getCFRunLoopSource
;
136 * @abstract Support for DPRunLoopSource.
138 @interface
NSRunLoop (DPRunLoopSource
)
141 * @abstract Registers <code>src</code> with the receiver for <code>mode</code>.
143 - (void)addSource
:(DPRunLoopSource
*)src forMode
:(NSString
*)mode
;
146 * @abstract Unregisters <code>src</code> with the receiver for <code>mode</code>.
148 - (void)removeSource
:(DPRunLoopSource
*)src forMode
:(NSString
*)mode
;
151 * @abstract Returns whether the receiver contains a given source.
153 - (BOOL
)containsSource
:(DPRunLoopSource
*)src inMode
:(NSString
*)mode
;
156 #endif //__UITESTINGKIT__
159 @interface DPQueue
: NSObject
{
161 NSMutableArray
*queue
;
162 OSSpinLock queueLock
;
165 - (void)enqueu
:(id
)obj
;
173 * @abstract A queue of invocations that can be shared across
174 * runloops to distribute work.
176 * @discussion An invocation queue is a runloop source, and therefor
177 * can be shared across runloops/threads. Each runloop takes the topmost
178 * invocation and invokes it, until the queue is empty.
180 * DPInvocation queue implements thread safe reference-counting so there's
181 * no need to lock when retaining/releasing it.
183 @interface DPInvocationQueue
: DPRunLoopSource
{
185 int32_t _theApocalypseArrived
;
189 * @abstract Returns the default queue in the current thread.
190 * @discussion If there's no queue in the current thread one
196 * @abstract Appends an invocation to the queue for later execution.
198 * @discussion If the invocation returns an object (either instance or class)
199 * a future is returned. A future is a proxy to the result of the invocation
200 * that does not yet exist.
202 * Upon the first message to the future it'll block the caller thread until
203 * the result of the invocation is available. When the result is available,
204 * the future will act as a simple proxy and just forward any message to its
205 * target. No thread safety is implemented and the target of the proxy (the
206 * result of the invocation) is assumed to be owned by the caller thread.
208 * For non-object return values nil is returned. If you have no use for the
209 * proxy just ignore it, but if you do, you must retain it to keep it around.
211 * If you wish to get the real object behind the future, send it a
212 * <code>self</code> message and use the result.
214 * Warning: Futures are not thread safe. They assume to be owned by excactly
215 * one thread and to never be messaged from another thread. That being said,
216 * after the first message to the future returns (meaning the future's target
217 * is now available), the future can safely be shared across threads, assuming
218 * its target is thread safe of course. Futures implement thread-safe reference
219 * counting to allow sharing after a target is available.
221 - (id
)appendInvocation
:(NSInvocation
*)invocation
;
224 * @abstract Removes an invocation from the queue.
226 * @discussion This method removes the topmost invocation that matches
227 * <code>invocation</code> and returns. If the invocation appears more
228 * than once in the queue, you should invoke this method as many times
229 * as the invocation appears.
231 //- (void)removeInvocation:(NSInvocation *)invocation;
234 * @abstract Invokes all invocations of the receiver empties it.
236 * @discussion This method returns only after all invocations have
237 * been invoked and the receiver is empty.
238 * The queue is locked until all invocations are invoked, preventing
239 * additions of invocations from other threads.
241 - (void)pushAllInvocations
;
246 * @abstract Returns whether a given object is a future or not.
247 * @discussion Once a future gets its value, it is no longer
248 * considered a future and this function returns <code>NO</code>.
250 DP_EXTERN BOOL
DPObjectIsFuture(id obj
);
254 * @abstract A condition variable that's shared across threads.
255 * @discussion A condition variable is a variable that's shared
256 * across threads and it's value is only available when a certain
259 * This is a wrapper around pthread's condition variable.
261 @interface DPConditionVariable
: NSObject
{
264 pthread_mutex_t _lock
;
265 pthread_cond_t _condition
;
266 volatile void *_value
;
267 volatile int _conditionValue
;
271 * @abstract Waits for the default condition (1) and returns the value.
276 * @abstract Waits until a certain condition is met, and returns the value.
278 * @param cond The condition to wait for. -1 is a reserved condition. Never use it yourself.
279 * @result The value that was signaled/broadcasted.
281 - (void *)waitForCondition
:(int)cond
;
284 * @abstract Signals the default condition (1) with a given value.
285 * @discussion If there are more than one thread waiting for this value
286 * only one is unblocked.
288 - (void)signalValue
:(void *)val
;
291 * @abstract Signals a custom condition with a given value.
292 * @discussion If there are more than one thread waiting for this value
293 * only one is unblocked.
295 * @param cond The condition to signal. -1 is a reserved condition. Never use it yourself.
296 * @param val The value to send with the signal.
298 - (void)signalCondition
:(int)cond withValue
:(void *)val
;
301 * @abstract Broadcasts the default condition (1) with a given value.
302 * @discussion A broadcast unblocks all threads waiting for the condition.
304 - (void)broadcastValue
:(void *)cond
;
307 * @abstract Broadcasts a custom condition with a given value.
308 * @discussion A broadcast unblocks all threads waiting for the condition.
310 * @param cond The condition to signal. -1 is a reserved condition. Never use it yourself.
311 * @param val The value to send with the signal.
313 - (void)broadcastCondition
:(int)cond withValue
:(void *)val
;
316 * @abstract Resets the receiver to its initial condition and value.
317 * @discussion If you'd like to reuse a condition variable, send it a
318 * <code>clear</code> message before waiting for a new condition.
324 @
class DPCoroutineStack
, DPCoroutineScheduler
, DPCoroutine
;
327 * @abstract A class representing a pool of threads that share work.
329 * @discussion A thread pool may have any number of threads and any
330 * number of messages to send. The threads in the pool send the messages
331 * whenever they can, and split the workload among themselves.
333 @interface DPThreadPool
: NSObject
{
336 DPInvocationQueue
*queue
;
337 DPCoroutineStack
*coroStack
;
338 NSMutableArray
*threads
;
342 * @abstract Returns the number of processors currently available for executing threads.
343 * @discussion This number can change when power management modes are changed.
344 * @result The number of active CPUs or -1 on error.
349 * @abstract Creates and returns an autoreleased pool with a given
352 + (id
)poolWithNumberOfThreads
:(unsigned)threadsCount
;
355 * @abstract Spawns a new thread and adds it to the pool.
360 * @abstract Terminates a thread from the pool.
362 * @discussion If there are sleeping threads one of them is terminated.
363 * Otherwise, the first thread to become idle is terminated. Note that
364 * any prioir messages added to the pool will be sent before the thread
367 - (void)terminateThread
;
370 * @abstract Returns the total amount of threads in the pool.
372 - (unsigned)numberOfThreads
;
375 * @abstract Returns the number of active threads in the pool.
376 * @discussion An active thread is a thread that processes a messages.
378 - (unsigned)activeThreads
;
381 * @abstract Adds a message to the pool's work queue.
382 * @discussion The message is invoked whenever one of the threads in
383 * the pool finds the time to do it.
385 - (id
)sendMessage
:(DPMessage
*)msg to
:(id
)target
;
387 - (void)sendCoroutineMessage
:(DPMessage
*)message to
:(id
)target
;
393 * @abstract Adds an API for easy usage of worker threads.
395 @interface
NSThread (DPWorkerThread
)
398 * @abstract Detaches and returns a new worker thread.
400 * @discussion A worker thread is a thread that has a default
401 * invocation queue set up together with an active runloop.
403 * This method does all the dirty work for you (including setting up an
404 * autorelease pool), but you can set up a worker thread yourself buy calling
405 * <code>[[NSRunLoop defaultRunLoop] addSource:[DPInvocationQueue defaultQueue]
406 forMode:NSDefaultRunLoopMode];</code>
407 * and running the runloop ins the default mode.
409 + (id
)detachNewWorkerThread
;
411 #if !defined(MAC_OS_X_VERSION_10_5) || MAC_OS_X_VERSION_10_5 > MAC_OS_X_VERSION_MAX_ALLOWED
413 * @abstract Returns the main thread.
415 + (NSThread
*)mainThread
;
419 * @abstract Returns the default invocaiton queue of the receiver, or <code>nil</code>
422 - (DPInvocationQueue
*)defaultInvocationQueue
;
425 * @abstract Sets the default invocation queue of the receiver.
427 - (void)setDefaultInvocationQueue
:(DPInvocationQueue
*)queue
;
430 * @abstract Terminates a worker thread that was created using
431 * +[NSThread detachNewWorkerThread].
433 * @discussion If the thread wasn't created with +[NSThread detachNewWorkerThread],
434 * this method does nothing.
439 * @abstract Sends a given message to an object inside the context of the
442 * @discussion The object must respond to the passed message or an exception
445 * @result A future if the message returns an object, <code>nil</code> otherwise.
446 * See the documentation for -[DPInvocationQueue appendInvocation:] for more
447 * information about futures.
449 - (id
)sendMessage
:(DPMessage
*)msg to
:(id
)obj
;
453 @interface DPThreadID
: NSObject
{
456 + (id
)currentIdentifier
;
457 - (pthread_t
)getThreadID
;
460 @interface
NSObject (DPAsyncMessaging
)
463 * @abstract Makes the receiver receive <code>msg</code> after the given delay.
465 - (void)receiveMessage
:(DPMessage
*)msg afterDelay
:(NSTimeInterval
)sec
;
468 * @abstract Spwns a new thread in which the passed message will be received.
470 * @discussion If the message doesn't return an object the behavior is undefined,
471 * and will most likely cause a crash.
473 * @result A future to the message's result. Please refer to the
474 * -[DPInvocationQueue appendInvocation:] documentation for more information
477 - (id
)future
:(DPMessage
*)msg
;
481 @interface
NSInvocation (DPMessageSupport
)
483 + (id
)invocationWithMessage
:(DPMessage
*)msg receiver
:(id
)target
;
487 #ifndef __UITESTINGKIT__
489 * @abstract Sets up an autorelease pool that's automatically released
490 * and recreated at each entry of the current runloop.
491 * @discussion The autorelease pool is added to the current runloop in
492 * <code>runLoopMode</code>. If <code>runLoopMode</code> is <code>nil</code>,
493 * the pool is added to the current mode.
494 * Each call to this function creates and sets up a new autorelease pool.
496 DP_EXTERN
void DPSetUpAutoreleasePool(NSString
*runLoopMode
);