Objective-C Threading - objective-c

I wanted to learn more about threading in objective-c, so I made a little test program that would just loop and output what iteration of the loop it is on.
However, the output I am getting is not what I am expecting. I have an idea as to why, but first here is my code:
main.m
#import <Foundation/Foundation.h>
#import "Car.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Car* myCar = [[Car new] autorelease];
[myCar performSelectorInBackground:#selector(LoopAndSay) withObject:nil];
for(int i = 0; i < 100; i++ )
{
NSLog(#"Main loop on %i", i);
}
}
return 0;
}
Car.m
#import "Car.h"
#implementation Car
#synthesize name, model;
-(void) LoopAndSay {
for(int i = 0; i < 100; i++)
{
NSLog(#"Looping for the %i time", i);
}
}
#end
Now, if I run it as is the background loop will sometimes not complete(stopping between iterations 94 and 97). Additionally, if I switch my code around so that the background loop is not called until after the main thread loop then it will not run any iterations.
Is this because the main thread is finished and does not want to wait for the background thread to run to completion? If this is the case, is there a way to force the program to continue running until both the main thread and any background threads are complete?

This is almost certainly because you exit the scope of the autorelease pool before the thread completes your main function returns before the background thread completes. Try adding sleep(3) or something before its end and see if your background thread completes.
On Mac OS X, a process terminates when all its "foreground" threads have completed. peformSelectorInBackground: only creates a background thread, so once main returns, you're left with no foreground thread and your process is terminated.

This is because all threads created with performSelectorInBackground:... and other Objective-C APIs are detached, so that the program can terminate without waiting for them to finish. From the Threading Programming Guide:
At application exit time, detached threads can be terminated immediately but joinable threads cannot. [...] If you do want to create joinable threads, the only way to do so is using POSIX threads...
In this case, your main thread loop happens to finish a bit sooner than the background thread, so the background thread loop doesn't run through to the end. Instead of using POSIX threads (which is much less convenient), you could also use NSOperationQueue, which easily allows you to wait for all operations in a queue to finish.

Related

How to switch between background and main threads

I've never used background threads before. I have a time consuming computation currently running on the main thread which appends the data output to a TERecord. My workflow essentially goes:
run long process…
update GUI…
run long process…
update GUI…
and so on.
At several places where the code produces (string) output I update the UI by calling my 'addToRecord' method shown here:
-(void)addToRecord:(NSString*)passedStr:(BOOL)updateUI
{
NSRange endRange;
// add the passed text...
endRange.location = [[theOutputView textStorage] length];
endRange.length = 0;
[theOutputView replaceCharactersInRange:endRange withString:passedStr];
if(updateUI) // immediate GUI update needed...
{
// scroll window contents to BOTTOM of page...
endRange = NSMakeRange([[theOutputView string] length],0);
[theOutputView scrollRangeToVisible:endRange];
[theOutputView display];
}
}
While it does the job, my entire UI remains unresponsive until the process completes, of course. I know I should be doing the heavy lifting on a background thread which I've never used before. I've figured out part of the problem in creating a background thread like below:
-(IBAction)readUserInput:(id)sender
{
// irrelevant code snipped for brevity
if([self checkForErrors] == NO)
{
[runButton setEnabled:NO];
[self performSelectorInBackground:#selector(runWorkThread) withObject:nil];
}
}
-(void)runWorkThread
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
[self runLongProcess];
[pool drain];
}
but i just don't understand how to call the main thread every time the code encounters my 'addToRecord' method, then how to return control to the background thread?
Another possibility might be to remove the updateUI code from my 'addToRecord' method and just have have the main thread calling this code every second or so on a timer?
Any advice and sample code would be greatly appreciated. Thanks!
Instead of using performSelectorInBackground you can use the Dispatch framework (also called GCD), which is the preferred way of handling concurrent work. The Dispatch already has a pool of background threads set up that you can use. To switch thread you call dispatch_async() like this:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
// :
// Do your background work here
// :
dispatch_async(dispatch_get_main_queue(), ^{
// :
// Now you are back in the main thread
// :
});
});
The first parameter is the queue identifier which is supplied to you by either dispatch_get_global_queue() which returns one of the "worker" queues, or dispatch_get_main_queue() which returns the main queue. The last parameter is a code block that is executed on the selected queue.
When requesting a concurrent queue using dispatch_get_global_queue() you specify a Quality of Service, which determines the priority your code will have in relation to other work. See the documentation for more information and possible values.
Read more on the Dispatch

Stopping the NSApplication main event loop

I have an application consisting of the following single .m file:
#import <Cocoa/Cocoa.h>
int main(int argc, char* argv[]) {
[[[NSThread alloc] initWithBlock: ^{
sleep(2);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Stop");
[[NSApplication sharedApplication] stop:nil];
});
}] start];
[[NSApplication sharedApplication] run];
NSLog(#"Run finished");
return 0;
}
According to the developer documentation, stop should stop the main loop (run), but it doesn't (at least not on OS X 10.12 and 10.13). There's also terminate, but this exits the program too soon. I also tried setting an NSApplicationDelegate that implements applicationShouldTerminate, but this is never called.
How can I make sure the main run loop is (cleanly) exited?
Note: The shared application main loop is necessary because there is UI work being done elsewhere. More concretely, this is giving problems in the Go WDE UI library, which uses Cocoa to provide a window to a Go application.
The documentation for -stop: says:
[C]alling this method from a timer or run-loop observer routine would not stop the run loop because they do not result in the posting of an NSEvent object.
A block dispatched to the main queue is similar in that it doesn't post an event. You can try posting an NSEventTypeApplicationDefined event after calling -stop:.
After investigating this further, it seems that the UI loop stop request is only processed after a UI event (so not just after a main loop event). So, it works in response to a UI event, but not in a thread like I did in my example.
Triggering a UI event after a stop request (e.g. a programmatic resize works for me) causes the loop to end.

Objective-C Cocoa how to correctly use run loop in GCD

I'm not sure how to correctly use GCD in a run loop situation where the thread might need to be stopped. The problem starts from the outset, and how or where to use CGEventCallback (which is absent from my code). The stop button won't stop the loop, and I don't think my dispatch queue is setup properly -- along with the while loop creating a huge lag.
I've read top question-answers from the search, like this and this, but one is for iOS and the other isn't relevant. Could someone show me how to properly do this?
my code:
// .h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSTextField *textFieldBox;
IBOutlet NSButton *stop;
}
#property (assign) IBOutlet NSWindow *window;
- (void)stop;
#end
// .m
#import "AppDelegate.h"
#implementation AppDelegate
BOOL isActive = FALSE;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self mainMethod];
}
- (void)mainMethod {
NSLog(#"loop started");
isActive = TRUE;
[self theArbitraryNonCompliantLoop];
NSLog(#"doing other stuff");
}
- (void)stop {
isActive = FALSE;
return;
}
- (void)theArbitraryNonCompliantLoop {
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
while (isActive) {
for (NSUInteger i = 0; i < 1000000; i++) {
[textFieldBox setStringValue:[NSString stringWithFormat:#"%lu",(unsigned long)i]];
}
}
});
}
#end
Ignoring the name, the for loop needs to test isActive as well. That will solve the latency issue.
The UI update needs to be done on the main thread which is easy because you can just schedule a block on the main queue to do it.
- (void)theArbitraryNonCompliantLoop {
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
while (isActive)
{
for (NSUInteger i = 0; isActive && i < 1000000; i++)
{
dispatch_async(dispatch_get_main_queue(),
^{ [textFieldBox setStringValue:[NSString stringWithFormat:#"%lu",(unsigned long)i]] };
}
}
});
}
There are still some issues here. I think, as it stands it will flood the main thread's run loop with events, so some throttling will be required. You might also consider some synchronisation for the inActive instance variable in case the compiler optimises it by pulling it into a register at the beginning of the method. Also, it will be subject to race conditions thanks to caching etc.
Big mistake: You are changing a UI element on a background thread. That will cause all kinds of problems. Don't do that.
You seem to be quite confused what a runloop is. You are also trying to confuse people by calling something "theRunLoop" that just does stuff on a background thread. Your code has nothing to do with the runloop, and until you understand what a runloop is, better keep away from it.
Why would you call an arbitrary method theRunLoop?
Either way, quoting Run Loops (Threading Programming Guide):
Both Cocoa and Core Foundation provide run loop objects to help you
configure and manage your thread’s run loop. Your application does not
need to create these objects explicitly; each thread, including the
application’s main thread, has an associated run loop object. Only
secondary threads need to run their run loop explicitly, however. The
app frameworks automatically set up and run the run loop on the main
thread as part of the application startup process.
My guess would be that your while loop is still on its first run. The 1000000 for loop is probably taking too long which is why it still seems like the loop is still running. To test it out put an NSLog after your for loop to see if it has exited it after you changed isActive to false.

monitoring ifstream read progress from separate thread in Obj-C

This is the code I'm using to write and read a file in the background using GCD.
#import "AppDelegate.h"
#import <dispatch/dispatch.h>
#import <iostream>
#import <fstream>
size_t fileSize = 1024 * 1024 * 10;
std::ofstream *osPtr = 0;
std::ifstream *isPtr = 0;
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
const float framerate = 40;
const float frequency = 1.0f/framerate;
[NSTimer scheduledTimerWithTimeInterval:frequency
target:self selector:#selector(doSomething)
userInfo:nil repeats:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
std::ofstream os("myFile", std::ios::binary);
if (os) {
osPtr = &os;
for (int i = 0; i<fileSize; i++) {
os << 'c';
}
osPtr = 0;
os.close();
printf("write done\n");
}
std::ifstream is("myFile", std::ios::binary);
if (is) {
is.seekg(0, std::ifstream::end);
fileSize = (size_t)is.tellg();
is.seekg(0, std::ifstream::beg);
isPtr = &is;
while ( is.good() )
{
char c;
is >> c;
}
isPtr = 0;
is.close();
printf("read done\n");
}
});
}
- (void)doSomething
{
// write file progress indicator
if (osPtr)
printf("%5.1f\n", (float)osPtr->tellp()/fileSize*100.0f);
// read file progress indicator
if (isPtr)
printf("%5.1f\n", (float)isPtr->tellg()/fileSize*100.0f);
}
#end
It writes ok, but when reading big files (5 mb or more) an EXEC_BAD_ACCESS error is thrown, within the streambuf class code.
template <class _CharT, class _Traits>
inline _LIBCPP_INLINE_VISIBILITY
typename basic_streambuf<_CharT, _Traits>::int_type
basic_streambuf<_CharT, _Traits>::sbumpc()
{
if (__ninp_ == __einp_)
return uflow();
return traits_type::to_int_type(*__ninp_++); //<---EXEC_BAD_ACCESS
}
This is the project test.zip
Does the documentation of std::of stream say that it is thread safe? I don't think so.
My bet would be that you always get a crash if your progress function is called while the osPtr or isPtr exists. But for small files, the writing/reading is so fast that they are both gone before your progress method is ever called.
The best way to read and write files asynchronously would be to use the GCD IO functions...
There is a convenience read function (and a similar write function).
void dispatch_read(
dispatch_fd_t fd,
size_t length,
dispatch_queue_t queue,
void (^handler)(dispatch_data_t data, int error));
Your handler block would be called back each time the system had some data ready to be read, and you could update your progress indicator.
You could use dispatch_after with the same queue, and they would be automatically seriallized (as long as you used a serial queue).
However, just to be clear: your problem is that you are accessing the stream objects from multiple threads at the same time. One thread is running the queue code block, and another is running your timer call. They are both trying to access the same stream objects. Bad news.
If you want to continue to use your method of IO, you need to serialize access in one of several ways. You can create a class that provides safe access to an IOStream across multiple threads, or you can serialize the access yourself with locks. Both C++ and Obj-C provide many synchronization APIs.
However, there is a very common idiom used in lots of apple code: the delegate.
In your case, a simple progress delegate, with a method that sends the current progress, would suffice. This way the delegate is called from within the context of the long running task, which means you have synchronized access to any shared data.
If you want, you can dispatch any GUI work to the main thread with GCD.
So this would be my approach.
I need to subclass std::streambuf, even when it can be overkill (like it is posted here or here), at least for a feature that should be quite common in multithreaded applications.

NSProgressIndicator progress with For loops?

My application does a lot of work with a bunch of For loops. It calculates a massive amount of strings, and it can take over a whole minute to finish.
So I placed a NSProgressIndicator in my app.
Within the loops, I used the "incrementBy" function of the NSProgressIndicator. However, I don't see the actual bar filling up.
I suspect that's because of the loops taking all power possible, and thus the NSProgressIndicator is not updated (graphically).
How would I make it progress then?
Are your for loops running on the main thread or in a background thread? If they're running on the main thread, the GUI will never get a chance to update itself to reflect the progress change as this will only happen at the end of the runloop, i.e. after your functions have finished running.
If your for loops are running in the background, you're being naughty! You shouldn't update the GUI from anywhere but the main thread. If you're targeting a modern system, you can use GCD to trivially work around this.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
for (int i = 0; i < n; i++) {
// do stuff
dispatch_async(dispatch_get_main_queue(), ^(void) {
// do your ui update here
});
}
});
Alternatively, you can rewrite your for loops to take advantage of GCD even further and use dispatch_apply. The equivalent of the above would be:
dispatch_apply(n, DISPATCH_QUEUE_PRIORITY_DEFAULT, ^(size_t i) {
// for loop stuff here
dispatch_async(dispatch_get_main_queue(), ^(void) {
// do your ui update here
});
});
Note that using dispatch_apply means that each "iteration" of the loop may run concurrently with respect to one another, so this won't be applicable if your for loop requires to be run in a serial fashion.