git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@40007 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2016-07-18 09:51:35 +00:00
parent 11a838ff5e
commit cbbc6d14e1
3 changed files with 81 additions and 22 deletions

View file

@ -1,3 +1,11 @@
2016-07-18 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSOperation.m: avoid sorting the queue ... keep the array of
waiting operations sorted by inserting new operations at the correct
position and observing the queuePriority of waiting operations (and
re-positiuoning them in the waiting array as necessary).
Fix for scalability problem (bug #47926)
2016-07-16 Richard Frith-Macdonald <rfm@gnu.org>
* Source/win32/GSRunLoopCtxt.m: fix bug in return value when polling.

View file

@ -64,12 +64,17 @@
#import "Foundation/NSException.h"
#import "Foundation/NSKeyValueObserving.h"
#import "Foundation/NSThread.h"
#import "GNUstepBase/NSArray+GNUstepBase.h"
#import "GSPrivate.h"
#define GSInternal NSOperationInternal
#include "GSInternal.h"
GS_PRIVATE_INTERNAL(NSOperation)
static void *isFinishedCtxt = (void*)"isFinished";
static void *isReadyCtxt = (void*)"isReady";
static void *queuePriorityCtxt = (void*)"queuePriority";
/* The pool of threads for 'non-concurrent' operations in a queue.
*/
#define POOL 8
@ -135,7 +140,7 @@ static NSArray *empty = nil;
[op addObserver: self
forKeyPath: @"isFinished"
options: NSKeyValueObservingOptionNew
context: NULL];
context: isFinishedCtxt];
if (internal->ready == YES)
{
/* The new dependency stops us being ready ...
@ -245,7 +250,7 @@ static NSArray *empty = nil;
[self addObserver: self
forKeyPath: @"isFinished"
options: NSKeyValueObservingOptionNew
context: NULL];
context: isFinishedCtxt];
}
return self;
}
@ -355,7 +360,7 @@ static NSArray *empty = nil;
[self observeValueForKeyPath: @"isFinished"
ofObject: op
change: nil
context: nil];
context: isFinishedCtxt];
}
[self didChangeValueForKey: @"dependencies"];
}
@ -603,7 +608,7 @@ static NSOperationQueue *mainQueue = nil;
[op addObserver: self
forKeyPath: @"isReady"
options: NSKeyValueObservingOptionNew
context: NULL];
context: isReadyCtxt];
[self willChangeValueForKey: @"operations"];
[self willChangeValueForKey: @"operationCount"];
[internal->operations addObject: op];
@ -614,7 +619,7 @@ static NSOperationQueue *mainQueue = nil;
[self observeValueForKeyPath: @"isReady"
ofObject: op
change: nil
context: nil];
context: isReadyCtxt];
}
}
[internal->lock unlock];
@ -679,7 +684,7 @@ static NSOperationQueue *mainQueue = nil;
[op addObserver: self
forKeyPath: @"isReady"
options: NSKeyValueObservingOptionNew
context: NULL];
context: isReadyCtxt];
[internal->operations addObject: op];
if (NO == [op isReady])
{
@ -697,7 +702,7 @@ static NSOperationQueue *mainQueue = nil;
[self observeValueForKeyPath: @"isReady"
ofObject: op
change: nil
context: nil];
context: isReadyCtxt];
}
}
[internal->lock unlock];
@ -867,9 +872,14 @@ static NSOperationQueue *mainQueue = nil;
change: (NSDictionary *)change
context: (void *)context
{
[internal->lock lock];
if (YES == [object isFinished])
/* We observe three properties in sequence ...
* isReady (while we wait for an operation to be ready)
* queuePriority (when priority of a ready operation may change)
* isFinished (to see if an executing operation is over).
*/
if (context == isFinishedCtxt)
{
[internal->lock lock];
internal->executing--;
[object removeObserver: self
forKeyPath: @"isFinished"];
@ -882,11 +892,27 @@ static NSOperationQueue *mainQueue = nil;
[self didChangeValueForKey: @"operationCount"];
[self didChangeValueForKey: @"operations"];
}
else if (YES == [object isReady])
else if (context == queuePriorityCtxt || context == isReadyCtxt)
{
[object removeObserver: self
forKeyPath: @"isReady"];
[internal->waiting addObject: object];
NSInteger pos;
[internal->lock lock];
if (context == queuePriorityCtxt)
{
[internal->waiting removeObjectIdenticalTo: object];
}
if (context == isReadyCtxt)
{
[object removeObserver: self forKeyPath: @"isReady"];
[object addObserver: self
forKeyPath: @"queuePriority"
options: NSKeyValueObservingOptionNew
context: queuePriorityCtxt];
}
pos = [internal->waiting insertionPosition: object
usingFunction: sortFunc
context: 0];
[internal->waiting insertObject: object atIndex: pos];
[internal->lock unlock];
}
[self _execute];
@ -985,10 +1011,6 @@ static NSOperationQueue *mainQueue = nil;
{
NSOperation *op;
/* Make sure we have a sorted queue of operations waiting to execute.
*/
[internal->waiting sortUsingFunction: sortFunc context: 0];
/* Take the first operation from the queue and start it executing.
* We set ourselves up as an observer for the operating finishing
* and we keep track of the count of operations we have started,
@ -996,10 +1018,11 @@ static NSOperationQueue *mainQueue = nil;
*/
op = [internal->waiting objectAtIndex: 0];
[internal->waiting removeObjectAtIndex: 0];
[op removeObserver: self forKeyPath: @"queuePriority"];
[op addObserver: self
forKeyPath: @"isFinished"
options: NSKeyValueObservingOptionNew
context: NULL];
context: isFinishedCtxt];
internal->executing++;
if (YES == [op isConcurrent])
{

View file

@ -83,13 +83,13 @@
- (void) release
{
NSLog(@"Will release %@ at %@", self, [NSThread callStackSymbols]);
// NSLog(@"Will release %@ at %@", self, [NSThread callStackSymbols]);
[super release];
}
- (id) retain
{
NSLog(@"Will retain %@ at %@", self, [NSThread callStackSymbols]);
// NSLog(@"Will retain %@ at %@", self, [NSThread callStackSymbols]);
return [super retain];
}
@ -218,7 +218,8 @@ int main()
[q addOperation: obj];
[q waitUntilAllOperationsAreFinished];
PASS(([obj isFinished] == YES), "main queue runs an operation");
PASS(([obj thread] != [NSThread currentThread]), "operation ran in other thread");
PASS(([obj thread] != [NSThread currentThread]),
"operation ran in other thread");
[q setSuspended: YES];
obj = [OpFlag new];
@ -256,6 +257,31 @@ int main()
[q addOperations: a waitUntilFinished: YES];
PASS(([list isEqual: a]), "operations ran in order of addition");
[list removeAllObjects];
[a removeAllObjects];
[q setSuspended: YES];
obj = [OpOrder new];
[obj setQueuePriority: NSOperationQueuePriorityHigh];
[a addObject: obj];
[q addOperation: obj];
[obj release];
obj = [OpOrder new];
[a addObject: obj];
[q addOperation: obj];
[obj release];
obj = [OpOrder new];
[obj setQueuePriority: NSOperationQueuePriorityLow];
[a addObject: obj];
[q addOperation: obj];
[obj release];
obj = [a objectAtIndex: 1];
[obj setQueuePriority: NSOperationQueuePriorityVeryLow];
[a addObject: obj];
[a removeObjectAtIndex: 1];
[q setSuspended: NO];
[q waitUntilAllOperationsAreFinished];
PASS(([list isEqual: a]), "operations ran in order of priority");
[list removeAllObjects];
[a removeAllObjects];
[q setSuspended: YES];
@ -274,7 +300,9 @@ int main()
[obj addDependency: old];
[q setSuspended: NO];
[q addOperations: a waitUntilFinished: YES];
PASS(([list objectAtIndex: 0] == [a objectAtIndex: 1] && [list objectAtIndex: 1] == [a objectAtIndex: 0]), "operations ran in order of dependency");
PASS(([list objectAtIndex: 0] == [a objectAtIndex: 1]
&& [list objectAtIndex: 1] == [a objectAtIndex: 0]),
"operations ran in order of dependency");
PASS(1 == [[old dependencies] count], "dependencies not removed when done")
[arp release]; arp = nil;