Working of NSCondition lock - cocoa

I have been using NSCondition lately, and I really don't understand the lock and unlock part.
For example,
NSCondition lock = [NSCondition new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Do stuff
[lock signal];
};
[lock wait];
//Do rest
This is working fine. What difference does it make from this..
NSCondition lock = [[NSCondition alloc] init];
[lock lock];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Do stuff
[lock signal];
[lock unlock];
};
[lock wait];
//Do rest

You must hold the lock on a condition variable before waiting.
Waiting releases the lock, then is guaranteed to block until it has been signalled and the lock has been re-taken. Note that if multiple threads are waiting, only one will be notified of the signal, the others will continue to wait for further signals. Alternatively, if the predicate will remain true after execution, then the signal can be broadcast to all threads.
The lock is a mutual exclusion lock (mutex), meaning only one thread can hold the lock at any one time. As well as using this to protect whatever you're using the condition variable for, it also protects the internal workings of the condition variable. If you attempted to use it without the lock then there would be race conditions on checking/setting the condition across multiple threads.
For correct use of a condition variable, neither of your examples are correct. The whole point is to be protecting the state of a predicate. Here is an example is based on the pseudo-code from the documentation.
NSCondition condvar = [NSCondition new];
__block BOOL workRequired = NO;
// something somewhere else does this at some point ^{
[condvar lock];
workRequired = YES;
[condvar signal];
[condvar unlock];
};
[condvar lock];
while (!workRequired) {
[condvar wait];
}
// Do the work
workRequired = NO;
[condvar unlock];
In your example, you don't have any condition beyond "the block finished". In which case you should just use dispatch_sync.
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do stuff
};
// Do rest
If for some reason you don't know if you're already running on that queue, then a semaphore will suffice.
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0L);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do stuff
dispatch_semaphore_signal(semaphore);
};
dispatch_semaphore_wait(semaphore);
// Do rest

Related

How to read all remaining output of readInBackgroundAndNotify after NSTask has ended?

I'm invoking various command line tools via NSTask. The tools may run for several seconds, and output text constantly to stdout. Eventually, the tool will terminate on its own. My app reads its output asynchronously with readInBackgroundAndNotify.
If I stop processing the async output as soon as the tool has exited, I will often lose some of its output that hasn't been delivered by then.
Which means I have to wait a little longer, allowing the RunLoop to process pending read notifications. How do I tell when I've read everything the tool has written to the pipe?
This problem can be verified in the code below by removing the line with the runMode: call - then the program will print that zero lines were processed. So it appears that at the time the process has exited, there's already a notification in the queue that is waiting to be delivered, and that delivery happens thru the runMode: call.
Now, it might appear that simply calling runMode: once after the tool's exit may be enough, but my testing shows that it isn't - sometimes (with larger amounts of output data), this will still only process parts of the remaining data.
Note: A work-around such as making the invoked tool outout some end-of-text marker is not a solution I seek. I believe there must be some proper way to do this, whereby the end of the pipe stream is signalled somehow, and that's what I'm looking for in an answer.
Sample Code
The code below can be pasted into a new Xcode project's AppDelegate.m file.
When run, it invokes a tool that generates some longer output and then waits for the termination of the tool with waitUntilExit. If it would then immediately remove the outputFileHandleReadCompletionObserver, most of the tool's output would be missed. By adding the runMode: invocation for the duration of a second, all output from the tool is received - Of course, this timed loop is less than optimal.
And I would like to keep the runModal function synchronous, i.e. it shall not return before it has received all output from the tool. It does run in its own tread in my actual program, if that matters (I saw a comment from Peter Hosey warning that waitUntilExit would block the UI, but that would not be an issue in my case).
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self runTool];
}
- (void)runTool
{
// Retrieve 200 lines of text by invoking `head -n 200 /usr/share/dict/words`
NSTask *theTask = [[NSTask alloc] init];
theTask.qualityOfService = NSQualityOfServiceUserInitiated;
theTask.launchPath = #"/usr/bin/head";
theTask.arguments = #[#"-n", #"200", #"/usr/share/dict/words"];
__block int lineCount = 0;
NSPipe *outputPipe = [NSPipe pipe];
theTask.standardOutput = outputPipe;
NSFileHandle *outputFileHandle = outputPipe.fileHandleForReading;
NSString __block *prevPartialLine = #"";
id <NSObject> outputFileHandleReadCompletionObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleReadCompletionNotification object:outputFileHandle queue:nil usingBlock:^(NSNotification * _Nonnull note)
{
// Read the output from the cmdline tool
NSData *data = [note.userInfo objectForKey:NSFileHandleNotificationDataItem];
if (data.length > 0) {
// go over each line
NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *lines = [[prevPartialLine stringByAppendingString:output] componentsSeparatedByString:#"\n"];
prevPartialLine = [lines lastObject];
NSInteger lastIdx = lines.count - 1;
[lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL * _Nonnull stop) {
if (idx == lastIdx) return; // skip the last (= incomplete) line as it's not terminated by a LF
// now we can process `line`
lineCount += 1;
}];
}
[note.object readInBackgroundAndNotify];
}];
NSParameterAssert(outputFileHandle);
[outputFileHandle readInBackgroundAndNotify];
// Start the task
[theTask launch];
// Wait until it is finished
[theTask waitUntilExit];
// Wait one more second so that we can process any remaining output from the tool
NSDate *endDate = [NSDate dateWithTimeIntervalSinceNow:1];
while ([NSDate.date compare:endDate] == NSOrderedAscending) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
[[NSNotificationCenter defaultCenter] removeObserver:outputFileHandleReadCompletionObserver];
NSLog(#"Lines processed: %d", lineCount);
}
It's quite simple. In the observer block when data.length is 0 remove the observer and call terminate.
The code will continue after the waitUntilExit line.
- (void)runTool
{
// Retrieve 20000 lines of text by invoking `head -n 20000 /usr/share/dict/words`
const int expected = 20000;
NSTask *theTask = [[NSTask alloc] init];
theTask.qualityOfService = NSQualityOfServiceUserInitiated;
theTask.launchPath = #"/usr/bin/head";
theTask.arguments = #[#"-n", [#(expected) stringValue], #"/usr/share/dict/words"];
__block int lineCount = 0;
__block bool finished = false;
NSPipe *outputPipe = [NSPipe pipe];
theTask.standardOutput = outputPipe;
NSFileHandle *outputFileHandle = outputPipe.fileHandleForReading;
NSString __block *prevPartialLine = #"";
[[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleReadCompletionNotification object:outputFileHandle queue:nil usingBlock:^(NSNotification * _Nonnull note)
{
// Read the output from the cmdline tool
NSData *data = [note.userInfo objectForKey:NSFileHandleNotificationDataItem];
if (data.length > 0) {
// go over each line
NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *lines = [[prevPartialLine stringByAppendingString:output] componentsSeparatedByString:#"\n"];
prevPartialLine = [lines lastObject];
NSInteger lastIdx = lines.count - 1;
[lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL * _Nonnull stop) {
if (idx == lastIdx) return; // skip the last (= incomplete) line as it's not terminated by a LF
// now we can process `line`
lineCount += 1;
}];
} else {
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadCompletionNotification object:nil];
[theTask terminate];
finished = true;
}
[note.object readInBackgroundAndNotify];
}];
NSParameterAssert(outputFileHandle);
[outputFileHandle readInBackgroundAndNotify];
// Start the task
[theTask launch];
// Wait until it is finished
[theTask waitUntilExit];
// Wait until all data from the pipe has been received
while (!finished) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.0001]];
}
NSLog(#"Lines processed: %d (should be: %d)", lineCount, expected);
}
The problem with waitUntilExit is that it doesn't always behave the way one might think. The following is mentioned in the documenation:
waitUntilExit does not guarantee that the terminationHandler
block has been fully executed before waitUntilExit returns.
It appears this is precisely the problem you are having; it's a race condition. The waitUntilExit is not waiting long enough and the lineCount variable is reached before the NSTask completes. The solution would likely be to use a semaphore or dispatch_group, although it's unclear if you want to go that route — this is not an easy problem to resolve it seems.
*I experienced a similar issue from months back that still isn't resolved unfortunately.

How do you wait for an application to close in OS X?

I am using the code below to check if an application is running and close it. Can someone provide an example of how to request an application calose and wait for it to close before proceeding?
+ (BOOL)isApplicationRunningWithName:(NSString *)applicationName {
BOOL isAppActive = NO;
NSDictionary *aDictionary;
NSArray *selectedApps = [[NSWorkspace sharedWorkspace] runningApplications];
for (aDictionary in selectedApps) {
if ([[aDictionary valueForKey:#"NSApplicationName"] isEqualToString: applicationName]) {
isAppActive = YES;
break;
}
}
return isAppActive;
}
+ (void)stopApplication:(NSString *)pathToApplication {
NSString *appPath = [[NSWorkspace sharedWorkspace] fullPathForApplication:pathToApplication];
NSString *identifier = [[NSBundle bundleWithPath:appPath] bundleIdentifier];
NSArray *selectedApps = [NSRunningApplication runningApplicationsWithBundleIdentifier:identifier];
// quit all
[selectedApps makeObjectsPerformSelector:#selector(terminate)];
}
You can use Key-Value Observing to observe the terminated property of each running application. This way, you'll get notified when each application terminates, without having to poll.
One way would be to periodically call isApplicationRunningWithName on a timer, and wait until that function returns NO.
The commandline timelimit will let you send a close signal to an app, wait x seconds, then kill it (or send any other signal you like, kill is -9) if hasn't obeyed the "warning" signal.
(Note: I haven't tried compiling it on Mac, but I believe it's fairly POSIX-compliant code and not Linux-specific as it runs on BSD and others.)

When will NSautorelease release this?

Okay say I have a function like below:
-(NSNumber *)calculate{
NSNumber *myNum = [[[NSNumber alloc]initWithInt:5] autorelease];
return myNum;
}
When will myNum be released? Will whenever I call calculate, myNum will be created and added to the stack?
Also say I have a property like:
#property (nonatomic, retain) NSMutableArray *inputsArr;
and I synthesized it as:
#synthesize inputsArr = _inputsArr;
and I alloc and initiate it in the code of one of mu functions..
How would I go about releasing this memory? any guides to CoaCoa memory management...I can only find really confusing or obvious guides..
Thanks in advance
autorelease pools are thread local stacks -- you push and pop them. the deferred release message will be sent to the object when the pool is destroyed.
consider this:
NSAutoreleasePool * pool = [NSAutoreleasePool new];
NSNumber * n = [NSNumber numberWithDouble:1.0/17.0]; << n is autoreleased
[n self]; << OK!
[pool release]; << n is messaged release
[n self]; << BAM!
So 'when' really depends on how the autorelease pools are built -- but it's always possible to ensure your objects outlive a local pool, so this is never a restriction:
NSAutoreleasePool * pool = [NSAutoreleasePool new];
NSNumber * n = [NSNumber numberWithDouble:1.0/17.0]; << n is autoreleased
[n self]; << OK!
[n retain];
[pool release]; << n is messaged release
[n self]; << OK!
[n release];
[n self]; << BAM!
The above is what you should rely on. In some real world cases, an object may still be alive where you would expect "BAM!", but you should never rely on "well, it should have been destroyed, but it seems to work alright".
The NSApplication class sets up autorelease pools (instances of the NSAutoreleasePool class) during initialization and inside the event loop—specifically, within its initialization (or sharedApplication) and run methods. generally autorelease pool is popped at the end of the event loop, but this depends on you or the app.
If you are going to be using lots of temporary objects (autoreleased / from convenience methods) you may want to think about creating your own short-term autorelease pools to avoid temporary memory peaks.Autorelease objects are added to the latest autorelease pool to be created.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // create your own little autorelease pool
// these objects get added to the autorelease pool you created above
NSNumber *aNumber1 = [NSNumber numberWithFloat:1]; // refcount is 1, you are not owner, will be automatically released
NSNumber *aNumber2 = [NSNumber numberWithFloat:2]; // refcount is 1, you are not owner, will be automatically released
NSNumber *aNumber3 = [NSNumber numberWithFloat:3]; // refcount is 1, you are not owner, will be automatically released
NSNumber *aNumber4 = [NSNumber numberWithFloat:4]; // refcount is 1, you are not owner, will be automatically released
NSNumber *aNumber5 = [NSNumber numberWithFloat:5]; // refcount is 1, you are not owner, will be automatically released
NSNumber *aNumber6 = [NSNumber numberWithFloat:6]; // refcount is 1, you are not owner, will be automatically released
// ... do a bunch of stuff with all objects above.
...
[pool release]; // all objects added to this pool (the ones above) are released
Take a look at Memory Management with Objective C / Cocoa / iPhone.

Sheets and long running tasks

I need to run a complex (ie long) task after the user clicks on a button.
The button opens a sheet and the long running operation is started using dispatch_async and other Grand Central Dispatch stuff.
I've written the code and it works fine but I need help to understand if I've done everything correctly or if I've ignored (due to my ignorance) any potential problem.
The user clicks the button and opens sheet, the block contains the long task (in this example it only runs a for(;;) loop
The block contains also the logic to close the sheet when task completes.
-(IBAction)openPanel:(id)sender {
[NSApp beginSheet:panel
modalForWindow:[self window]
modalDelegate:nil
didEndSelector:NULL
contextInfo:nil];
void (^progressBlock)(void);
progressBlock = ^{
running = YES; // this is a instance variable
for (int i = 0; running && i < 1000000; i++) {
[label setStringValue:[NSString stringWithFormat:#"Step %d", i]];
[label setNeedsDisplay: YES];
}
running = NO;
[NSApp endSheet:panel];
[panel orderOut:sender];
};
//Finally, run the block on a different thread.
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue,progressBlock);
}
The panel contains a Stop button that allows user to stop the task before its completion
-(IBAction)closePanel:(id)sender {
running = NO;
[NSApp endSheet:panel];
[panel orderOut:sender];
}
This code has a potential problem where it sets value of the status text. Basically all objects in AppKit are only allowed to be called from the main thread and can break in weird ways if they're not. You're calling the setStringValue: and setNeedsDisplay: methods on the label from whatever thread the global queue is running on. To fix this you should write the loop like so:
for (int i = 0; running && i < 1000000; i++) {
dispatch_async(dispatch_get_main_queue(), ^{
[label setStringValue:[NSString stringWithFormat:#"Step %d", i]];
[label setNeedsDisplay: YES];
});
}
This will set the label text from the main thread as AppKit expects.

Making an NSMutableString transformation without leaking memory?

I have this function within an iPhone project Objective C class.
While it's correct in terms of the desired functionality, after a few calls, it crashes into the debugger.
So I think it's a case of bad memory management, but I'm not sure where.
- (NSString *)stripHtml:(NSString *)originalText {
// remove all html tags (<.*>) from the originalText string
NSMutableString *strippedText = [[NSMutableString alloc] init];
BOOL appendFlag = YES;
for( int i=0; i<[originalText length]; i++ ) {
NSString *current = [originalText substringWithRange:NSMakeRange(i, 1)];
if( [current isEqualTo:#"<"] )
appendFlag = NO;
if( appendFlag )
[strippedText appendString:current];
if( [current isEqualTo:#">"] )
appendFlag = YES;
}
NSString *newText = [NSString stringWithString:strippedText];
[strippedText release];
return newText;
}
Every time you iterate over your for loop, you're allocating a new NSString. While these NSStrings are autoreleased, they won't actually be released until after all the processing of your last input is finished. In the meantime, you'll allocate a potentially infinite amount of memory. The solution is to create your own autorelease pool and drain it every trip through the for loop. It'll look something like this:
BOOL appendFlag = YES;
for( int i=0; i<[originalText length]; i++ ) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
// rest of for loop body
[pool drain];
}
That'll free up the memory used by your current pointer right away.

Resources