ObjectiveC - Avoiding deadlock while synchronous dispatch to main queue from background - objective-c

Recently I came to a point where I needed some block of code to execute always on the main thread synchronously. This block can be called from any thread. I solved this problem with the code that was already suggested in this SO answer by #Brad Larson
As the comments to this answer it is evident that the deadlock can occur, but I got into the deadlock very very easily. Please have a look at this code.
-(IBAction) buttonClicked
{
// Dispatch on the global concurrent queue async.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSString* data = [self getTheString];
NSLog(#"From Background Thread: %#", data);
};
// Dispatch on the main queue async.
dispatch_async(dispatch_get_main_queue(), ^{
NSString* data = [self getTheString];
NSLog(#"From Main Thread: %#", data);
};
}
// This method can be called from any thread so synchronize it.
// Also the code that sets the string variable based on some logic need to execute on main thread.
-(NSString*) getTheString
{
__block NSString* data = nil;
#synchronized(self)
{
// Have some code here that need to be synchronized between threads.
// .......
//
// Create a block to be executed on the main thread.
void (^blockToBeRunOnMainThread)(void) = ^{
// This is just a sample.
// Determining the actual string value can be more complex.
data = #"Tarun";
};
[self dispatchOnMainThreadSynchronously:blockToBeRunOnMainThread];
}
}
- (void) dispatchOnMainThreadSynchronously:(void(^)(void))block
{
if([NSThread isMainThread])
{
if (block)
{
block();
}
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
if (block)
{
block();
}
});
}
}
In this piece of code there are two simultaneous asynchronous requests to function getTheString (Assume you have no control over the buttonClicked method and how it calls getTheString api) . Suppose the request from global queue comes first and it is trying to run the block on the main thread synchronously, till that time background thread in waiting for main thread to execute the block synchronously, at the same time request from main queue comes and trying the acquire the lock from background thread, but as background thread in not complete main thread waiting for background thread to complete. Here we have a deadlock on main thread as main thread waiting for background thread to finish, and background thread is waiting for main thread to execute block.
If I remove the #synchronize statement everything works fine as expected. May be I don't need a #synchronize statement here but in same case you may need to have this. Or it can even happen from some other parts of the code.
I tried to search the whole net for the solution and also tried dispatch_semaphore but couldn't solve the issue. May be I am just not doing things the right way.
I assume this is classic problem of deadlock and faced by developers again and again, and probably have solved it to some extent. Can anyone help with this, or point me to right direction?

I would create a synchronous queue (NSOperationQueue would be simplest) and submit the block to be run on the main thread to that queue. The queue would dispatch the blocks in the order received, maintaining the ordering you desire. At the same time, it disassociates the synchronicity between calling the getTheString method and the dispatch to the main thread.

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

dispatch_sync() always execute block in main thread

Is there any difference between if dispatch_sync is called in 3 different queue like
1.
dispatch_sync(dispatch_get_main_queue(),^(void){
NSLog(#"this execute in main thread") // via [NSThread isMainThread]
});
2.
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void){
NSLog(#"this also execute in main thread") // via [NSThread isMainThread]
}
3.
dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_sync(queue, ^(void){
NSLog(#"this also execute in main thread") // via [NSThread isMainThread]
}
Whenever i call dispatch_sync, block executed in main thread, without considering in which queue it is dispatched. So why this function take queue as a argument as it doesn't use it. Can someone please clarify this?
dispatch_sync is a blocking operation. That is, the function will not return until the work represented in the block is completed.
When dispatched to an asynchronous queue -- like one of the global queues or a concurrent queue of your own making -- there is no reason to do anything but invoke the block on the thread that called dispatch_sync(). Even in the case of invoking the block on a synchronous queue, the dispatch_sync() is going to wait until completion anyway so, internally, it might as well stop until the rest of the work is done in the queue and then execute the block directly.
As it turns out, passing data from thread A to thread B is expensive. If the queue is in the state where execution can happen immediately, then dispatch_sync will fast path the execution by simply calling the block on the thread that dispatch_sync was called on.
And, by definition, you shouldn't care. The calling thread is blocked -- can't do a thing -- until dispatch_sync() returns.
So, really, all of this is an implementation detail. GCD is free to execute the blocks on whatever threads it deems most appropriate. It just so happens that don't context switch is often the most important rule of figuring that out.
See dispatch_sync documentation, which notes
As an optimization, this function invokes the block on the current thread when possible.
If you dispatch something synchronously, since the thread must wait for the dispatched code to complete, anyway, it will frequently run that code on the current thread. So if dispatched synchronously from the main thread, it will run on main thread. If dispatched synchronously from a background thread, it will run on that background thread.
As noted by ipmcc, a well-known exception is when a background thread dispatches something synchronously to the main thread. As the libdispatch source says:
It's preferred to execute synchronous blocks on the current thread
due to thread-local side effects, garbage collection, etc. However,
blocks submitted to the main thread MUST be run on the main thread.
For your problem: you call the dispatch_sync always in main queue, and if you wanna know why, see the following:
At first you need to pay attention to the description on of "dispatch_sync"
Submits a block to a dispatch queue for synchronous execution. Unlike dispatch_async,
this function does not return until the block has finished.
Calling this function and targeting the current QUEUE(NOT THREAD) results in deadlock.
#define logStep(step,queue) NSLog(#"step: %d at thread: %# in -- queue: %s",step,[NSThread currentThread],dispatch_queue_get_label(queue));
// call the method in main thread within viewDidLoad or viewWillAppear ...
- (void)testDispatchSync{
//let's distinctly tell the 4 queues we often use at first
self.concurrentQ = dispatch_queue_create("com.shared.concurrent", DISPATCH_QUEUE_CONCURRENT);
self.serialQ = dispatch_queue_create("com.shared.serial", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t mainQ = dispatch_get_main_queue();
dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
logStep(1,mainQ) //we're in main thread and main queue, current queue IS main queue
// do a sync in main thread & concurrent queue
dispatch_sync(_concurrentQ, ^{
logStep(2,_concurrentQ)
});
// do a sync in main thread & serial queue
dispatch_sync(_serialQ, ^{
logStep(3,_serialQ)
});
//uncommenting the following code that you'wll see a crash will occur, because current queue is main queue
// dispatch_sync(mainQ, ^{
// logStep(4, mainQ)
// });
dispatch_async(_concurrentQ, ^{
// inside of the this scope, current queue is "_concurrentQ"
logStep(11,_concurrentQ)
// using sync in any queue here will be safe!
dispatch_sync(_concurrentQ, ^{
logStep(12,_concurrentQ)
});
dispatch_sync(_serialQ, ^{
logStep(13,_concurrentQ)
});
dispatch_sync(mainQ, ^{
logStep(14,mainQ)
});
dispatch_sync(globalQ, ^{
logStep(15,globalQ)
});
// using async in any queue here will be safe!
dispatch_async(_concurrentQ, ^{
logStep(111,_concurrentQ)
});
dispatch_async(_serialQ, ^{
logStep(112,_concurrentQ)
});
dispatch_async(mainQ, ^{
logStep(113,mainQ)
});
dispatch_async(globalQ, ^{
logStep(114,globalQ)
});
});
dispatch_async(_serialQ, ^{
// inside of the this scope, current queue is "_serialQ"
logStep(21,_serialQ)
// using async in any queue except current queue here will be safe!
dispatch_sync(_concurrentQ, ^{
logStep(22,_concurrentQ)
});
dispatch_sync(mainQ, ^{
logStep(23,mainQ)
});
dispatch_sync(globalQ, ^{
logStep(24,globalQ)
});
//uncommenting the following code that you'wll see a crash will occur, because current queue is "_serialQ"
// dispatch_sync(_serialQ, ^{ //app will die at here
// logStep(25,_serialQ)
// });
});
}
So we got the conclusion:
the key problem is that thread will be blocked while "dispatch_sync" running on current queue which is a serial queue at the same time.
main queue is also a serial queue so that it explained why you cann't call dispatch_sync in main thread

Replacing performSelectorInBackground with GCD

In my iOS app I'm running a computationally intensive task on a background thread like this:
// f is called on the main thread
- (void) f {
[self performSelectorInBackground:#selector(doCalcs) withObject:nil];
}
- (void) doCalcs {
int r = expensiveFunction();
[self performSelectorOnMainThread:#selector(displayResults:) withObject:#(r) waitUntilDone:NO];
}
How can I use GCD to run an expensive calculation such that it doesn't block the main thread?
I've looked at dispatch_async and some options for the GCD queue choice but I'm too new to GCD to feel like I understand it sufficiently.
You use dispatch_async like suggested.
For example:
// Create a Grand Central Dispatch (GCD) queue to process data in a background thread.
dispatch_queue_t myprocess_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// Start the thread
dispatch_async(myprocess_queue, ^{
// place your calculation code here that you want run in the background thread.
// all the UI work is done in the main thread, so if you need to update the UI, you will need to create another dispatch_async, this time on the main queue.
dispatch_async(dispatch_get_main_queue(), ^{
// Any UI update code goes here like progress bars
}); // end of main queue code block
}); // end of your big process.
// finally close the dispatch queue
dispatch_release(myprocess_queue);
That's the general gist of it, hope that helps.

dispatch_sync on main queue hangs in unit test

I was having some trouble unit testing some grand central dispatch code with the built in Xcode unit testing framework, SenTestingKit. I managed to boil my problem done to this. I have a unit test that builds a block and tries to execute it on the main thread. However, the block is never actually executed, so the test hangs because it's a synchronous dispatch.
- (void)testSample {
dispatch_sync(dispatch_get_main_queue(), ^(void) {
NSLog(#"on main thread!");
});
STFail(#"FAIL!");
}
What is it about the testing environment that causes this to hang?
dispatch_sync runs a block on a given queue and waits for it to complete. In this case, the queue is the main dispatch queue. The main queue runs all its operations on the main thread, in FIFO (first-in-first-out) order. That means that whenever you call dispatch_sync, your new block will be put at the end of the line, and won't run until everything else before it in the queue is done.
The problem here is that the block you just enqueued is at the end of the line waiting to run on the main thread—while the testSample method is currently running on the main thread. The block at the end of the queue can't get access to the main thread until the current method (itself) finishes using the main thread. However dispatch_sync means Submits a block object for execution on a dispatch queue and waits until that block completes.
The problem in your code is that no matter whether you use dispatch_sync or dispatch_async , STFail() will always be called, causing your test to fail.
More importantly, as BJ Homer's explained, if you need to run something synchronously in the main queue, you must make sure you are not in the main queue or a dead-lock will happen. If you are in the main queue you can simply run the block as a regular function.
Hope this helps:
- (void)testSample {
__block BOOL didRunBlock = NO;
void (^yourBlock)(void) = ^(void) {
NSLog(#"on main queue!");
// Probably you want to do more checks here...
didRunBlock = YES;
};
// 2012/12/05 Note: dispatch_get_current_queue() function has been
// deprecated starting in iOS6 and OSX10.8. Docs clearly state they
// should be used only for debugging/testing. Luckily this is our case :)
dispatch_queue_t currentQueue = dispatch_get_current_queue();
dispatch_queue_t mainQueue = dispatch_get_main_queue();
if (currentQueue == mainQueue) {
blockInTheMainThread();
} else {
dispatch_sync(mainQueue, yourBlock);
}
STAssertEquals(YES, didRunBlock, #"FAIL!");
}
If you are on the main queue and synchronously wait for the main queue to be available you will indeed wait a long time. You should test to make sure you are not already on the main thread.
Will you ever get out of house if you must wait for yourself to get out house first? You guessed right! No! :]
Basically if:
You are on FooQueue. (doesn't have to be main_queue)
You call the method using sync ie in a serial way and want to execute on FooQueue.
It will never happen for same reason that you will never get out of house!
It won't ever get dispatched because it's waiting for itself to get off the queue!
To follow up, since
dispatch_get_current_queue()
is now deprecated, you can use
[NSThread isMainThread]
to see if you are on the main thread.
So, using the other answer above, you could do:
- (void)testSample
{
BOOL __block didRunBlock = NO;
void (^yourBlock)(void) = ^(void) {
NSLog(#"on main queue!");
didRunBlock = YES;
};
if ([NSThread isMainThread])
yourBlock();
else
dispatch_sync(dispatch_get_main_queue(), yourBlock);
STAssertEquals(YES, didRunBlock, #"FAIL!");
}

GCD to perform task in main thread

I have a callback which might come from any thread. When I get this callback, then I would like to perform a certain task on the main thread.
Do I need to check whether I already am on the main thread - or is there any penalty by not performing this check befora calling the code below?
dispatch_async(dispatch_get_main_queue(), ^{
// do work here
});
No, you do not need to check whether you’re already on the main thread. By dispatching the block to the main queue, you’re just scheduling the block to be executed serially on the main thread, which happens when the corresponding run loop is run.
If you already are on the main thread, the behaviour is the same: the block is scheduled, and executed when the run loop of the main thread is run.
For the asynchronous dispatch case you describe above, you shouldn't need to check if you're on the main thread. As Bavarious indicates, this will simply be queued up to be run on the main thread.
However, if you attempt to do the above using a dispatch_sync() and your callback is on the main thread, your application will deadlock at that point. I describe this in my answer here, because this behavior surprised me when moving some code from -performSelectorOnMainThread:. As I mention there, I created a helper function:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
which will run a block synchronously on the main thread if the method you're in isn't currently on the main thread, and just executes the block inline if it is. You can employ syntax like the following to use this:
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
As the other answers mentioned, dispatch_async from the main thread is fine.
However, depending on your use case, there is a side effect that you may consider a disadvantage: since the block is scheduled on a queue, it won't execute until control goes back to the run loop, which will have the effect of delaying your block's execution.
For example,
NSLog(#"before dispatch async");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"inside dispatch async block main thread from main thread");
});
NSLog(#"after dispatch async");
Will print out:
before dispatch async
after dispatch async
inside dispatch async block main thread from main thread
For this reason, if you were expecting the block to execute in-between the outer NSLog's, dispatch_async would not help you.
No you don't need to check if you're in the main thread. Here is how you can do this in Swift:
runThisInMainThread { () -> Void in
runThisInMainThread { () -> Void in
// No problem
}
}
func runThisInMainThread(block: dispatch_block_t) {
dispatch_async(dispatch_get_main_queue(), block)
}
Its included as a standard function in my repo, check it out: https://github.com/goktugyil/EZSwiftExtensions