/* * Grouch.app Copyright (C) 2006 Andy Sveikauskas * ------------------------------------------------------------------------ * This program is free software under the GNU General Public License * -- * This class provides a way to queue NSInvocations for thread safety. It * creates an NSTimer (which runs in a run loop). Then other threads will * add invocations to its queue, and they will be invoked in the timer call. * * In addition, GrouchRunLoopHack provides NSRunLoop-style methods to add * and remove things from the run loop. So you can pretend GrouchRunLoopHack * is an NSRunLoop if it suits you for this purpose. */ #import #import #import #import #import #import #import #import #include #include #define node grouchrunloop_node @implementation GrouchRunLoopHack - (void)setTimer { if( !loop ) loop = [NSRunLoop currentRunLoop]; if( !timer ) { timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timedEvent) userInfo:nil repeats:YES]; [loop addTimer:timer forMode:NSDefaultRunLoopMode]; } } - init { loop = nil; head = tail = NULL; lock = [NSLock new]; timer = nil; [self setTimer]; return self; } - initForRunLoop:(NSRunLoop*)l { loop = l; head = tail = NULL; lock = [NSLock new]; timer = nil; [self setTimer]; return self; } - (void)processQueue:(BOOL)go { struct node *n; [lock lock]; n = head; while( n ) { NS_DURING if( go ) [n->invocation invoke]; NS_HANDLER NSLog(@"GrouchRunLoopHack caught %@", [localException name]); NS_ENDHANDLER [n->invocation release]; if( n->array ) [n->array release]; head = head->next; free( n ); n = head; } if( !head ) tail = NULL; [lock unlock]; } - (void)dealloc { [self processQueue:NO]; [timer invalidate]; [lock release]; [super dealloc]; } - (void)timedEvent { if( head ) [self processQueue:YES]; if( !head && markedForDestruction ) { [timer invalidate]; [self release]; } } - (void)invalidate { markedForDestruction = YES; } - (void)addInvocation:(NSInvocation*)invok withArguments:(NSObject*)array { struct node *r; [lock lock]; r = malloc(sizeof(struct node)); if( !r ) [GrouchException raiseMemoryException]; memset( r, 0, sizeof(struct node) ); if( !tail ) head = tail = r; else tail = tail->next = r; r->invocation = [invok retain]; if( array ) r->array = [array retain]; [lock unlock]; } - (void)addInvocation:(NSInvocation*)invok { [self addInvocation:invok withArguments:nil]; } @end @implementation GrouchRunLoopHack (RunLoopCompatibility) - (NSInvocation*)_setUpSelector:(SEL)sel { NSInvocation *i; i = [NSInvocation invocationWithMethodSignature: [loop methodSignatureForSelector:sel]]; [i setTarget:loop]; [i setSelector:sel]; return i; } - (NSInvocation*)_methodWithTwoArgs:(SEL)sel withArg:arg1 and:arg2 { NSInvocation *i = [self _setUpSelector:sel]; [i setArgument:&arg1 atIndex:2]; [i setArgument:&arg2 atIndex:3]; [i retainArguments]; return i; } - (void)addTimer:(NSTimer*)t forMode:(NSString*)mode { [self addInvocation: [self _methodWithTwoArgs:@selector(addTimer:forMode:) withArg:t and:mode]]; } - (void)addPort:(NSPort*)port forMode:(NSString*)mode { [self addInvocation: [self _methodWithTwoArgs:@selector(addPort:forMode:) withArg:port and:mode]]; } - (void)removePort:(NSPort*)port forMode:(NSString*)mode { [self addInvocation: [self _methodWithTwoArgs:@selector(removePort:forMode:) withArg:port and:mode]]; } #ifdef GNUSTEP - (void)addEvent:(void*)data type:(RunLoopEventType)type watcher:(id)watcher forMode:(NSString*)mode { SEL sel = @selector(addEvent:type:watcher:forMode:); NSArray *objs = [NSArray arrayWithObjects:watcher,mode,nil]; NSInvocation *i = [self _setUpSelector:sel]; [i setArgument:&data atIndex:2]; [i setArgument:&type atIndex:3]; [i setArgument:&watcher atIndex:4]; [i setArgument:&mode atIndex:5]; [self addInvocation:i withArguments:objs]; } - (void)removeEvent:(void*)data type:(RunLoopEventType)type forMode:(NSString*)mode all:(BOOL)all { SEL sel = @selector(removeEvent:type:forMode:all:); NSObject *objs = mode; NSInvocation *i = [self _setUpSelector:sel]; [i setArgument:&data atIndex:2]; [i setArgument:&type atIndex:3]; [i setArgument:&mode atIndex:4]; [i setArgument:&all atIndex:5]; [self addInvocation:i withArguments:objs]; } #endif @end