Oolite 1.91.0.7644-241112-7f5034b
Loading...
Searching...
No Matches
OOAsyncWorkManager.m
Go to the documentation of this file.
1/*
2
3OOAsyncWorkManager.m
4
5
6Copyright (C) 2009-2013 Jens Ayton
7
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in all
16copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24SOFTWARE.
25
26*/
27
29#import "OOAsyncQueue.h"
30#import "OOCPUInfo.h"
33#import "OONSOperation.h"
34
35#define USE_PTHREAD_ONCE (!OOLITE_WINDOWS)
36
37#if USE_PTHREAD_ONCE
38#include <pthread.h>
39#endif
40
41
43
44
45@interface NSThread (MethodsThatMayExistDependingOnSystem)
46
47- (BOOL) isMainThread;
48+ (BOOL) isMainThread;
49
50@end
51
52
53/* OOAsyncWorkManagerInternal: shared superclass of our two implementations,
54 which implements shared functionality but is not itself concrete.
55*/
57{
58@private
60
63}
64
65- (void) queueResult:(id<OOAsyncWorkTask>)task;
66
67- (void) noteTaskQueued:(id<OOAsyncWorkTask>)task;
68
69@end
70
71
72#if !OO_HAVE_NSOPERATION
73@interface OOManualDispatchAsyncWorkManager: OOAsyncWorkManagerInternal
74{
75@private
76 OOAsyncQueue *_taskQueue;
77}
78
79- (void) queueTask:(NSNumber *)threadNumber;
80
81@end
82#endif
83
84
86{
87@private
89}
90
91#if !OO_HAVE_NSOPERATION
92+ (BOOL) canBeUsed;
93#endif
94
95- (void) dispatchTask:(id<OOAsyncWorkTask>)task;
96
97@end
98
99
100#if !USE_PTHREAD_ONCE
101static NSLock *sInitLock = nil;
102#endif
103
104
105static void InitAsyncWorkManager(void)
106{
107 NSCAssert(sSingleton == nil, @"Async Work Manager singleton not nil in one-time init");
108
109#if !OO_HAVE_NSOPERATION
110 if ([OOOperationQueueAsyncWorkManager canBeUsed])
111 {
113 }
114 if (sSingleton == nil)
115 {
116 sSingleton = [[OOManualDispatchAsyncWorkManager alloc] init];
117 }
118#else
120#endif
121
122 if (sSingleton == nil)
123 {
124 OOLog(@"asyncWorkManager.setUpDispatcher.failed", @"%@", @"***** FATAL ERROR: could not set up async work manager!");
125 exit(EXIT_FAILURE);
126 }
127
128 OOLog(@"asyncWorkManager.dispatchMethod", @"Selected async work manager: %@", [sSingleton class]);
129}
130
131
132@implementation OOAsyncWorkManager
133
134#if !USE_PTHREAD_ONCE
135+ (void) initialize
136{
137 if (sInitLock == nil)
138 {
139 sInitLock = [[NSLock alloc] init];
140 NSAssert(sInitLock != nil, @"Async Work Manager init failed");
141 }
142}
143#endif
144
145
146+ (OOAsyncWorkManager *) sharedAsyncWorkManager
147{
148#if USE_PTHREAD_ONCE
149 static pthread_once_t once = PTHREAD_ONCE_INIT;
150 pthread_once(&once, InitAsyncWorkManager);
151 NSAssert(sSingleton != nil, @"Async Work Manager init failed");
152#else
153 [sInitLock lock];
154 if (sSingleton == nil)
155 {
157 NSAssert(sSingleton != nil, @"Async Work Manager init failed");
158 }
159 [sInitLock unlock];
160#endif
161
162 return sSingleton;
163}
164
165
166+ (id) allocWithZone:(NSZone *)inZone
167{
168 if (sSingleton == nil)
169 {
170 sSingleton = [super allocWithZone:inZone];
171 return sSingleton;
172 }
173 return nil;
174}
175
176
177- (void) dealloc
178{
179 abort();
180 [super dealloc];
181}
182
183
184- (oneway void) release
185{}
186
187
188- (id) retain
189{
190 return self;
191}
192
193
194- (NSUInteger) retainCount
195{
196 return UINT_MAX;
197}
198
199
200- (BOOL) addTask:(id<OOAsyncWorkTask>)task priority:(OOAsyncWorkPriority)priority
201{
203 return NO;
204}
205
206
207- (void) completePendingTasks
208{
210}
211
212
213- (void) waitForTaskToComplete:(id<OOAsyncWorkTask>)task
214{
216 [NSException raise:NSInternalInconsistencyException format:@"%s called.", __PRETTY_FUNCTION__];
217}
218
219@end
220
221
222@implementation OOAsyncWorkManagerInternal
223
224
225- (id) init
226{
227 if ((self = [super init]))
228 {
229 _readyQueue = [[OOAsyncQueue alloc] init];
230
231 if (_readyQueue == nil)
232 {
233 [self release];
234 return nil;
235 }
236
237 _pendingCompletableOperations = [[NSMutableSet alloc] init];
238 _pendingOpsLock = [[NSLock alloc] init];
239
240 if (_pendingCompletableOperations == nil || _pendingOpsLock == nil)
241 {
242 [self release];
243 return nil;
244 }
245 }
246
247 return self;
248}
249
250
251- (void) completePendingTasks
252{
253 id next = nil;
254
255 [_pendingOpsLock lock];
256 for (;;)
257 {
258 next = [_readyQueue tryDequeue];
259 if (next == nil) break;
260
261 [_pendingCompletableOperations removeObject:next];
262 [next completeAsyncTask];
263 }
264 [_pendingOpsLock unlock];
265}
266
267
268- (void) waitForTaskToComplete:(id<OOAsyncWorkTask>)task
269{
270 if (task == nil) return;
271
272#if OO_DEBUG
273 NSParameterAssert([(id)task respondsToSelector:@selector(completeAsyncTask)]);
274 NSAssert1(![NSThread respondsToSelector:@selector(isMainThread)] || [[NSThread self] isMainThread], @"%s can only be called from the main thread.", __PRETTY_FUNCTION__);
275#endif
276
277 [_pendingOpsLock lock];
278 BOOL exists = [_pendingCompletableOperations containsObject:task];
279 if (exists) [_pendingCompletableOperations removeObject:task];
280 [_pendingOpsLock unlock];
281
282 if (!exists) return;
283
284 id next = nil;
285 do
286 {
287 // Dequeue a task and complete it.
288 next = [_readyQueue dequeue];
289 [_pendingOpsLock lock];
290 [_pendingCompletableOperations removeObject:next];
291 [_pendingOpsLock unlock];
292
293 [next completeAsyncTask];
294
295 } while (next != task); // We don't control order, so keep looking until we get the one we care about.
296}
297
298
299- (void) queueResult:(id<OOAsyncWorkTask>)task
300{
301 if ([task respondsToSelector:@selector(completeAsyncTask)])
302 {
303 [_readyQueue enqueue:task];
304 }
305}
306
307
308- (void) noteTaskQueued:(id<OOAsyncWorkTask>)task
309{
310 [_pendingOpsLock lock];
311 [_pendingCompletableOperations addObject:task];
312 [_pendingOpsLock unlock];
313}
314
315@end
316
317
318
319/******* OOManualDispatchAsyncWorkManager - manual thread management *******/
320
321enum
322{
325
326
327#if !OO_HAVE_NSOPERATION
328@implementation OOManualDispatchAsyncWorkManager
329
330- (id) init
331{
332 if ((self = [super init]))
333 {
334 // Set up work queue.
335 _taskQueue = [[OOAsyncQueue alloc] init];
336 if (_taskQueue == nil)
337 {
338 [self release];
339 return nil;
340 }
341
342 // Set up loading threads.
343 NSUInteger threadCount, threadNumber = 1;
344#if OO_DEBUG
345 threadCount = kMaxWorkThreads;
346#else
347 threadCount = MIN(OOCPUCount(), (unsigned)kMaxWorkThreads);
348#endif
349 do
350 {
351 [NSThread detachNewThreadSelector:@selector(queueTask:) toTarget:self withObject:[NSNumber numberWithInt:threadNumber++]];
352 } while (--threadCount > 0);
353 }
354
355 return self;
356}
357
358
359- (BOOL) addTask:(id<OOAsyncWorkTask>)task priority:(OOAsyncWorkPriority)priority
360{
361 if (EXPECT_NOT(task == nil)) return NO;
362
363 [super noteTaskQueued:task];
364
365 // Priority is ignored.
366 return [_taskQueue enqueue:task];
367}
368
369
370- (void) queueTask:(NSNumber *)threadNumber
371{
372 NSAutoreleasePool *rootPool = nil, *pool = nil;
373
374 rootPool = [[NSAutoreleasePool alloc] init];
375
376 [NSThread setThreadPriority:0.5];
377 [NSThread ooSetCurrentThreadName:[NSString stringWithFormat:@"OOAsyncWorkManager thread %@", threadNumber]];
378
379 for (;;)
380 {
381 pool = [[NSAutoreleasePool alloc] init];
382
383 id<OOAsyncWorkTask> task = [_taskQueue dequeue];
384 @try
385 {
386 [task performAsyncTask];
387 }
388 @catch (id exception) {}
389 [self queueResult:task];
390
391 [pool release];
392 }
393
394 [rootPool release];
395}
396
397@end
398#endif
399
400
401/******* OOOperationQueueAsyncWorkManager - dispatch through NSOperationQueue if available *******/
402
403
405
406#if !OO_HAVE_NSOPERATION
407+ (BOOL) canBeUsed
408{
409 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"disable-operation-queue-work-manager"]) return NO;
410 return [OONSInvocationOperationClass() class] != Nil;
411}
412#endif
413
414
415- (id) init
416{
417 if ((self = [super init]))
418 {
419 _operationQueue = [[OONSOperationQueueClass() alloc] init];
420
421 if (_operationQueue == nil)
422 {
423 [self release];
424 return nil;
425 }
426 }
427
428 return self;
429}
430
431
432- (void) dealloc
433{
434 [_operationQueue release];
435
436 [super dealloc];
437}
438
439
440- (BOOL) addTask:(id<OOAsyncWorkTask>)task priority:(OOAsyncWorkPriority)priority
441{
442 if (EXPECT_NOT(task == nil)) return NO;
443
444 id operation = [[OONSInvocationOperationClass() alloc] initWithTarget:self selector:@selector(dispatchTask:) object:task];
445 if (operation == nil) return NO;
446
447 if (priority == kOOAsyncPriorityLow) [operation setQueuePriority:OONSOperationQueuePriorityLow];
448 else if (priority == kOOAsyncPriorityHigh) [operation setQueuePriority:OONSOperationQueuePriorityHigh];
449
450 [_operationQueue addOperation:operation];
451 [operation release];
452
453 [super noteTaskQueued:task];
454 return YES;
455}
456
457
458- (void) dispatchTask:(id<OOAsyncWorkTask>)task
459{
460 @try
461 {
462 [task performAsyncTask];
463 }
464 @catch (id exception) {}
465 [self queueResult:task];
466}
467
468@end
OOAsyncWorkPriority
@ kOOAsyncPriorityLow
@ kOOAsyncPriorityHigh
@ kMaxWorkThreads
static OOAsyncWorkManager * sSingleton
static void InitAsyncWorkManager(void)
NSUInteger OOCPUCount(void)
Definition OOCPUInfo.m:101
static OODebugMonitor * sSingleton
#define EXPECT_NOT(x)
#define OOLogGenericSubclassResponsibility()
Definition OOLogging.h:129
#define OOLog(class, format,...)
Definition OOLogging.h:88
#define MIN(A, B)
Definition OOMaths.h:111
#define OONSOperationQueue
return nil
NSMutableSet * _pendingCompletableOperations