diff --git a/Classes/Headers/IRCWorld.h b/Classes/Headers/IRCWorld.h index 80d42c5b58..524e1bf961 100755 --- a/Classes/Headers/IRCWorld.h +++ b/Classes/Headers/IRCWorld.h @@ -73,8 +73,7 @@ @property (nonatomic, strong) NSDictionary *bundlesForUserInput; @property (nonatomic, strong) NSDictionary *bundlesForServerInput; @property (nonatomic, strong) NSDictionary *bundlesWithOutputRules; -@property (nonatomic, assign) dispatch_queue_t frontmostViewMessageQueue; -@property (nonatomic, assign) dispatch_queue_t backgroundViewMessageQueue; +@property (nonatomic, strong) NSOperationQueue *messageOperationQueue; - (void)setup:(IRCWorldConfig *)seed; - (void)setupTree; @@ -91,8 +90,6 @@ - (void)terminate; - (void)prepareForSleep; -- (void)runMessageQueueLoop:(TVCLogController *)sender; - - (IRCClient *)findClient:(NSString *)name; - (IRCClient *)findClientById:(NSInteger)uid; - (IRCChannel *)findChannelByClientId:(NSInteger)uid channelId:(NSInteger)cid; @@ -150,4 +147,15 @@ - (void)destroyAllEvidence; - (TVCLogController *)createLogWithClient:(IRCClient *)client channel:(IRCChannel *)channel; +@end + +@interface TKMessageBlockOperation : NSOperation +@property (nonatomic, retain) TVCLogController *controller; +@property (nonatomic, assign) BOOL special; ++ (TKMessageBlockOperation *) operationWithBlock:(void(^)(void))block + forController:(TVCLogController *)controller + withSpecialPriority:(BOOL)special; ++ (TKMessageBlockOperation *) operationWithBlock:(void(^)(void))block + forController:(TVCLogController *)controller; ++ (TKMessageBlockOperation *) operationWithBlock:(void(^)(void))block; @end \ No newline at end of file diff --git a/Classes/Headers/TVCLogController.h b/Classes/Headers/TVCLogController.h index efc6be98e6..65869a01cc 100755 --- a/Classes/Headers/TVCLogController.h +++ b/Classes/Headers/TVCLogController.h @@ -68,10 +68,6 @@ typedef id (^TVCLogMessageBlock)(void); @property (nonatomic, assign) NSInteger lastVisitedHighlight; @property (nonatomic, strong) NSMutableArray *highlightedLineNumbers; -@property (strong) NSMutableArray *normalMessageQueue; // Priority: Low -@property (strong) NSMutableArray *specialMessageQueue; // Priority: High -@property (assign) BOOL queueInProgress; - - (void)setUp; - (void)restorePosition; - (void)notifyDidBecomeVisible; @@ -85,8 +81,6 @@ typedef id (^TVCLogMessageBlock)(void); - (void)moveToTop; - (void)moveToBottom; -- (void)runMessageQueueLoop; // Only Textual should call this. - - (void)setTopic:(NSString *)topic; - (void)mark; @@ -105,4 +99,8 @@ typedef id (^TVCLogMessageBlock)(void); - (NSString *)renderedBodyForTranscriptLog:(TVCLogLine *)line; - (void)logViewOnDoubleClick:(NSString *)e; + +- (void)handleMessageBlock:(id)block isSpecial:(BOOL)special; +- (void)enqueueMessageBlock:(id)messageBlock fromSender:(TVCLogController *)sender isSpecial:(BOOL)special; +- (void)enqueueMessageBlock:(id)messageBlock fromSender:(TVCLogController *)sender; @end \ No newline at end of file diff --git a/Classes/IRC/IRCWorld.m b/Classes/IRC/IRCWorld.m index f2da32b8d8..c048d499ba 100755 --- a/Classes/IRC/IRCWorld.m +++ b/Classes/IRC/IRCWorld.m @@ -52,9 +52,11 @@ - (id)init { if ((self = [super init])) { self.clients = [NSMutableArray new]; - - self.frontmostViewMessageQueue = dispatch_queue_create("frontmostViewMessageQueue", NULL); - self.backgroundViewMessageQueue = dispatch_queue_create("backgroundViewMessageQueue", NULL); + + self.messageOperationQueue = [NSOperationQueue new]; + self.messageOperationQueue.name = @"IRCWordMessageOperationQueue"; + self.messageOperationQueue.maxConcurrentOperationCount = 1; +// Only 1 at a time or else we get a race condition and out of order messages } return self; @@ -63,12 +65,6 @@ - (id)init - (void)dealloc { [NSBundle deallocBundlesFromMemory:self]; - - dispatch_release(self.frontmostViewMessageQueue); - self.frontmostViewMessageQueue = nil; - - dispatch_release(self.backgroundViewMessageQueue); - self.backgroundViewMessageQueue = nil; } - (void)setup:(IRCWorldConfig *)seed @@ -151,47 +147,6 @@ - (void)setChannelMenuItem:(NSMenuItem *)item self.channelMenu = [item.submenu copy]; } -#pragma mark - -#pragma mark View Run Loop - -- (void)fireMessageQueue:(TVCLogController *)log -{ - if (log.normalMessageQueue.count >= 25 || - log.specialMessageQueue.count >= 25) { - - static dispatch_once_t once; - - /* Not 100% sure dispatch_once is - designed to do something like this. */ - dispatch_once(&once, ^{ - [log runMessageQueueLoop]; - }); - } else { - [log runMessageQueueLoop]; - } -} - -- (void)runMessageQueueLoop:(TVCLogController *)sender -{ - if (sender.queueInProgress) { - return; // Do not even add to queue if its already running… - } - - TVCLogController *active = self.selected.log; - - if (PointerIsNotEmpty(active)) { - if (NSDissimilarObjects(sender, self.selected.log)) { - dispatch_async(self.backgroundViewMessageQueue, ^{ - [self fireMessageQueue:sender]; - }); - } else { - dispatch_async(self.frontmostViewMessageQueue, ^{ - [self fireMessageQueue:sender]; - }); - } - } -} - #pragma mark - #pragma mark Properties @@ -1398,4 +1353,51 @@ - (void)serverListKeyDown:(NSEvent *)e [self logKeyDown:e]; } -@end \ No newline at end of file +@end + +@implementation TKMessageBlockOperation ++ (TKMessageBlockOperation *) operationWithBlock:(void(^)(void))block + forController:(TVCLogController *)controller + withSpecialPriority:(BOOL)special +{ + TKMessageBlockOperation * retval = [TKMessageBlockOperation new]; + retval.completionBlock = block; + retval.controller = controller; + retval.special = special; + retval.queuePriority = retval.priority; + return retval; +} + ++ (TKMessageBlockOperation *) operationWithBlock:(void(^)(void))block + forController:(TVCLogController *)controller +{ return [self operationWithBlock:block forController:controller withSpecialPriority:NO]; } + ++ (TKMessageBlockOperation *) operationWithBlock:(void(^)(void))block +{ return [self operationWithBlock:block forController:nil withSpecialPriority:NO]; } + +- (id) init +{ + if (self = [super init]) { + self.special = NO; + } + return self; +} + +- (NSOperationQueuePriority) priority +{ + if (!self.controller) return NSOperationQueuePriorityVeryHigh; + id target = self.controller.channel ?: self.controller.client; + id selected = self.controller.world.selected; + NSOperationQueuePriority retval = NSOperationQueuePriorityLow; + + if ((target || selected) && target == selected) retval += 4L; + if (self.special) retval += 4L; + return retval; +} + +- (BOOL) isReady +{ + if (!self.controller) return YES; + return ([self.controller.view isLoading] == NO); +} +@end diff --git a/Classes/Views/Channel View/TVCLogController.m b/Classes/Views/Channel View/TVCLogController.m index e5326df877..8db6497992 100755 --- a/Classes/Views/Channel View/TVCLogController.m +++ b/Classes/Views/Channel View/TVCLogController.m @@ -51,8 +51,6 @@ - (id)init self.bottom = YES; self.maxLines = 300; - self.normalMessageQueue = [NSMutableArray new]; - self.specialMessageQueue = [NSMutableArray new]; self.highlightedLineNumbers = [NSMutableArray new]; [[WebPreferences standardPreferences] setCacheModel:WebCacheModelDocumentViewer]; @@ -127,86 +125,6 @@ - (void)setUp self.view.shouldUpdateWhileOffscreen = NO; [self loadAlternateHTML:[self initialDocument:nil]]; - - self.queueInProgress = NO; -} - -#pragma mark - - -- (void)runMessageQueueLoop -{ - self.queueInProgress = YES; - - [self runMessageQueueLoop:self.specialMessageQueue honorLoopDelay:NO]; - [self runMessageQueueLoop:self.normalMessageQueue honorLoopDelay:YES]; - - self.queueInProgress = NO; -} - -- (void)runMessageQueueLoop:(NSMutableArray *)messageQueue honorLoopDelay:(BOOL)delayLoop -{ - NSMutableString *result; - - if (delayLoop == NO) { - result = [NSMutableString string]; - } - - while (NSObjectIsNotEmpty(messageQueue)) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (NSObjectIsNotEmpty(messageQueue)) { - if ([self.view isLoading] == NO) { - // Internally, TVCLogMessageBlock should only return a - // BOOL as NSValue or NSString absolute value. - - BOOL rrslt = NO; - - // ---- // - - id stslt = ((TVCLogMessageBlock)messageQueue[0])(); - - // ---- // - - if ([stslt isKindOfClass:NSString.class]) { - if (PointerIsNotEmpty(stslt)) { - if (delayLoop == NO) { - [result appendString:stslt]; - } else { - [self appendToDocumentBody:stslt]; - } - - rrslt = YES; - } - } else { - rrslt = [stslt boolValue]; - } - - // ---- // - - if (rrslt) { - [messageQueue removeObjectAtIndex:0]; - } - } - } - }); - - // ---- // - - if (delayLoop) { - if (self.channel) { - [NSThread sleepForTimeInterval:[TPCPreferences viewLoopChannelDelay]]; - } else { - [NSThread sleepForTimeInterval:[TPCPreferences viewLoopConsoleDelay]]; - } - } - } - - // ---- // - - if (delayLoop == NO && NSObjectIsNotEmpty(result)) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self appendToDocumentBody:result]; - }); - } } #pragma mark - @@ -331,9 +249,7 @@ - (void)setTopic:(NSString *)topic return @(YES); } copy]; - [self.normalMessageQueue safeAddObject:messageBlock]; - - [self.world runMessageQueueLoop:self]; + [self enqueueMessageBlock:messageBlock fromSender:self]; } #pragma mark - @@ -438,10 +354,7 @@ - (void)mark return @(YES); } copy]; - - [self.normalMessageQueue safeAddObject:messageBlock]; - - [self.world runMessageQueueLoop:self]; + [self enqueueMessageBlock:messageBlock fromSender:self]; } - (void)unmark @@ -464,10 +377,7 @@ - (void)unmark return @(YES); } copy]; - - [self.normalMessageQueue safeAddObject:messageBlock]; - - [self.world runMessageQueueLoop:self]; + [self enqueueMessageBlock:messageBlock fromSender:self]; } - (void)goToMark @@ -989,16 +899,50 @@ - (void)writeLine:(id)line return nil; } copy]; + [self enqueueMessageBlock:messageBlock fromSender:self isSpecial:isSpecial]; +} + +- (void)enqueueMessageBlock:(id)messageBlock fromSender:(TVCLogController *)sender +{ [self enqueueMessageBlock:messageBlock fromSender:sender isSpecial:NO]; } + +- (void)enqueueMessageBlock:(id)messageBlock fromSender:(TVCLogController *)sender isSpecial:(BOOL)special +{ + [self.world.messageOperationQueue addOperation:[TKMessageBlockOperation operationWithBlock:^{ + [sender handleMessageBlock:messageBlock isSpecial:special]; + } forController:sender withSpecialPriority:special]]; +} + +- (void) handleMessageBlock:(id)messageBlock isSpecial:(BOOL)special +{ + // Internally, TVCLogMessageBlock should only return a + // BOOL as NSValue or NSString absolute value. + + BOOL rrslt = NO; // ---- // - if (isSpecial) { - [self.specialMessageQueue safeAddObject:messageBlock]; + __block id stslt = nil; + + dispatch_sync(dispatch_get_main_queue(), ^{ + stslt = ((TVCLogMessageBlock)messageBlock)(); + }); + + // ---- // + + if ([stslt isKindOfClass:NSString.class]) { + if (PointerIsNotEmpty(stslt)) { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [self appendToDocumentBody:stslt]; + }]; + rrslt = YES; + } } else { - [self.normalMessageQueue safeAddObject:messageBlock]; + rrslt = [stslt boolValue]; } - [self.world runMessageQueueLoop:self]; + // ---- // + + if (!rrslt) [self enqueueMessageBlock:messageBlock fromSender:self isSpecial:special]; } #pragma mark -