re-introduce thread pool

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@32442 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2011-03-03 15:14:29 +00:00
parent db78d4cd8e
commit e0a2640d15
3 changed files with 106 additions and 10 deletions

View file

@ -1,9 +1,13 @@
2011-03-03 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSOperation.m: re-introduce per-queue thread pool.
2011-03-03 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSOperation.m: reorganise and simplify code, removing the
pool of threads for handling operations (need to re-add this at a
later point, but probably as a separate class) ... implement
support for 'concurrent' operations (ones which mange their own
support for 'concurrent' operations (ones which manage their own
executions rather than running in a thread provided by the queue.
2011-03-03 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -45,11 +45,14 @@
#define GS_NSOperationQueue_IVARS \
NSRecursiveLock *lock; \
NSConditionLock *cond; \
NSMutableArray *operations; \
NSMutableArray *waiting; \
NSMutableArray *starting; \
NSString *name; \
BOOL suspended; \
NSInteger executing; \
NSInteger threadCount; \
NSInteger count;
#import "Foundation/NSOperation.h"
@ -66,6 +69,10 @@
#include "GSInternal.h"
GS_PRIVATE_INTERNAL(NSOperation)
/* The pool of threads for 'non-concurrent' operations in a queue.
*/
#define POOL 8
static NSArray *empty = nil;
@interface NSOperation (Private)
@ -539,6 +546,7 @@ GS_PRIVATE_INTERNAL(NSOperationQueue)
@interface NSOperationQueue (Private)
+ (void) _mainQueue;
- (void) _execute;
- (void) _thread;
- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
@ -720,8 +728,10 @@ static NSOperationQueue *mainQueue = nil;
- (void) dealloc
{
[internal->operations release];
[internal->starting release];
[internal->waiting release];
[internal->name release];
[internal->cond release];
[internal->lock release];
GS_DESTROY_INTERNAL(NSOperationQueue);
[super dealloc];
@ -735,8 +745,10 @@ static NSOperationQueue *mainQueue = nil;
internal->suspended = NO;
internal->count = NSOperationQueueDefaultMaxConcurrentOperationCount;
internal->operations = [NSMutableArray new];
internal->starting = [NSMutableArray new];
internal->waiting = [NSMutableArray new];
internal->lock = [NSRecursiveLock new];
internal->cond = [[NSConditionLock alloc] initWithCondition: 0];
}
return self;
}
@ -901,6 +913,75 @@ static NSOperationQueue *mainQueue = nil;
[self _execute];
}
- (void) _thread
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
for (;;)
{
NSOperation *op;
NSDate *when;
BOOL found;
when = [[NSDate alloc] initWithTimeIntervalSinceNow: 5.0];
found = [internal->cond lockWhenCondition: 1 beforeDate: when];
[when release];
if (NO == found)
{
break; // Idle for 5 seconds ... exit thread.
}
if ([internal->starting count] > 0)
{
op = [internal->starting objectAtIndex: 0];
[internal->starting removeObjectAtIndex: 0];
}
else
{
op = nil;
}
if ([internal->starting count] > 0)
{
// Signal any other idle threads,
[internal->cond unlockWithCondition: 1];
}
else
{
// There are no more operations starting.
[internal->cond unlockWithCondition: 0];
}
if (nil != op)
{
NS_DURING
{
NSAutoreleasePool *opPool = [NSAutoreleasePool new];
if (NO == [op isCancelled])
{
[NSThread setThreadPriority: [op threadPriority]];
[op main];
}
[opPool release];
}
NS_HANDLER
{
NSLog(@"Problem running operation %@ ... %@",
op, localException);
}
NS_ENDHANDLER
[op _finish];
}
}
[internal->lock lock];
internal->threadCount--;
[internal->lock unlock];
[pool release];
[NSThread exit];
}
/* Check for operations which can be executed and start them.
*/
- (void) _execute
@ -943,10 +1024,26 @@ static NSOperationQueue *mainQueue = nil;
}
else
{
// FIXME ... use a thread pool again in future.
[NSThread detachNewThreadSelector: @selector(start)
toTarget: op
withObject: nil];
NSUInteger pending;
[internal->cond lock];
pending = [internal->starting count];
[internal->starting addObject: op];
/* Create a new thread if all existing threads are busy and
* we haven't reached the pool limit.
*/
if (0 == internal->threadCount
|| (pending > 0 && internal->threadCount < POOL))
{
internal->threadCount++;
[NSThread detachNewThreadSelector: @selector(_thread)
toTarget: self
withObject: nil];
}
/* Tell the thread pool that there is an operation to start.
*/
[internal->cond unlockWithCondition: 1];
}
}
[internal->lock unlock];

View file

@ -182,16 +182,11 @@ int main()
PASS(([obj ran] == YES), "operation ran");
PASS(([obj thread] != [NSThread currentThread]), "operation ran in other thread");
/* Currently we start a new thread for each operation, so this next test
* won't pass until we re-implement a thread pool.
*/
testHopeful = YES;
PASS(([cnt count] == 0), "thread did not exit immediately");
[obj release];
/* Observer behavior on OSX 10.6 is that the thread exits after five seconds ... but who knows what that might change to. */
[NSThread sleepForTimeInterval: 6.0];
PASS(([cnt count] == 1), "thread exit occurs after six seconds");
testHopeful = NO;
PASS(([NSOperationQueue currentQueue] == [NSOperationQueue mainQueue]), "current queue outside -main is main queue");
PASS(([NSOperationQueue mainQueue] != nil), "main queue is not nil");