#import #import #import #import #import #import #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]; PASS_EXCEPTION([obj start];, NSGenericException, "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"); 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"); PASS(1 == [[old dependencies] count], "dependencies not removed when done") [arp release]; arp = nil; return 0; }