@@ -41,10 +41,13 @@ - (void)retryWork:(MTRAsyncCallbackQueueWorkItem *)workItem;
41
41
@end
42
42
43
43
@interface MTRAsyncCallbackQueueWorkItem ()
44
+ @property (nonatomic , readonly ) os_unfair_lock lock;
44
45
@property (nonatomic , strong , readonly ) dispatch_queue_t queue;
45
46
@property (nonatomic , readwrite ) NSUInteger retryCount;
46
47
@property (nonatomic , strong ) MTRAsyncCallbackWorkQueue * workQueue;
48
+ @property (nonatomic , readonly ) BOOL enqueued;
47
49
// Called by the queue
50
+ - (void )markedEnqueued ;
48
51
- (void )callReadyHandlerWithContext : (id )context ;
49
52
- (void )cancel ;
50
53
@end
@@ -72,6 +75,13 @@ - (NSString *)description
72
75
73
76
- (void )enqueueWorkItem : (MTRAsyncCallbackQueueWorkItem *)item
74
77
{
78
+ if (item.enqueued ) {
79
+ MTR_LOG_ERROR (" MTRAsyncCallbackWorkQueue enqueueWorkItem: item cannot be enqueued twice" );
80
+ return ;
81
+ }
82
+
83
+ [item markedEnqueued ];
84
+
75
85
os_unfair_lock_lock (&_lock);
76
86
item.workQueue = self;
77
87
[self .items addObject: item];
@@ -163,12 +173,14 @@ @implementation MTRAsyncCallbackQueueWorkItem
163
173
- (instancetype )initWithQueue : (dispatch_queue_t )queue
164
174
{
165
175
if (self = [super init ]) {
176
+ _lock = OS_UNFAIR_LOCK_INIT;
166
177
_queue = queue;
167
178
}
168
179
return self;
169
180
}
170
181
171
- - (void )invalidate
182
+ // assume lock is held
183
+ - (void )_invalidate
172
184
{
173
185
// Make sure we don't leak via handlers that close over us, as ours must.
174
186
// This is a bit odd, since these are supposed to be non-nullable
@@ -181,6 +193,38 @@ - (void)invalidate
181
193
_cancelHandler = nil ;
182
194
}
183
195
196
+ - (void )invalidate
197
+ {
198
+ os_unfair_lock_lock (&_lock);
199
+ [self _invalidate ];
200
+ os_unfair_lock_unlock (&_lock);
201
+ }
202
+
203
+ - (void )markedEnqueued
204
+ {
205
+ os_unfair_lock_lock (&_lock);
206
+ _enqueued = YES ;
207
+ os_unfair_lock_unlock (&_lock);
208
+ }
209
+
210
+ - (void )setReadyHandler : (MTRAsyncCallbackReadyHandler)readyHandler
211
+ {
212
+ os_unfair_lock_lock (&_lock);
213
+ if (!_enqueued) {
214
+ _readyHandler = readyHandler;
215
+ }
216
+ os_unfair_lock_unlock (&_lock);
217
+ }
218
+
219
+ - (void )setCancelHandler : (dispatch_block_t )cancelHandler
220
+ {
221
+ os_unfair_lock_lock (&_lock);
222
+ if (!_enqueued) {
223
+ _cancelHandler = cancelHandler;
224
+ }
225
+ os_unfair_lock_unlock (&_lock);
226
+ }
227
+
184
228
- (void )endWork
185
229
{
186
230
[self .workQueue endWork: self ];
@@ -196,24 +240,35 @@ - (void)retryWork
196
240
- (void )callReadyHandlerWithContext : (id )context
197
241
{
198
242
dispatch_async (self.queue , ^{
199
- if (self.readyHandler == nil ) {
243
+ os_unfair_lock_lock (&self->_lock );
244
+ MTRAsyncCallbackReadyHandler readyHandler = self->_readyHandler ;
245
+ NSUInteger retryCount = self->_retryCount ;
246
+ if (readyHandler) {
247
+ self->_retryCount ++;
248
+ }
249
+ os_unfair_lock_unlock (&self->_lock );
250
+
251
+ if (readyHandler == nil ) {
200
252
// Nothing to do here.
201
253
[self endWork ];
202
254
} else {
203
- self.readyHandler (context, self.retryCount );
204
- self.retryCount ++;
255
+ readyHandler (context, retryCount);
205
256
}
206
257
});
207
258
}
208
259
209
260
// Called by the work queue
210
261
- (void )cancel
211
262
{
212
- dispatch_async (self.queue , ^{
213
- if (self.cancelHandler != nil ) {
214
- self.cancelHandler ();
215
- }
216
- [self invalidate ];
217
- });
263
+ os_unfair_lock_lock (&self->_lock );
264
+ dispatch_block_t cancelHandler = self->_cancelHandler ;
265
+ [self _invalidate ];
266
+ os_unfair_lock_unlock (&self->_lock );
267
+
268
+ if (cancelHandler) {
269
+ dispatch_async (self.queue , ^{
270
+ cancelHandler ();
271
+ });
272
+ }
218
273
}
219
274
@end
0 commit comments