Threadsafe function help
Threadsafe function help
- Subject: Threadsafe function help
- From: Ken Tozier <email@hidden>
- Date: Fri, 1 May 2009 00:50:37 -0400
Hi
I'm trying to write a general purpose console-like view and have most
of the parts working except the convenience functions for adding
attributed strings to an NSTextView subclass. Basically, what I want
is to have a couple of functions that can be used like NSLog, but am
finding that my functions seem to halt execution when they a re called
from inside threads other than the main thread. I'm using
"performSelectorOnMainThread:withObject:waitUntilDone:" to append the
text to the NSTextView but this doesn't seem to be enough. Something
I'm doing is causing a block somewhere when more than one thread tries
to write to the NSTextView. If only one thread is created then my log
function seems to work, but when two or more threads try to use the
log functions, it locks up.
For the basic threading, I'm using NSOperationQueues and
NSInvocationOperations and everything runs as expected when I use
NSLog, but when I try to call my KCLog function, execution stops.
Could someone point out what I'm doing wrong in my log functions?
Here's the code that creates new operations and generates new ones
when the operation completes. This code seems to work corectly when I
replace my log functions with NSLog, but it's included in case there
is some subtle (or not so subtle error) in how I'm using the queue/
operations.
- (void) updateOperation
{
operation = [[[NSInvocationOperation alloc]
initWithTarget: self
selector:@selector(updateDirectories)
object: nil]
autorelease];
[operation addObserver: self
forKeyPath: @"isFinished"
options: NSKeyValueObservingOptionNew
context: NULL];
[operationQueue addOperation: operation];
}
- (void) observeValueForKeyPath: inKeyPath
ofObject: inObject
change: inChange
context: inContext
{
if ([inKeyPath isEqualToString: @"isFinished"])
{
[operation removeObserver: self forKeyPath: @"isFinished"];
sleep(catalogInterval);
[self updateOperation];
}
}
Here are the log functions:
void KCLog(id inMessage)
{
[[KSharedConsole sharedConsole] addMessage: inMessage];
}
void KCLogColor(id inMessage, NSColor *inColor)
{
[[KSharedConsole sharedConsole] addMessage: inMessage color: inColor];
}
Here are the shared console methods called by the functions
- (void) addMessage:(id) inMessage
{
// not sure if I need the synchronize, but I want it to work in
threads and the more user friendly NSOperations
// so I added it
@synchronized(self)
{
[consoleView addMessage: inMessage];
}
}
- (void) addMessage:(id) inMessage
color:(NSColor *) inColor
{
@synchronized(self)
{
[consoleView addMessage: inMessage color: inColor];
}
}
Here is the console view interface
@interface KConsoleView : NSScrollView
{
NSTextView *textView;
NSDictionary *timeAttributes;
NSMutableDictionary *messageAttributes;
NSString *timestampFormat;
unsigned int maxLength;
}
Here are the console view methods called by the shared console
- (void) addMessage:(id) inMessage
{
[self appendTime: [self styledTime] message: [self styledMessage:
inMessage color: nil]];
}
- (void) addMessage:(id) inMessage
color:(NSColor *) inColor
{
[self appendTime: [self styledTime] message: [self styledMessage:
inMessage color: inColor]];
}
And finally, here is the console view "appendTime:message:color"
method that actually adds the message to the [textView textStorage]
object
- (void) appendTime:(NSAttributedString *) inTime
message:(NSAttributedString *) inMessage
{
NSMutableAttributedString *styledLine =
[[[NSMutableAttributedString alloc] init] autorelease];
// don't let the string get too big
if ([[textView textStorage] length] > maxLength)
{
// time to flush console
NSMutableAttributedString *styledFlush =
[[[NSMutableAttributedString alloc] init] autorelease];
NSMutableDictionary *tempStyle = [messageAttributes mutableCopy];
[tempStyle setObject: [NSColor redColor] forKey:
NSForegroundColorAttributeName];
NSAttributedString *flushMessage = [[[NSAttributedString alloc]
initWithString: @"Flushed console\r"
attributes: tempStyle]
autorelease];
[styledFlush appendAttributedString: inTime];
[styledFlush appendAttributedString: flushMessage];
// replace the entire contents with the flush message
[[textView textStorage]
performSelectorOnMainThread:
@selector(setAttributedString:)
withObject: styledFlush
waitUntilDone: YES];
}
// build user message
[styledLine appendAttributedString: inTime];
[styledLine appendAttributedString: inMessage];
// add user message to text storage
[[textView textStorage]
performSelectorOnMainThread:
@selector(appendAttributedString:)
withObject: styledLine
waitUntilDone: YES];
// adjust scroll
[textView scrollRangeToVisible: NSMakeRange([[textView textStorage]
length], 0)];
[textView display];
}
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden