-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathSSYOperationQueue.h
279 lines (236 loc) · 12.4 KB
/
SSYOperationQueue.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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#import <Cocoa/Cocoa.h>
extern NSString* const constKeySSYOperationQueueDoneTarget ;
extern NSString* const constKeySSYOperationQueueError ;
extern NSString* const constKeySSYOperationGroup ;
extern NSString* const constKeySSYOperationQueueDoneSelectorName ;
@class SSYOperation ;
/*!
@brief Name of a notification, enqueued on the main thread, when the
number of tasks in the receiver's queue increases from 0.
@details The notification object is the receiver.
There is no userInfo dictionary.
This notification is posted with style NSPostNow.
(Neither NSPostASAP nor NSPostWhenIdle were not fast enough,
for example, in BookMacster, because during a Save
operation, for example, the run loop does not enter into
the required wait state to post the notification until the
Save is already complete to the hard disk. If an Agent
had a From Cloud trigger, this would fire its kqueue and the
Worker would find the Agent to be uninhibited, and do its
work, which is undesired. At first, testing of NSPostASAP
seemed like it was working but then we found that if the
Save was preceded by an Import 30 seconds earlier, it was
too late like NSPostWhenIdle.)
*/
extern NSString* const SSYOperationQueueDidBeginWorkNotification ;
/*!
@brief Name of a notification, enqueued on the main thread, when the
number of tasks in the receiver's queue decreases to 0.
@details The notification object is the receiver.
There is no userInfo dictionary.
This notification is posted with style NSPostWhenIdle.
*/
extern NSString* const SSYOperationQueueDidEndWorkNotification ;
@interface SSYOperationQueue : NSOperationQueue {
// Note that we do not enter the doneTarget, doneSelector and
// doneThread as instance variables, because they may be different
// for each group of operations entered with queueGroup::::::::.
// But the following instance variables are common to all groups...
NSError* m_error ;
NSScriptCommand* m_scriptCommand ;
id m_scriptResult ;
NSMutableArray* m_errorRetryInvocations ;
NSSet* m_skipOperationsExceptGroups ;
id m_noAppNapActivity ;
}
/*!
@brief Compares the values, if any, for the keys constKeySSYOperationGroup
between two given dictionaries and returns YES if they both have these keys
and the keys are unequal (-isEqual:); otherwise returns NO.
*/
+ (BOOL)operationGroupsDifferInfo:(NSDictionary*)info
otherInfo:(NSDictionary*)otherInfo ;
/*!
@brief Compares the values, if any, for the keys constKeySSYOperationGroup
between two given dictionaries and returns YES if they both have these keys
and the keys are equal (-isEqual:); otherwise returns NO.
*/
+ (BOOL)operationGroupsSameInfo:(NSDictionary*)info
otherInfo:(NSDictionary*)otherInfo ;
- (NSError*)error ;
/*!
@brief Any error encountered during linked operations.
@details Sending this message with a non-nil argument will cause subsequent
operations, except the doneTarget/doneSelector, to be skipped, and all
operations after that to be cancelled, until the receiver's queue is
emptied. When the queue is emptied, this property is set to nil.
An augmented replica error will be available to the doneTarget/doneSelector as
the value for info dictionary key constKeySSYOperationQueueError. The
augmentation is that the error's userInfo dictionary will include a value
for the key constKeySSYOperationGroup copied from the operation passed to
setError:. You can use this key to avoid displaying the same error
in the doneTarget/doneSelector of subsequent operation groups.
Before the resumeExecutionWithResult: message is sent to the receiver's
scriptError, if any, the scriptError's scriptErrorNumber and scriptErrorString
will be set to, respectively, this error's code and its localizedDescription.
*/
- (void)setError:(NSError*)error
operationGroup:(NSString*)operationGroup ;
/*
@brief Invokes -setError:operationGroup:, passing as operationGroup the
operationGroup of a given operation
@details Just a different way of invoking -setError:operationGroup:.
*/
- (void)setError:(NSError*)error
operation:(SSYOperation*)operation ;
/*!
@brief A script command which, if set, will be sent a
-resumeExecutionWithResult: message after sending the final doneSelector
to the doneTarget, if any, but only if the receiver's queue is
empty; i.e. only if there are no more groups in the queue.
@details This instance variable is designed for "one shot" operation;
after sending -resumeExecutionWithResult: to its scriptCommand,
the receiver sets its scriptCommand to nil.
*/
@property (retain) NSScriptCommand* scriptCommand ;
/*!
@brief A result object which will be sent to the receiver's script
command, if any, as the parameter of the resumeExecutionWithResult:
message.
*/
@property (retain) id scriptResult ;
/*!
@brief A set of group names (strings) whose operations will not be
skipped.
@details If, during the course of operations, you encounter an error
which requires that the some groups be completed but other groups
be skipped, instead of setting an error, set this parameter to the
the groups that should not be skipped. This will cause *other* groups
to be skipped.
This parameter is automatically reset to nil when the receiver's
queue is emptied, so that in the future, no groups will be skipped.
*/
@property (copy) NSSet* skipOperationsExceptGroups ;
/*!
@brief Message which should be sent in the -main function of
SSYOperation to enforce the operation of skipOperationsExceptGroups.
@result NO if the receiver's skipOperationsExceptGroups is not
nil and includes the given group, or if the given group is nil.
YES otherwise.
*/
- (BOOL)shouldSkipOperationsInGroup:(NSString*)group ;
/*!
@brief Executes a group of method selectors as operations in
the queue of the receiver and adds the current invocation of this
method to the receiver's errorRetryInvocations.
@details Each method selector will be manufactured into an
SSYOperation. The recommended idiom is to write a category
of SSYOperation (which is itself a subclass of NSOperation), and
implement the method selectors you need performed in that category.
The methods in the category must take no arguments and return void;
data is passed in the SSYOperation's 'info' dictionary.
The first selector/operation manufactured is made dependent on any
pre-existing operations in the queue, so that none of them will start
until all pre-existing operations have been finished. Because they
are SSYOperations, subsequent operations will be no-op if any
previous operation sets an error. However, the last operation,
specified by the doneTarget and doneSelector, will execute even if there
is an error.
Typically, the doneThread, doneTarget and doneSelector arguments are
used to return final results, and/or return the error object.
Typically, the doneThread is the main thread.
When adding the invocation to the errorRetryInvocations, a
(mutable) *copy* of the parameter 'info' is used, so that changes to the
given 'info' dictionary in the process of execution will not affect
subsequent re-invocations for error recovery.
@param group An arbitrary name you provide for this new group. If
nil, [[NSDate date] description] will be generated and used. Typically,
a recognizable name is useful for debugging. If you're not creative,
consider using NSStringFromSelector(_cmd). If the addon parameter is NO,
and if the name given or generated or generated is not unique, a suffix
of -<decimal number> will be added to make it so.
The group (name) will be set into the receiver's info dictionary,
so you should recover it from there if there is any possibility that
the receiver needed to uniquify it with such a suffix.
@param addon If YES, the given group (name) will not be checked
for uniqueness. Pass YES if you wish to add on more operations to
an existing group which was created by a prior invocation of this method.
@param selectorNames An array of the names of the method selectors
to be executed, in the order in which they are to be executed.
You must implement corresponding methods with each of these names
in a category (class extension) of SSYOperation. This parameter may be
nil, or an empty array, which you may want to do if you need only to queue
the doneTarget/doneSelector operation (because it will run even if there
is an error).
@param info A mutable dictionary which the method selectors may access
to get arguments and return values. If an error occurs during execution of
any of your method selectors, the error registered by your method selector
will be returned as the value of the key constKeySSYOperationQueueError in
this dictionary.
@param block YES if you would like this method to block until all
operations have been completed. NO if you would like it to return
immediately, after queueing the operations. This method simply sends
-waitUntilAllOperationsAreFinished to your <i>queue</i>. Again, if you
pass NO, remember to retain the arguments <i>queue</i> and <i>info</i>.
@param doneThread The thread in which a message will be sent after
the last operation in selectorNames is completed. If doneThread is nil,
the default thread assumed will be the currentThread (the thread on which
performSelectorNames:::::::: is sent). You may want to pass
[NSThread mainThread].
@param doneTarget The target object to which a message will be sent
after the last operation in selectorNames is completed. If doneTarget
is nil, no such message will be sent.
@param doneSelector The selector to which a message will be sent
after the last operation in selectorNames is completed, even if there
has been an error. The doneSelector should take one parameter, to which
will be passed the receiver's 'info'. If doneSelector is nil, no such
message will be sent.
@param keepWithNext If NO, re-invocation of this group will be
deleted from the receiver's errorRetryInvocations after all of this group's
operations have executed. This is normally what you want. If YES, this
deletion will not occur, which means that another another invocation of this
method had better occur sometime in the future, with this parameter NO,
which cause errorRetryInvocations to ultimately be deleted. Otherwise,
an errorRetryInvocation will hang around in the receiver's errorRetryDic,
which is kind of a memory leak in itself, but more importantly may cause a
retain cycle involving objects that the invoker of this method has set into
the 'info'. You will want to pass YES in cases where error retry must
re-invoke a group of groups in order to get the desired result.
*/
- (void)queueGroup:(NSString*)group
addon:(BOOL)addon
selectorNames:(NSArray*)selectorNames
info:(NSMutableDictionary*)info
block:(BOOL)block
doneThread:(NSThread*)doneThread
doneTarget:(id)doneTarget
doneSelector:(SEL)doneSelector
keepWithNext:(BOOL)keepWithNext ;
/*!
@brief Returns an invocation which will re-invoke
any of your previous invocations of queueGroup:::::::::, ordered as they
were originally invoked, and excluding any which have already completed
all of their operations including the isDoneSelector, if any, and excluding
any which were still pending when a prior group failed.
@details This method is useful if you can recover from an error by
making adjustments and repeating the operations. Your isDoneSelector
will typically invoke an error presentation method, and then an
attemptRecoveryFromError::… method. If the user clicks a non-cancel
recovery option, you make the adjustments and, to repeat the operations,
simply invoke the invocations in the array returned by this method.
However, you must grab this array during your isDoneSelector, because after
your isDoneSelector returns, the array you get will be empty, as explained
below. Typically, after grabbing this array you process the contents into a
single invocation of invocations, and add the resulting "errorRetryInvocation"
to the -userInfo of your error object.
After invoking your isDoneSelector, unless you passed keepWithNext:YES,
SSYOperationQueue will forget and release all of its error retry invocations.
*/
- (NSInvocation*)errorRetryInvocation ;
- (void)doDone:(NSDictionary*)doneInfo ;
/*!
@brief Removes an internal observer which observes the receiver itself.
@details To prevent crashes, you should send this prior to the owner of the
receiver's operations being deallocced.
*/
@end