2011-02-16 08:21:17 +00:00
|
|
|
#import <Foundation/NSArray.h>
|
|
|
|
#import <Foundation/NSLock.h>
|
|
|
|
#import <Foundation/NSOperation.h>
|
|
|
|
#import <Foundation/NSThread.h>
|
|
|
|
#import <Foundation/NSNotification.h>
|
|
|
|
#import <Foundation/NSAutoreleasePool.h>
|
|
|
|
#import "ObjectTesting.h"
|
|
|
|
|
|
|
|
|
|
|
|
@interface ThreadCounter : NSObject
|
|
|
|
{
|
|
|
|
unsigned count;
|
|
|
|
}
|
|
|
|
- (unsigned) count;
|
|
|
|
- (void) increment: (NSNotification*)n;
|
|
|
|
- (void) reset;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ThreadCounter
|
|
|
|
- (unsigned) count
|
|
|
|
{
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) increment: (NSNotification*)n
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver: self
|
|
|
|
selector: @selector(increment:)
|
|
|
|
name: NSThreadWillExitNotification
|
|
|
|
object: nil];
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) reset
|
|
|
|
{
|
|
|
|
count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface OpFlag : NSOperation
|
|
|
|
{
|
|
|
|
NSThread *thread;
|
|
|
|
BOOL flag;
|
|
|
|
}
|
|
|
|
- (void) main;
|
|
|
|
- (BOOL) ran;
|
|
|
|
- (NSThread*) thread;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation OpFlag
|
|
|
|
- (void) main
|
|
|
|
{
|
|
|
|
flag = YES;
|
|
|
|
thread = [NSThread currentThread];
|
|
|
|
}
|
|
|
|
- (BOOL) ran
|
|
|
|
{
|
|
|
|
return flag;
|
|
|
|
}
|
|
|
|
- (NSThread*) thread
|
|
|
|
{
|
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface OpExit : OpFlag
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation OpExit
|
|
|
|
- (void) main
|
|
|
|
{
|
|
|
|
[super main];
|
|
|
|
[NSThread exit];
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface OpRaise : OpFlag
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation OpRaise
|
|
|
|
- (void) main
|
|
|
|
{
|
|
|
|
[super main];
|
|
|
|
[NSException raise: NSGenericException format: @"done"];
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface OpOrder : NSOperation
|
|
|
|
@end
|
|
|
|
@implementation OpOrder
|
|
|
|
static NSLock *lock = nil;
|
|
|
|
static NSMutableArray *list = nil;
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
lock = [NSLock new];
|
|
|
|
list = [NSMutableArray new];
|
|
|
|
}
|
|
|
|
- (void) main
|
|
|
|
{
|
|
|
|
[lock lock];
|
|
|
|
[list addObject: self];
|
|
|
|
[lock unlock];
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
ThreadCounter *cnt;
|
|
|
|
id old;
|
|
|
|
id obj;
|
|
|
|
NSMutableArray *a;
|
|
|
|
NSOperationQueue *q;
|
|
|
|
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
|
|
|
|
|
|
|
cnt = [ThreadCounter new];
|
|
|
|
PASS((cnt != nil && [cnt count] == 0), "counter was set up");
|
|
|
|
|
|
|
|
// Check that operation runs in current thread.
|
|
|
|
obj = [OpFlag new];
|
|
|
|
[obj start];
|
|
|
|
PASS(([obj ran] == YES), "operation ran");
|
|
|
|
PASS(([obj thread] == [NSThread currentThread]), "operation ran in this thread");
|
|
|
|
[obj release];
|
|
|
|
|
|
|
|
// Check that monitoring of another thread works.
|
|
|
|
obj = [OpFlag new];
|
|
|
|
[NSThread detachNewThreadSelector: @selector(start)
|
|
|
|
toTarget: obj
|
|
|
|
withObject: nil];
|
|
|
|
[NSThread sleepForTimeInterval: 0.1];
|
|
|
|
PASS(([obj isFinished] == YES), "operation finished");
|
|
|
|
PASS(([obj ran] == YES), "operation ran");
|
|
|
|
PASS(([obj thread] != [NSThread currentThread]), "operation ran in other thread");
|
|
|
|
[obj release];
|
|
|
|
|
|
|
|
// Check that exit from thread in -main causes operation tracking to fail.
|
|
|
|
obj = [OpExit new];
|
|
|
|
[NSThread detachNewThreadSelector: @selector(start)
|
|
|
|
toTarget: obj
|
|
|
|
withObject: nil];
|
|
|
|
[NSThread sleepForTimeInterval: 0.1];
|
|
|
|
PASS(([obj isFinished] == NO), "operation exited");
|
|
|
|
PASS(([obj ran] == YES), "operation ran");
|
|
|
|
PASS(([obj thread] != [NSThread currentThread]), "operation ran in other thread");
|
|
|
|
PASS(([obj isExecuting] == YES), "operation seems to be running");
|
|
|
|
[obj release];
|
|
|
|
|
|
|
|
// Check that raising exception in -main causes operation tracking to fail.
|
|
|
|
obj = [OpRaise new];
|
2011-02-24 16:26:01 +00:00
|
|
|
PASS_EXCEPTION([obj start];,
|
|
|
|
NSGenericException,
|
2011-02-16 08:21:17 +00:00
|
|
|
"NSOperation exceptions propogate from main");
|
|
|
|
PASS(([obj isFinished] == NO), "operation failed to finish");
|
|
|
|
PASS(([obj ran] == YES), "operation ran");
|
|
|
|
PASS(([obj thread] == [NSThread currentThread]), "operation ran in this thread");
|
|
|
|
PASS(([obj isExecuting] == YES), "operation seems to be running");
|
|
|
|
[obj release];
|
|
|
|
|
|
|
|
obj = [OpFlag new];
|
|
|
|
[obj start];
|
|
|
|
PASS(([obj ran] == YES), "operation ran");
|
|
|
|
PASS(([obj thread] == [NSThread currentThread]), "operation ran in this thread");
|
|
|
|
[obj release];
|
|
|
|
|
|
|
|
obj = [OpFlag new];
|
|
|
|
q = [NSOperationQueue new];
|
|
|
|
[cnt reset];
|
|
|
|
[q addOperation: obj];
|
|
|
|
[q waitUntilAllOperationsAreFinished];
|
|
|
|
PASS(([obj ran] == YES), "operation ran");
|
|
|
|
PASS(([obj thread] != [NSThread currentThread]), "operation ran in other thread");
|
2011-03-03 10:56:47 +00:00
|
|
|
|
2011-02-16 08:21:17 +00:00
|
|
|
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");
|
|
|
|
|
|
|
|
PASS(([NSOperationQueue currentQueue] == [NSOperationQueue mainQueue]), "current queue outside -main is main queue");
|
|
|
|
PASS(([NSOperationQueue mainQueue] != nil), "main queue is not nil");
|
|
|
|
obj = [OpFlag new];
|
|
|
|
[q addOperation: obj];
|
|
|
|
[q waitUntilAllOperationsAreFinished];
|
|
|
|
PASS(([obj isFinished] == YES), "main queue runs an operation");
|
|
|
|
PASS(([obj thread] != [NSThread currentThread]), "operation ran in other thread");
|
|
|
|
|
|
|
|
[q setSuspended: YES];
|
|
|
|
obj = [OpFlag new];
|
|
|
|
[q addOperation: obj];
|
|
|
|
[NSThread sleepForTimeInterval: 0.1];
|
|
|
|
PASS(([obj isFinished] == NO), "suspend works");
|
|
|
|
[q setSuspended: NO];
|
|
|
|
[q waitUntilAllOperationsAreFinished];
|
|
|
|
PASS(([obj isFinished] == YES), "unsuspend works");
|
|
|
|
[obj release];
|
|
|
|
|
|
|
|
[q setMaxConcurrentOperationCount: 0];
|
|
|
|
obj = [OpFlag new];
|
|
|
|
[q addOperation: obj];
|
|
|
|
[NSThread sleepForTimeInterval: 0.1];
|
|
|
|
PASS(([obj isFinished] == NO), "max operation count of zero suspends queue");
|
|
|
|
[q setMaxConcurrentOperationCount: 1];
|
|
|
|
[q waitUntilAllOperationsAreFinished];
|
|
|
|
PASS(([obj isFinished] == YES), "resetting max operation queue sarts it");
|
|
|
|
[obj release];
|
|
|
|
|
|
|
|
a = [NSMutableArray array];
|
|
|
|
|
|
|
|
[q setSuspended: YES];
|
|
|
|
obj = [OpOrder new];
|
|
|
|
[a addObject: obj];
|
|
|
|
[obj release];
|
|
|
|
obj = [OpOrder new];
|
|
|
|
[a addObject: obj];
|
|
|
|
[obj release];
|
|
|
|
obj = [OpOrder new];
|
|
|
|
[a addObject: obj];
|
|
|
|
[obj release];
|
|
|
|
[q setSuspended: NO];
|
|
|
|
[q addOperations: a waitUntilFinished: YES];
|
|
|
|
PASS(([list isEqual: a]), "operations ran in order of addition");
|
|
|
|
|
|
|
|
[list removeAllObjects];
|
|
|
|
[a removeAllObjects];
|
|
|
|
[q setSuspended: YES];
|
|
|
|
old = [OpOrder new];
|
|
|
|
[a addObject: old];
|
|
|
|
[old release];
|
|
|
|
[old setQueuePriority: NSOperationQueuePriorityLow];
|
|
|
|
obj = [OpOrder new];
|
|
|
|
[a addObject: obj];
|
|
|
|
[obj release];
|
|
|
|
[old addDependency: obj];
|
|
|
|
obj = [OpOrder new];
|
|
|
|
[a addObject: obj];
|
|
|
|
[obj release];
|
|
|
|
[obj setQueuePriority: NSOperationQueuePriorityHigh];
|
|
|
|
[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");
|
2011-02-28 07:01:11 +00:00
|
|
|
PASS(1 == [[old dependencies] count], "dependencies not removed when done")
|
2011-02-16 08:21:17 +00:00
|
|
|
|
|
|
|
[arp release]; arp = nil;
|
|
|
|
return 0;
|
|
|
|
}
|