/* * ----------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you can * do whatever you want with this stuff. If we meet some day, and you think this * stuff is worth it, you can buy me a beer in return. Jakob Stoklund Olesen * ----------------------------------------------------------------------------- */ #import "YAMessageQueue.h" #import "Protocol-YASignature.h" static NSString *const defaultQueueKey = @"defaultYAMessageQueue"; @implementation YAMessageQueue + (YAMessageQueue*)defaultQueue; { YAMessageQueue *q = [[[NSThread currentThread] threadDictionary] objectForKey:defaultQueueKey]; if (q==nil) { q = [[YAMessageQueue alloc] init]; [q deliverInCurrentThread]; [q release]; } return q; } - (void)enqueueInvocation:(NSInvocation*)invoc { [lock lock]; [queue addObject:invoc]; [lock unlock]; if (runLoopSource!=NULL) CFRunLoopSourceSignal(runLoopSource); if (runLoop!=NULL) CFRunLoopWakeUp(runLoop); } - (NSInvocation*)dequeueInvocation { NSInvocation *invoc = nil; [lock lock]; if ([queue count]>0) { invoc = [[[queue objectAtIndex:0] retain] autorelease]; [queue removeObjectAtIndex:0]; } [lock unlock]; return invoc; } static void rlsCallBack(void *info) { YAMessageQueue *queue = [(YAMessageQueue*)info retain]; NSInvocation *invoc; do { invoc = nil; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NS_DURING invoc = [queue dequeueInvocation]; if (invoc) [invoc invoke]; NS_HANDLER NSLog(@"YAMessageQueue caught \"%@\" when invoking %s", localException, invoc ? (const char*)[invoc selector] : "dequeueInvocation"); NS_ENDHANDLER [pool release]; } while (invoc!=nil); [queue release]; } - (id)init; { if (![super init]) return nil; lock = [[NSLock alloc] init]; queue = [[NSMutableArray alloc] init]; CFRunLoopSourceContext context = { 0, self, NULL, NULL, NULL, NULL, NULL, NULL, NULL, rlsCallBack }; runLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); return self; } - (void)dealloc; { [lock lock]; CFRunLoopSourceInvalidate(runLoopSource); CFRelease(runLoopSource); runLoopSource = NULL; if (runLoop!=NULL) CFRelease(runLoop); runLoop = NULL; [queue release]; queue = nil; [lock autorelease]; [lock unlock]; lock = nil; [super dealloc]; } - (void)deliverInCurrentThread; { CFRunLoopRef current = CFRunLoopGetCurrent(); if (current==runLoop) return; if (runLoop!=NULL) { CFRunLoopRemoveSource(runLoop, runLoopSource, kCFRunLoopCommonModes); CFRelease(runLoop); } runLoop = current; CFRetain(runLoop); CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes); NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary]; if ([dict objectForKey:defaultQueueKey]==nil) [dict setValue:self forKey:defaultQueueKey]; } - (id)proxyForTarget:target; { return [[[YAMessageQueueingProxy alloc] initWithTarget:target queue:self] autorelease]; } @end @implementation YAMessageQueueingProxy - (id)initWithTarget:t queue:(YAMessageQueue*)q; { // NSProxy does not implement init target = [t retain]; queue = [q retain]; return self; } - (void)dealloc { [target release]; [queue release]; [super dealloc]; } - (void)setProtocolForProxy:(Protocol *)p { protocol = p; } - (void)setTargetForProxy:(id)newTarget; { [newTarget retain]; [target release]; target = newTarget; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { if (protocol!=nil) { return [protocol instanceMethodSignatureForSelector:sel]; } else { return [target methodSignatureForSelector:sel]; } } - (void)forwardInvocation:(NSInvocation *)invoc { [invoc setTarget:target]; [invoc retainArguments]; [queue enqueueInvocation:invoc]; } @end