4 // Perform a long task without blocking the main thread.
6 // Copyright (c) 2004-2005, Charles McGarvey
7 // All rights reserved.
9 // Redistribution and use in source and binary forms, with or without modification, are
10 // permitted provided that the following conditions are met:
12 // 1. Redistributions of source code must retain the above copyright notice, this list
13 // of conditions and the following disclaimer.
15 // 2. Redistributions in binary form must reproduce the above copyright notice, this
16 // list of conditions and the following disclaimer in the documentation and/or other
17 // materials provided with the distribution.
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
22 // SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 // BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 #import <Cocoa/Cocoa.h>
34 @interface ThreadedTask
: NSObject
39 int (*_function
)(id
, unsigned);
41 // keeping track of things...
42 BOOL _isTaskThreadRunning
;
44 // the delegate object
52 // #############################################################################
53 #pragma mark Initialization
54 // #############################################################################
56 /* See accessor methods for a description of the parameters. */
57 - (id
)initWithTarget
:(id
)target selector
:(SEL
)selector delegate
:(id
)delegate
;
58 - (id
)initWithTarget
:(id
)target selector
:(SEL
)selector context
:(id
)context delegate
:(id
)delegate
;
59 - (id
)initWithFunction
:(int (*)(ThreadedTask
*, unsigned))function delegate
:(id
)delegate
;
60 - (id
)initWithFunction
:(int (*)(ThreadedTask
*, unsigned))function context
:(id
)context delegate
:(id
)delegate
;
63 // #############################################################################
64 #pragma mark Accessor Methods
65 // #############################################################################
68 * As a protection, it is not possible to change the iteration method or function,
69 * or the context while the task is running.
73 * The target is the object (or class) that the selector is used on. Target is
76 * The selector which should be used should be in this form:
78 * - (int)task:(ThreadedTask *)task iteration:(unsigned)iteration;
80 * The task parameter is the ThreadedTask object belonging to the task.
81 * The iteration parameter increases by one for each iteration that passes.
83 * The return value is the important part. It's what tells task object
84 * whether to continue or stop or report a failure. Here are the values:
86 * Returning 1 means the task is not finished, and the iteration method
87 * or function will be called again.
88 * Returning 0 means the task is finished, and to end it.
89 * Returning _anything_ else and the task will assume it is an error code;
90 * the task would then abort and report the error code to the delegate.
92 * The target and selector will be set to nil if either are not valid.
96 - (void)setTarget
:(id
)target selector
:(SEL
)selector
;
99 * A function can be used instead of a target and selector. The function
100 * should be in the following form:
102 * int MyTask( ThreadedTask *task, unsigned iteration );
104 * The parameters and return value are the same as for the selector. The
105 * task uses the function if setFunction: was called after setTarget:, or
106 * if setTarget: was never called.
109 - (int (*)(id
, unsigned))function
;
110 - (void)setFunction
:(int (*)(id
, unsigned))function
;
113 * The context of the threaded task can be any object. It is set before
114 * the task is run. The iteration method/function can retrieve this
115 * context from the task and use it to safely store results from the
116 * task. The context can also be nil if the iteration doesn't need it.
119 - (void)setContext
:(id
)context
;
122 * Delegation is how information is received from the task. Setting a
123 * delegate isn't required, but it's pointless not to do so. Unlike
124 * the above accessors, the delegate can be changed while a task is running.
125 * A runloop can also be specified which is used to send the delegate
126 * methods using the given modes. If no runloop is specified, the current
127 * runloop for the thread which runs the threaded task is used. Neither
128 * the delegate or the runloop are retained, but the modes are. If a
129 * runloop is not specified and there is no current runloop, then no delegate
130 * methods will be sent. Pass nil for modes to use the mode of the current
134 - (void)setDelegate
:(id
)delegate
;
135 - (void)setDelegateRunLoop
:(NSRunLoop
*)runloop modes
:(NSArray
*)modes
;
138 * Returns YES if the thread is detached and the task is being performed.
143 // #############################################################################
144 #pragma mark Control Methods
145 // #############################################################################
148 * Begin execution of the task. This method returns immediately.
149 * The delegate will recieve threadedTaskFinished: when the task has completed
150 * its purpose. This method will return YES if the task was successfully
151 * started, and NO if the task could not be run, which usually occurs if the
152 * iteration method/selector or function is not valid.
157 * General information about cancelling: If you release the ThreadedTask object
158 * while a task is running, the task will be cancelled for you automatically
159 * and this is generally safe to do, but the release may block the main thread
160 * for a short amount of time while the task cancels. This can be avoided by
161 * using a cancel method below which doesn't block.
165 * Signal the task to cancel prematurely. This method will block until the
166 * task actually does cancel. It is safe to release the ThreadedTask object
167 * any time after this call without blocking. If the iteration method or
168 * function is blocking for some reason, you should used a different cancel
169 * method which doesn't block, otherwise a deadlock could occur.
174 * Signal the task to cancel prematurely. This method returns immediately, but
175 * you should not release the ThreadedTask object until the delegate receives
176 * a conclusion method.
178 - (void)cancelWithoutWaiting
;
181 * Signal the task to cancel prematurely. This is a convenience method that
182 * sets the delegate to nil and cancels the task without blocking at the same
183 * time. This is useful if the delegate is going to be released while the task
184 * is running. You should not release the ThreadedTask object immediately
185 * after this call, but you will also not receive any notification that it is
186 * safe to do so. You will know when receiver can be released without blocking
187 * when the isRunning method returns NO.
189 - (void)cancelAndRemoveDelegate
;
192 // #############################################################################
193 #pragma mark Iteration Methods
194 // #############################################################################
197 * Report progress of the task back to the main thread. This method should only
198 * be called from the iteration method or function. It takes a single integer
199 * parameter which can be anything the receiver of the progress report will
200 * understand (perhaps 0 thru 100, like as a percentage).
202 - (void)reportProgress
:(int)progress
;
207 @interface
NSObject ( ThreadedTaskDelegate
)
209 // #############################################################################
210 #pragma mark Delegate Methods
211 // #############################################################################
214 * These delegate methods are sent on the thread running the delegate runloop,
215 * or the main runloop if none is specified. It is typically safe to update
216 * the user interface from these methods.
220 * Sent to the delegate upon completion of the task.
222 - (void)threadedTaskFinished
:(ThreadedTask
*)theTask
;
225 * Sent to the delegate when the task has finished cancelling.
227 - (void)threadedTaskCancelled
:(ThreadedTask
*)theTask
;
230 * Sent to the delegate when the iteration returned an error.
232 - (void)threadedTask
:(ThreadedTask
*)theTask failedWithErrorCode
:(int)errorCode
;
235 * Sent to the delegate to report the progress of the task. This is a direct
236 * result of the reportProgress: method being called from the iteration.
238 - (void)threadedTask
:(ThreadedTask
*)theTask reportedProgress
:(int)theProgress
;