From 4c9c691626ddd0492257617f9518860d7a2e687c Mon Sep 17 00:00:00 2001 From: rfm Date: Tue, 7 Jan 2025 13:05:55 +0000 Subject: [PATCH] Operation queue convenience methods --- .../NSOperationQueue+GNUstepBase.h | 8 ++ .../Additions/NSOperationQueue+GNUstepBase.m | 20 +++++ Tests/base/NSOperation/concurrent.m | 19 ++++- Tests/base/NSOperation/perform.m | 77 +++++++++++++++++++ 4 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 Tests/base/NSOperation/perform.m diff --git a/Headers/GNUstepBase/NSOperationQueue+GNUstepBase.h b/Headers/GNUstepBase/NSOperationQueue+GNUstepBase.h index cd3cf3852..bd76c2075 100644 --- a/Headers/GNUstepBase/NSOperationQueue+GNUstepBase.h +++ b/Headers/GNUstepBase/NSOperationQueue+GNUstepBase.h @@ -36,6 +36,14 @@ extern "C" { @interface NSOperationQueue (GNUstepBase) +/** Wraps a nil terminated list of objects in key/value pairs in a + * dictionary and adds an operation to send a message to aTarget to + * perform aSelector (which takes the map as its single argument). + */ +- (void) addOperationWithTarget: (id)aTarget + performSelector: (SEL)aSelector + withMap: (id)firstkey, ...; + - (void) addOperationWithTarget: (id)aTarget performSelector: (SEL)aSelector withObject: (id)object1 diff --git a/Source/Additions/NSOperationQueue+GNUstepBase.m b/Source/Additions/NSOperationQueue+GNUstepBase.m index 9071e9567..5e3c6075c 100644 --- a/Source/Additions/NSOperationQueue+GNUstepBase.m +++ b/Source/Additions/NSOperationQueue+GNUstepBase.m @@ -22,9 +22,12 @@ */ #import "../common.h" +#import "Foundation/NSDictionary.h" #import "Foundation/NSException.h" +#import "Foundation/NSMapTable.h" #import "Foundation/NSObject.h" #import "Foundation/NSOperation.h" +#import "GNUstepBase/GSObjCRuntime.h" #import "GNUstepBase/NSOperationQueue+GNUstepBase.h" @@ -72,6 +75,23 @@ */ @implementation NSOperationQueue (GNUstepBase) +- (void) addOperationWithTarget: (id)aTarget + performSelector: (SEL)aSelector + withMap: (id)firstKey, ... +{ + GSTargetOperation *top; + NSDictionary *map; + + map = [NSDictionary alloc]; + GS_USEIDPAIRLIST(firstKey, + map = [map initWithObjects: __pairs forKeys: __objects count: __count/2]); + + top = [GSTargetOperation operationWithTarget: aTarget + performSelector: aSelector + withObject: AUTORELEASE(map)]; + [self addOperation: top]; +} + - (void) addOperationWithTarget: (id)aTarget performSelector: (SEL)aSelector withObject: (id)object1 diff --git a/Tests/base/NSOperation/concurrent.m b/Tests/base/NSOperation/concurrent.m index acb321631..480d86bcb 100644 --- a/Tests/base/NSOperation/concurrent.m +++ b/Tests/base/NSOperation/concurrent.m @@ -105,6 +105,10 @@ int main() NSOperationQueue *q; int i; NSMutableArray *a; + NSTimeInterval s; + NSTimeInterval f; + NSUInteger ran; + NSUInteger want; # if __has_feature(blocks) __block BOOL blockDidRun = NO; #endif @@ -128,23 +132,32 @@ int main() [obj release]; // multiple concurrent operations + s = [NSDate timeIntervalSinceReferenceDate]; [q setMaxConcurrentOperationCount: 10]; a = [NSMutableArray array]; - for (i = 0; i < 5; ++i) + want = 200; + ran = 0; + for (i = 0; i < want; ++i) { obj = [[MyOperation alloc] initWithValue: i]; [a addObject: obj]; [q addOperation: obj]; } [q waitUntilAllOperationsAreFinished]; + f = [NSDate timeIntervalSinceReferenceDate]; PASS(([obj isFinished] == YES), "operation ran"); PASS(([obj isExecuting] == NO), "operation is not executing"); - for (i = 0; i < 5; ++i) + for (i = 0; i < want; ++i) { obj = [a objectAtIndex: i]; - PASS(([obj getCalculation] == (2*i)), "operation was performed"); + if ([obj getCalculation] == (2*i)) + { + ran++; + } } + PASS((ran == want), "many operations, all were performed") + PASS((f - s) < 0.1, "many operations, duration was reasonably small") // multiple concurrent operations [q setMaxConcurrentOperationCount: 5]; diff --git a/Tests/base/NSOperation/perform.m b/Tests/base/NSOperation/perform.m new file mode 100644 index 000000000..069e82094 --- /dev/null +++ b/Tests/base/NSOperation/perform.m @@ -0,0 +1,77 @@ +#import +#import +#import "ObjectTesting.h" + +@interface MyClass : NSObject +{ + unsigned counter; +} +- (void) count: (NSMapTable*)map; +- (unsigned) counter; +- (void) increment; +- (void) reset; +@end + +@implementation MyClass +- (void) count: (NSDictionary*)map +{ + unsigned c = [map count]; + +// NSLog(@"Count %u for %@", c, map); + counter += c; +} +- (unsigned) counter +{ + return counter; +} +- (void) increment +{ + counter = counter + 1; +} +- (void) reset +{ + counter = 0; +} +@end + +int main() +{ + ENTER_POOL + NSOperationQueue *q; + NSUInteger i; + NSUInteger ran; + NSUInteger want; + NSTimeInterval s; + NSTimeInterval f; + MyClass *o = AUTORELEASE([[MyClass alloc] init]); + + q = AUTORELEASE([[NSOperationQueue alloc] init]); + [q setMaxConcurrentOperationCount: 1]; + + ran = 0; + want = 200; + s = [NSDate timeIntervalSinceReferenceDate]; + for (i = 0; i < want; i++) + { + [q addOperationWithTarget: o + performSelector: @selector(increment)]; + } + [q waitUntilAllOperationsAreFinished]; + f = [NSDate timeIntervalSinceReferenceDate]; + PASS([o counter] == want, "expected number of operations") + PASS((f - s) < 0.1, "duration was reasonable") + + [o reset]; + [q addOperationWithTarget: o + performSelector: @selector(count:) + withMap: + @"Key1", @"Val1", + @"Key2", @"Val2", + @"Key3", @"Val3", + nil]; + [q waitUntilAllOperationsAreFinished]; + PASS([o counter] == 3, "map had three keys") + + LEAVE_POOL + return 0; +}