DPQueue is now a two-lock concurrent queue.
[hom.git] / Source / DPMultiThreading.h
blob3971137dcde7e3d798f22e4bb30c1734faaa415f
1 //
2 // DPMultiThreading.h
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 #import <Foundation/Foundation.h>
32 #import <CoreFoundation/CoreFoundation.h>
33 #import <AvailabilityMacros.h>
34 #include <pthread.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()
43 #else
44 #import "DPObjCRuntime.h" // For DP_EXTERN
45 #endif
47 #if defined(__HOM_FRAMEWORK__)
48 #import <HigherOrderMessaging/DPMessage.h>
49 #else
50 #import "DPMessage.h"
51 #endif
54 #ifndef __UITESTINGKIT__
55 /*!
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 {
61 @private
62 int32_t _refCount;
63 CFRunLoopSourceRef _src;
66 /*!
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;
78 /*!
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.
83 - (void)fire;
85 /*!
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.
92 - (void)signal;
95 /*!
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.
100 - (void)invalidate;
103 * @abstract Returns whether the receiver is valid and able to fire.
105 - (BOOL)isValid;
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;
134 @end
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;
156 @end
157 #endif //__UITESTINGKIT__
160 // Private
161 typedef struct {
162 id value;
163 void *next;
164 } _DPQueueNode;
167 * @abstract A thread-safe queue.
169 @interface DPQueue : NSObject {
170 // Need our own ref-counting as Apple's implementation itsn't thread satfe
171 int32_t _refCount;
172 _DPQueueNode *head, *tail;
173 OSSpinLock h_lock, t_lock;
174 int32_t count;
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.
186 - (id)dequeue;
189 * @abstract Returns the number of objects in the receiver.
191 - (unsigned)count;
193 @end
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 {
208 DPQueue *queue;
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
215 * is created.
217 + (id)defaultQueue;
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;
257 @end
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
271 * condition is met.
273 * This is a wrapper around pthread's condition variable.
275 @interface DPConditionVariable : NSObject {
276 @private
277 int32_t _refCount;
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.
287 - (void *)wait;
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.
334 - (void)clear;
336 @end
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 {
351 @private
352 int32_t _refCount;
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.
363 + (int)activeCPUs;
366 * @abstract Creates and returns an autoreleased pool with a given
367 * number of threads.
369 + (id)poolWithNumberOfThreads:(unsigned)threadsCount;
372 * @abstract Spawns a new thread and adds it to the pool.
374 - (void)spawnThread;
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
382 * is terminated.
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;
410 @end
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;
437 #endif
440 * @abstract Returns the default invocaiton queue of the receiver, or <code>nil</code>
441 * if none exists.
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.
457 - (void)terminate;
460 * @abstract Sends a given message to an object inside the context of the
461 * receiving thread.
463 * @discussion The object must respond to the passed message or an exception
464 * will be thrown.
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;
472 @end
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 {
481 pthread_t threadId;
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;
494 @end
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
511 * about futures.
513 - (id)future:(DPMessage *)msg;
515 @end
517 @interface NSInvocation (DPMessageSupport)
519 + (id)invocationWithMessage:(DPMessage *)msg receiver:(id)target;
521 @end
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);
533 #endif