What's the good way to synchronize calls to a NSView? - multithreading

I have a view that receives new data from a secondary thread. Every time it does, it should redraw itself. However, it doesn't play nice with the run loop, and after some time (it's non-deterministic), I end up getting <Error>: kCGErrorIllegalArgument: CGSUnionRegionWithRect : Invalid region messages in the console.
I'm not sure what's the right way to synchronize the calls to [view setNeedsDisplay:YES] across threads; can you help me?
To clarify a little, thread B (actually a dispatch queue) gives new contents to a view by calling this:
-(void)setImageBuffer:(unsigned char*)buffer
{
/* image handling stuff; thread-safe */
[self setNeedsDisplay:YES]; // but this is not thread-safe
}
And then thread A, on which runs the run loop, should redisplay the view.

-(void)setImageBuffer:(unsigned char*)buffer
{
/* image handling stuff; thread-safe */
[self performSelectorOnMainThread:#selector(induceRedraw)
withObject:nil
// Don't just copy this; pick one...
waitUntilDone:YES or NO];
}
-(void)induceRedraw
{
[self setNeedsDisplay:YES]; // but this is not thread-safe
}

With GCD you don't need the extra proxy method:
dispatch_queue_t q = dispatch_get_main_queue();
dispatch_async(q, ^(void) {
[self setNeedsDisplay: YES];
});

Related

GCD with runloops and background refresh of app, iOS7

Maybe I mix too many different technologies together and run in some roadblock; some advise would be much appreciated.
I have an app which connects to several server; each connection with one input and output socket stream. The connection goes to a defined port and is close to telnet protocol. Text input/output. quite simple.
First I have an openStream function as wrapper called from main thread which create a client-specific GDC queue and dispatch the input/output-stream creation within that queue asynchronously:
gcdQueue = dispatch_queue_create([self.client.hostName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
// possible priorities:
// DISPATCH_QUEUE_PRIORITY_HIGH
// DISPATCH_QUEUE_PRIORITY_DEFAULT
// DISPATCH_QUEUE_PRIORITY_LOW
if (self.runASync)
{
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
dispatch_async(gcdQueue, ^{
[self openStreamsInternal];
});
}
Code for the technical open of steams
...
//
// in openStreamsInternal()
//
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.client.hostName, [self.client.hostPort intValue], &_readStream, &_writeStream);
self.inputStream = (__bridge_transfer NSInputStream *)_readStream;
self.outputStream = (__bridge_transfer NSOutputStream *)_writeStream;
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
self.runLoop = [NSRunLoop currentRunLoop];
[self.inputStream scheduleInRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[self.outputStream open];
// ... some lines later
if (self.runASync && (self.inputStream || self.outputStream))
{
[self.runLoop run];
}
I open both socket streams and link them to a runloop within the GCD-queue (assuming it will indirectly create a thread; not sure if that is always guaranteed).
Then via the delegate (a member function of my connection class) for the streams in
Code:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
I do what I have to do with in data flooding in. No issues until I stay in foreground. I close the streams when I go into background to release the resources.
Now with iOS 7 I want to enable background refresh for the streams. For that i don't close the streams anymore when moving into background and have the notification code in
Code:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"called in background for data fetch");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (Connection *connection in self.document.clientList)
{
// Add a task to the group
[connection parseResponseInQueue:group];
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(#"finished with background for data fetch");
completionHandler(UIBackgroundFetchResultNewData);
}
This is one of my variants of background processing; not working well. This one supposed to wait a second and check if the input stream has data copied. If thats the case the parser would be called and the method comes to and end; removing one item from the dispatch group created in the iOS7 background app notification.
I don't like the dispatch_after as it seems very brute; but without I run in an endless loop as the streams seems not be triggered at all times.
Code:
- (void)parseResponseInQueue:(dispatch_group_t)group
{
if (gcdQueue != nil)
{
dispatch_group_async(group, gcdQueue, ^{
while ([self.data length] > 0)
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), gcdQueue, ^{
NSLog(#"%#, wait for parser in background", self.client.hostName);
});
}
#if 0
if ([self.data length] > 0)
{
NSLog(#"%#, start working on buffer %d from background", self.client.hostName, [self.data length]);
[self parseResponse];
NSLog(#"%#, finish working on buffer, left %d in background", self.client.hostName, [self.data length]);
// NSLog(#"data : %#", [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]);
}
else
{
NSLog(#"%#, no data for background processing", self.client.hostName);
}
#endif
});
}
}
But somehow I don't get the refresh done. Sometimes the completionHandler finish without any update and sometime my dispatch group never finish.
So my question is mainly:
1) what is your suggestion to combine background app refresh with multiple streams in GCD queues.
2) does those runloops still be active when I trigger in background
3) are the GCD queues still active
4) should I better schedule in one runloop for all client connection in addition to one main runloop ?
Somehow I need to fresh thoughts on the way forward.
TIA
I have been working on a cross platform real time multiplayer game and ran into similar problems. The core issue I faced was with multiple threads competing for compute cycles and bottlenecks while trying to update common resources. I also had a bunch of scheduled threads that updated my game state.
I ended up switching to a single run loop for all my connections and background scheduled events. That helped in clearly identifying and properly isolating critical sections. This also simplified my control flow. I also saw an increase in performance since there were only 4 threads now that were competing for cycles instead of the earlier 20+
So for your Q4 I recommend that you switch to a single schedule.
For Q1 again, A single combined process for app refresh will not only speed up your app but make it easier to manage and debug.
For Q2 and Q3 It's best if you tested it out on your code by using logs as there may be factors involved that are not apparent in the code you shared.

dispatch_async not working with background thread

I am having some heavy content to be loaded from data base and showed on to the Screen.
But at the same time there is a background task of 'plist to Data base' saving is going on.
Hence the UI gets stuck.
The code I am using as bellow,
For Data base fetch and UI update method
- (void)performBlockInBackground:(dispatch_block_t)taskBlock
completion:(dispatch_block_t)completionBlock
withPriotity:(dispatch_queue_priority_t)piority
{
__block dispatch_block_t taskBlockRef = taskBlock;
__block dispatch_block_t completionBlockRef = completionBlock;
dispatch_async(dispatch_get_global_queue(piority, 0), ^{
dispatch_sync(dispatch_get_global_queue(piority, 0), taskBlockRef);
dispatch_async(dispatch_get_main_queue(), completionBlockRef);
});
}
And I am calling the 'Plist to DB' method as
[self performSelectorInBackground:#selector(syncData) withObject:nil];
here the taskBlock is the heavy DB fetch and completionBlock is the UI Update.
If I call this method after some time (after the syncData method is done) then it updates smoothly. But if its not completed yet then the UI gets stuck.
It seems to be the issue of the thread deadlock but not clear on this
Please help !!
You don't need dispatch_sync() when you're already in the right queue. You can call the block like so: taskBlock().
- (void)performBlockInBackground:(dispatch_block_t)taskBlock
withPriority:(dispatch_queue_priority_t)priority
completionOnMainThread:(dispatch_block_t)completionBlock
{
dispatch_async(dispatch_get_global_queue(priority, 0), ^{
if (taskBlock) {
taskBlock();
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), completionBlock);
}
});
}
What happens is this:
Immediately send the work to a background thread. Now we aren't blocking the calling thread or the main thread.
If we have a taskBlock, do it now. TaskBlock is where you put your data fetch.
TaskBlock finishes.
If we have a completionBlock, send it to the main thread. CompletionBlock is where you update the UI.

Core Data background fetching via new NSPrivateQueueConcurrencyType

Is it really so simple now in iOS5?
I used to perform a background fetch using this code in my AppDelegate:
dispatch_queue_t downloadQueue = dispatch_queue_create("DownloadQueue", NULL);
dispatch_async(downloadQueue, ^{
self.myDownloadClass = [[MyDownloadClass alloc]initInManagedObjectContext:self.managedObjectContext];
[self.myDownloadClass download];
});
dispatch_release(downloadQueue);
My download class performs an NSURLConnection to fetch some XML data, uses NSXMLParser to parse the data, and then updates a complex schema in core data. I would always switch to the main thread to actually update the core data. Messy code, with lots of calls to dispatch_sync(dispatch_get_main_queue()....
My new code looks like this:
NSManagedObjectContext *child = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[child setParentContext:self.managedObjectContext];
[child performBlock:^{
self.myDownloadClass = [[MyDownloadClass alloc]initInManagedObjectContext:child];
[self.myDownloadClass download];
}];
along with a small change to some other code in my AppDelegate to set the parent model object context type to NSMainQueueConcurrencyType:
- (NSManagedObjectContext *)managedObjectContext
{
if (__managedObjectContext != nil)
{
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return __managedObjectContext;
}
It seems to work very well. The entire update process still runs in a separate thread, but I did not have to create a thread. Seems like magic.
Just remember if you want to commit your changes to the physical core data files, you have call save: on the parent context as well.
I didn't really ask a question here. I'm posting this so it helps others because everything I found when searching for the new iOS5 managed object context methods only gave high level details with no code examples And all the other searches for fetching core data in the background are old, sometimes very old, and discuss how to do it pre-iOS5.
Yes - it is really that easy now (in iOS 5.0). For iOS 4 compatibility, the previous hurdles remain, but the documentation is not too bad on thread confinement. Maybe you should add this to a wiki section?
I'm trying to understand how this new API is implemented. My usual pattern for multithreaded core data is something like this:
Usually in a NSOperation but simplified using dispatch_async in this example:
dispatch_queue_t coredata_queue; // some static queue
dispatch_async(coredata_queue, ^() {
// get a new context for this thread, based on common persistent coordinator
NSManagedObjectContext *context = [[MyModelSingleton model] threadedContext];
// do something expensive
NSError *error = nil;
BOOL success = [context save:&error];
if (!success) {
// the usual.
}
// callback on mainthread using dispatch_get_main_queue();
});
Then the main thread will respond by updating the UI based on NSManagedObjectContextDidSaveNotification to merge the main context.
The new API's seem to be a wrapper around this pattern, where the child context looks like it just takes the persistent coordinator from its parent to create a new context. And specifying NSPrivateQueueConcurrencyType on init will make sure the performBlock parameter is executed on the private queue.
The new API doesn't seem to be much less code to type. Any advantages over the 'traditional' threading?

cocos2d notification/event/message from a different thread

I have a worker thread doing calculation on the background and I want to send a event/message to call a update function to update the graphics on screen once the worker thread finish calculation.
How do I do that in cocos2d ?
Some demo code:
-(void) updateGraphic
{
//this one update all the graphics/sprite
}
//note workerThreadFunc is being used to start a new thread
-(void) workerThreadFunc
{
//...
//...
//finish calculation here
//since it's in a different thread, I cannot call updateGraphic directly here
//So I need a event to notify update Graphic here somehow
}
Cocos2D calls the -(void) draw {} method on all nodes automatically on the main thread. You do not need to call that method from another thread, and you can not perform custom OpenGL drawing outside the draw method.
In order to call a method that should be performed on the main thread, use the performSelectorOnMainThread method.
I've achieve it via pthreads, it needs to do some changes in CCDirector.cpp & CCDirector.h
the details is in this thread.
to use it, we can register handleMessageInUI in UI thread, then worker thread sends a message to UI thread, which will call handleMessageInUI to do UI drawing. some sample code is below:
In UI thread, we can register a handler to process message in UI thread.
bool HelloWorldScene::handleMessageInUIThread(const EXTCCMessage &msg) {
// implementations
// return true if this handler has processed this msg,
// otherwise, false is returned
switch (msg.msgId) {
case 2:
break;
default:
return false;
}
return true;
}
// register this Handler to UI Threader
CCDirector::mainLoopHandler()->registerHandler(this, (EXTCCHandleMessage)&HelloWorldScene::handleMessageInUIThread);
send a message to UI thread in a worker thread
EXTCCMessage msg;
msg.msgId = 2;
msg.msgData1 = NULL;
// "msg" will be processed by "handleMessageInUIThread" in UI thread
CCDirector::mainLoopHandler()->postMessage(msg);

Updating a cocoa interface while processing code in a separate thread, using performSelectorInBackground

I am writing an app that can revert the firmware of a particular device. While executing this revert code, I wish to display a progress indicator.
This problem is of course best tackled with the use of multiple threads (http://stackoverflow.com/questions/1225700/can-i-start-a-thread-by-pressing-a-button-in-a-cocoa-interface-and-keep-using-in).
I have implemented the performSelectorInBackground method, which (according to the documentation) launches the specified selector in a separate thread. Meanwhile, my GUI is updated from the main thread by querying the 'reverter' object.
However, the GUI does not seem to be updating until the code in the secondary thread has finished executing. I obviously need the two to run in parallel. Here is what I've got so far - I'd be really grateful for any help as this is my first time with threading.
-(IBAction)pushButton:(id)sender{
//instatiate reverter object, which does all the firmware processing
Reverter *reverter = [[Reverter alloc] init];
//update the GUI to show a tab with a progress indicator
[tabView selectTabViewItemWithIdentifier:#"RevertProgressTab"];
//process revert code in a separate thread
[reverter performSelectorInBackground:#selector(revertFirmware) withObject:nil];
//process is complete when reverter progress reaches 100
while (!([reverter progress] == 100)) {
//check for failure
if ([reverter hasFailed]) {
[self showRevertFailureTab:nil];
return;
}
//update the progress indicator in the interface
[revertProgressBar setDoubleValue:(double)[reverter progress]];
[NSThread sleepForTimeInterval:0.05];
}
[self showRevertSuccessTab:nil];
}
Have I done anything obvious that would stop the GUI from being updated while the revertFirmware method runs?
Your while loop
while (/*condition*/) {
[NSThread sleepForTimeInterval:x];
}
will prevent your UI from updating. Your UI will only update as soon as your pushButton: method returns.
Instead of polling I would advice you start using an asynchronous event model:
Add a delegate to your reverter object
#protocol ReverterDelegate <NSObject>
- (void) reverterProgressDidUpdate:(float)progress;
#end
#interface Reverter : NSObject {
id<ReverterDelegate> delegate;
}
#property(assign) id<ReverterDelegate> delegate;
#end
Register your controller class as a delegate to your reverter
reverter.delegate = self;
and handle that event
- (void) reverterProgressDidUpdate:(float)progress {
// update ui
}
In your background thread send out events to the main thread
- (void) revertFirmware {
// once in a while send notifications of progress updates
if ([self.delegate respondsToSelector:#selector(reverterProgressDidUpdate:)]) {
[self.delegate performSelectorOnMainThread:#selector(reverterProgressDidUpdate:) withObject:[NSNumber numerWithFloat:progress] waitUntilDone:NO];
}
}
Make sure you retain your reverter somewhere, and release it when it's done working. You are now leaking in your pushButton: method. Also this is just a suggestion towards a better model. Instead of using performSelectorInBackground you could take a look at NSOperation and NSOperationQueue for example.

Resources