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