LCOV - code coverage report
Current view: top level - Core - OOAsyncWorkManager.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 27 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOAsyncWorkManager.m
       4             : 
       5             : 
       6             : Copyright (C) 2009-2013 Jens Ayton
       7             : 
       8             : Permission is hereby granted, free of charge, to any person obtaining a copy
       9             : of this software and associated documentation files (the "Software"), to deal
      10             : in the Software without restriction, including without limitation the rights
      11             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      12             : copies of the Software, and to permit persons to whom the Software is
      13             : furnished to do so, subject to the following conditions:
      14             : 
      15             : The above copyright notice and this permission notice shall be included in all
      16             : copies or substantial portions of the Software.
      17             : 
      18             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      23             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      24             : SOFTWARE.
      25             : 
      26             : */
      27             : 
      28             : #import "OOAsyncWorkManager.h"
      29             : #import "OOAsyncQueue.h"
      30             : #import "OOCPUInfo.h"
      31             : #import "OOCollectionExtractors.h"
      32             : #import "NSThreadOOExtensions.h"
      33             : #import "OONSOperation.h"
      34             : 
      35           0 : #define USE_PTHREAD_ONCE (!OOLITE_WINDOWS)
      36             : 
      37             : #if USE_PTHREAD_ONCE
      38             : #include <pthread.h>
      39             : #endif
      40             : 
      41             : 
      42           0 : static OOAsyncWorkManager *sSingleton = nil;
      43             : 
      44             : 
      45             : @interface NSThread (MethodsThatMayExistDependingOnSystem)
      46             : 
      47           0 : - (BOOL) isMainThread;
      48           0 : + (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             : */
      56           0 : @interface OOAsyncWorkManagerInternal: OOAsyncWorkManager
      57             : {
      58             : @private
      59           0 :         OOAsyncQueue                    *_readyQueue;
      60             :         
      61           0 :         NSMutableSet                    *_pendingCompletableOperations;
      62           0 :         NSLock                                  *_pendingOpsLock;
      63             : }
      64             : 
      65           0 : - (void) queueResult:(id<OOAsyncWorkTask>)task;
      66             : 
      67           0 : - (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             : 
      85           0 : @interface OOOperationQueueAsyncWorkManager: OOAsyncWorkManagerInternal
      86             : {
      87             : @private
      88           0 :         OONSOperationQueue              _operationQueue;
      89             : }
      90             : 
      91             : #if !OO_HAVE_NSOPERATION
      92             : + (BOOL) canBeUsed;
      93             : #endif
      94             : 
      95           0 : - (void) dispatchTask:(id<OOAsyncWorkTask>)task;
      96             : 
      97             : @end
      98             : 
      99             : 
     100             : #if !USE_PTHREAD_ONCE
     101             : static NSLock *sInitLock = nil;
     102             : #endif
     103             : 
     104             : 
     105           0 : static 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             :         {
     112             :                 sSingleton = [[OOOperationQueueAsyncWorkManager alloc] init];
     113             :         }
     114             :         if (sSingleton == nil)
     115             :         {
     116             :                 sSingleton = [[OOManualDispatchAsyncWorkManager alloc] init];
     117             :         }
     118             : #else
     119             :         sSingleton = [[OOOperationQueueAsyncWorkManager alloc] init];
     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             :         {
     156             :                 InitAsyncWorkManager();
     157             :                 NSAssert(sSingleton != nil, @"Async Work Manager init failed");
     158             :         }
     159             :         [sInitLock unlock];
     160             : #endif
     161             :         
     162             :         return sSingleton;
     163             : }
     164             : 
     165             : 
     166           0 : + (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           0 : - (void) dealloc
     178             : {
     179             :         abort();
     180             :         [super dealloc];
     181             : }
     182             : 
     183             : 
     184           0 : - (oneway void) release
     185             : {}
     186             : 
     187             : 
     188           0 : - (id) retain
     189             : {
     190             :         return self;
     191             : }
     192             : 
     193             : 
     194           0 : - (NSUInteger) retainCount
     195             : {
     196             :         return UINT_MAX;
     197             : }
     198             : 
     199             : 
     200             : - (BOOL) addTask:(id<OOAsyncWorkTask>)task priority:(OOAsyncWorkPriority)priority
     201             : {
     202             :         OOLogGenericSubclassResponsibility();
     203             :         return NO;
     204             : }
     205             : 
     206             : 
     207             : - (void) completePendingTasks
     208             : {
     209             :         OOLogGenericSubclassResponsibility();
     210             : }
     211             : 
     212             : 
     213             : - (void) waitForTaskToComplete:(id<OOAsyncWorkTask>)task
     214             : {
     215             :         OOLogGenericSubclassResponsibility();
     216             :         [NSException raise:NSInternalInconsistencyException format:@"%s called.", __PRETTY_FUNCTION__];
     217             : }
     218             : 
     219             : @end
     220             : 
     221             : 
     222             : @implementation OOAsyncWorkManagerInternal
     223             : 
     224             : 
     225           0 : - (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           0 : - (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           0 : - (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             : 
     321           0 : enum
     322             : {
     323             :         kMaxWorkThreads                 = 8
     324             : };
     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             : 
     404             : @implementation OOOperationQueueAsyncWorkManager
     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           0 : - (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           0 : - (void) dealloc
     433             : {
     434             :         [_operationQueue release];
     435             :         
     436             :         [super dealloc];
     437             : }
     438             : 
     439             : 
     440           0 : - (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

Generated by: LCOV version 1.14