mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 00:41:02 +00:00
fix for bug #47926
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@40007 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
11a838ff5e
commit
cbbc6d14e1
3 changed files with 81 additions and 22 deletions
|
@ -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.
|
||||
|
|
|
@ -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])
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue