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.
58 * This class is thread-safe.
60 @interface DPRunLoopSource
: NSObject
{
63 CFRunLoopSourceRef _src
;
67 * @abstract Initializes a new runloop source with a given priority.
69 * @param priority A priority index indicating the order in which run loop sources
70 * are processed. When multiple run loop sources are firing in a single pass through
71 * the run loop, the sources are processed in increasing order of this parameter.
72 * If the run loop is set to process only one source per loop, only the highest
73 * priority source, the one with the lowest order value, is processed.
74 * Pass 0 unless there is a reason to do otherwise.
76 - (id
)initWithPriority
:(unsigned)priority
;
79 * @abstract Called by the runloop in order for the source to do its work.
80 * @discussion Override this method in your subclass to perform the actual work.
81 * The default implementation of this method does nothing.
86 * @abstract Signals the receiver, marking it as ready to fire.
87 * @discussion This method marks the receiver as ready to fire.
88 * The receiver's -fire will then be invoked by one of the runloops the receiver
89 * is added to, whichever happens to be available first.
90 * After firing, the receiver must be signaled again in order to fire again.
96 * @abstract Invalidates the receiver, stopping it from ever firing again.
97 * @discussion Once invalidated, the receiver will never fire again. This method
98 * automatically removes the receiver from all run loop modes in which it was registered.
103 * @abstract Returns whether the receiver is valid and able to fire.
108 * @abstract Returns the ordering parameter for the receiver, which the run loop uses to
109 * determine the order in which sources are processed when multiple sources are firing.
111 - (unsigned)priority
;
115 * @abstract A scheduling callback for the receiver.
116 * @discussion This method is invoked when the receiver is added to a run loop mode.
117 * The default implementation of this method does nothing.
119 - (void)scheduleWithRunLoop
:(CFRunLoopRef
)rl forMode
:(NSString
*)mode
;
122 * @abstract A cancel callback for the receiver.
123 * @discussion This method is invoked when the receiver is removed from a run loop mode.
124 * The default implementation of this method does nothing.
126 - (void)cancelMode
:(NSString
*)mode forRunLoop
:(CFRunLoopRef
)rl
;
130 * @abstract Returns the underlying CFRunLoopSourceRef powering the receiver.
132 - (CFRunLoopSourceRef
)getCFRunLoopSource
;
137 * @abstract Support for DPRunLoopSource.
139 @interface
NSRunLoop (DPRunLoopSource
)
142 * @abstract Registers <code>src</code> with the receiver for <code>mode</code>.
144 - (void)addSource
:(DPRunLoopSource
*)src forMode
:(NSString
*)mode
;
147 * @abstract Unregisters <code>src</code> with the receiver for <code>mode</code>.
149 - (void)removeSource
:(DPRunLoopSource
*)src forMode
:(NSString
*)mode
;
152 * @abstract Returns whether the receiver contains a given source.
154 - (BOOL
)containsSource
:(DPRunLoopSource
*)src inMode
:(NSString
*)mode
;
157 #endif //__UITESTINGKIT__
167 * @abstract A thread-safe queue.
169 @interface DPQueue
: NSObject
{
170 // Need our own ref-counting as Apple's implementation itsn't thread satfe
172 _DPQueueNode
*head
, *tail
;
173 OSSpinLock h_lock
, t_lock
;
178 * @abstract Adds <code>obj</code> to the queue.
180 - (void)enqueue
:(id
)obj
;
183 * @abstract Pops the topmost object in the queue and returns it.
184 * @discussion Returns <code>nil</code> if the receiver is empty.
189 * @abstract Returns the number of objects in the receiver.
197 * @abstract A queue of invocations that can be shared across
198 * runloops to distribute work.
200 * @discussion An invocation queue is a runloop source, and therefor
201 * can be shared across runloops/threads. Each runloop takes the topmost
202 * invocation and invokes it, until the queue is empty.
204 * DPInvocation queue implements thread safe reference-counting so there's
205 * no need to lock when retaining/releasing it.
207 @interface DPInvocationQueue
: DPRunLoopSource
{
209 int32_t _theApocalypseArrived
;
213 * @abstract Returns the default queue in the current thread.
214 * @discussion If there's no queue in the current thread one
220 * @abstract Appends an invocation to the queue for later execution.
222 * @discussion If the invocation returns an object (either instance or class)
223 * a future is returned. A future is a proxy to the result of the invocation
224 * that does not yet exist.
226 * Upon the first message to the future it'll block the caller thread until
227 * the result of the invocation is available. When the result is available,
228 * the future will act as a simple proxy and just forward any message to its
229 * target. No thread safety is implemented and the target of the proxy (the
230 * result of the invocation) is assumed to be owned by the caller thread.
232 * For non-object return values nil is returned. If you have no use for the
233 * proxy just ignore it, but if you do, you must retain it to keep it around.
235 * If you wish to get the real object behind the future, send it a
236 * <code>self</code> message and use the result.
238 * Warning: Futures are not thread safe. They assume to be owned by excactly
239 * one thread and to never be messaged from another thread. That being said,
240 * after the first message to the future returns (meaning the future's target
241 * is now available), the future can safely be shared across threads, assuming
242 * its target is thread safe of course. Futures implement thread-safe reference
243 * counting to allow sharing after a target is available.
245 - (id
)appendInvocation
:(NSInvocation
*)invocation
;
248 * @abstract Invokes all invocations of the receiver empties it.
250 * @discussion This method returns only after all invocations have
251 * been invoked and the receiver is empty.
252 * The queue is locked until all invocations are invoked, preventing
253 * additions of invocations from other threads.
255 - (void)pushAllInvocations
;
260 * @abstract Returns whether a given object is a future or not.
261 * @discussion Once a future gets its value, it is no longer
262 * considered a future and this function returns <code>NO</code>.
264 DP_EXTERN BOOL
DPObjectIsFuture(id obj
);
268 * @abstract A condition variable that's shared across threads.
269 * @discussion A condition variable is a variable that's shared
270 * across threads and it's value is only available when a certain
273 * This is a wrapper around pthread's condition variable.
275 @interface DPConditionVariable
: NSObject
{
278 pthread_mutex_t _lock
;
279 pthread_cond_t _condition
;
280 volatile void *_value
;
281 volatile int _conditionValue
;
285 * @abstract Waits for the default condition (1) and returns the value.
290 * @abstract Waits until a certain condition is met, and returns the value.
292 * @param cond The condition to wait for. -1 is a reserved condition. Never use it yourself.
293 * @result The value that was signaled/broadcasted.
295 - (void *)waitForCondition
:(int)cond
;
298 * @abstract Signals the default condition (1) with a given value.
299 * @discussion If there are more than one thread waiting for this value
300 * only one is unblocked.
302 - (void)signalValue
:(void *)val
;
305 * @abstract Signals a custom condition with a given value.
306 * @discussion If there are more than one thread waiting for this value
307 * only one is unblocked.
309 * @param cond The condition to signal. -1 is a reserved condition. Never use it yourself.
310 * @param val The value to send with the signal.
312 - (void)signalCondition
:(int)cond withValue
:(void *)val
;
315 * @abstract Broadcasts the default condition (1) with a given value.
316 * @discussion A broadcast unblocks all threads waiting for the condition.
318 - (void)broadcastValue
:(void *)cond
;
321 * @abstract Broadcasts a custom condition with a given value.
322 * @discussion A broadcast unblocks all threads waiting for the condition.
324 * @param cond The condition to signal. -1 is a reserved condition. Never use it yourself.
325 * @param val The value to send with the signal.
327 - (void)broadcastCondition
:(int)cond withValue
:(void *)val
;
330 * @abstract Resets the receiver to its initial condition and value.
331 * @discussion If you'd like to reuse a condition variable, send it a
332 * <code>clear</code> message before waiting for a new condition.
338 @
class DPCoroutineStack
, DPCoroutineScheduler
, DPCoroutine
;
341 * @abstract A class representing a pool of threads that share work.
343 * @discussion A thread pool may have any number of threads and any
344 * number of messages to send. The threads in the pool send the messages
345 * whenever they can, and split the workload among themselves.
347 * Each thread pool has one shared invocation queue and one shared coroutine
348 * stack. Both split the work among all active threads.
350 @interface DPThreadPool
: NSObject
{
353 DPInvocationQueue
*queue
;
354 DPCoroutineStack
*coroStack
;
355 NSMutableArray
*threads
;
359 * @abstract Returns the number of processors currently available for executing threads.
360 * @discussion This number can change when power management modes are changed.
361 * @result The number of active CPUs or -1 on error.
366 * @abstract Creates and returns an autoreleased pool with a given
369 + (id
)poolWithNumberOfThreads
:(unsigned)threadsCount
;
372 * @abstract Spawns a new thread and adds it to the pool.
377 * @abstract Terminates a thread from the pool.
379 * @discussion If there are sleeping threads one of them is terminated.
380 * Otherwise, the first thread to become idle is terminated. Note that
381 * any prioir messages added to the pool will be sent before the thread
384 - (void)terminateThread
;
387 * @abstract Returns the total amount of threads in the pool.
389 - (unsigned)numberOfThreads
;
392 * @abstract Returns the number of active threads in the pool.
393 * @discussion An active thread is a thread that processes a messages.
395 - (unsigned)activeThreads
;
398 * @abstract Adds a message to the pool's work queue.
399 * @discussion The message is invoked whenever one of the threads in
400 * the pool finds the time to do it.
402 - (id
)sendMessage
:(DPMessage
*)msg to
:(id
)target
;
405 * @abstract Creates a coroutine with the given message and target and adds
406 * it to the pool's coroutine stack.
408 - (void)sendCoroutineMessage
:(DPMessage
*)message to
:(id
)target
;
414 * @abstract Adds an API for easy usage of worker threads.
416 @interface
NSThread (DPWorkerThread
)
419 * @abstract Detaches and returns a new worker thread.
421 * @discussion A worker thread is a thread that has a default
422 * invocation queue set up together with an active runloop.
424 * This method does all the dirty work for you (including setting up an
425 * autorelease pool), but you can set up a worker thread yourself buy calling
426 * <code>[[NSRunLoop defaultRunLoop] addSource:[DPInvocationQueue defaultQueue]
427 forMode:NSDefaultRunLoopMode];</code>
428 * and running the runloop ins the default mode.
430 + (id
)detachNewWorkerThread
;
432 #if !defined(MAC_OS_X_VERSION_10_5) || MAC_OS_X_VERSION_10_5 > MAC_OS_X_VERSION_MAX_ALLOWED
434 * @abstract Returns the main thread.
436 + (NSThread
*)mainThread
;
440 * @abstract Returns the default invocaiton queue of the receiver, or <code>nil</code>
443 - (DPInvocationQueue
*)defaultInvocationQueue
;
446 * @abstract Sets the default invocation queue of the receiver.
448 - (void)setDefaultInvocationQueue
:(DPInvocationQueue
*)queue
;
451 * @abstract Terminates a worker thread that was created using
452 * +[NSThread detachNewWorkerThread].
454 * @discussion If the thread wasn't created with +[NSThread detachNewWorkerThread],
455 * this method does nothing.
460 * @abstract Sends a given message to an object inside the context of the
463 * @discussion The object must respond to the passed message or an exception
466 * @result A future if the message returns an object, <code>nil</code> otherwise.
467 * See the documentation for -[DPInvocationQueue appendInvocation:] for more
468 * information about futures.
470 - (id
)sendMessage
:(DPMessage
*)msg to
:(id
)obj
;
476 * @abstract A unique thread ID.
477 * @discussion Each thread has exactly one unique DPThreadID instance.
478 * This instance can be used as a key in a dictionary and so on.
480 @interface DPThreadID
: NSObject
{
485 * @abstract Returns the identifier of the current thread.
487 + (id
)currentIdentifier
;
490 * @abstract Returns the pthread ID of the receiver.
492 - (pthread_t
)getThreadID
;
496 @interface
NSObject (DPAsyncMessaging
)
499 * @abstract Makes the receiver receive <code>msg</code> after the given delay.
501 - (void)receiveMessage
:(DPMessage
*)msg afterDelay
:(NSTimeInterval
)sec
;
504 * @abstract Spwns a new thread in which the passed message will be received.
506 * @discussion If the message doesn't return an object the behavior is undefined,
507 * and will most likely cause a crash.
509 * @result A future to the message's result. Please refer to the
510 * -[DPInvocationQueue appendInvocation:] documentation for more information
513 - (id
)future
:(DPMessage
*)msg
;
517 @interface
NSInvocation (DPMessageSupport
)
519 + (id
)invocationWithMessage
:(DPMessage
*)msg receiver
:(id
)target
;
523 #ifndef __UITESTINGKIT__
525 * @abstract Sets up an autorelease pool that's automatically released
526 * and recreated at each entry of the current runloop.
527 * @discussion The autorelease pool is added to the current runloop in
528 * <code>runLoopMode</code>. If <code>runLoopMode</code> is <code>nil</code>,
529 * the pool is added to the current mode.
530 * Each call to this function creates and sets up a new autorelease pool.
532 DP_EXTERN
void DPSetUpAutoreleasePool(NSString
*runLoopMode
);