Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dispatch on main queue synchronously without a deadlock?

I need to dispatch a block on the main queue, synchronously. I don’t know if I’m currently running on the main thread or no. The naive solution looks like this:

dispatch_sync(dispatch_get_main_queue(), block); 

But if I’m currently inside of a block running on the main queue, this call creates a deadlock. (The synchronous dispatch waits for the block to finish, but the block does not even start running, since we are waiting for the current one to finish.)

The obvious next step is to check for the current queue:

if (dispatch_get_current_queue() == dispatch_get_main_queue()) {     block(); } else {     dispatch_sync(dispatch_get_main_queue(), block); } 

This works, but it’s ugly. Before I at least hide it behind some custom function, isn’t there a better solution for this problem? I stress that I can’t afford to dispatch the block asynchronously – the app is in a situation where the asynchronously dispatched block would get executed “too late”.

like image 594
zoul Avatar asked Apr 26 '12 09:04

zoul


People also ask

Why main sync deadlock?

main is a serial queue which has single thread to execute all the operations. If we call "sync" on this queue it will block all other operations currently running on the thread and try to execute the code block inside sync whatever you have written. This results in "deadlock".

Should I sync on main queue?

NEVER call the sync function on the main queue. If you call the sync function on the main queue it will block the queue as well as the queue will be waiting for the task to be completed but the task will never be finished since it will not be even able to start due to the queue is already blocked.

What does dispatch queue main Async do?

main. async to avoid blocking the current thread. And potentially even deadlocking your app, which can happen if you call DispatchQueue.

What is deadlock swift 5?

In short, a deadlock can occur when the system is waiting for a resource to free up while it's logically impossible for that resource to become available. You can think of this resource as almost anything. It could be a database handle, a file on the filesystem, or even time to run code on the CPU.


1 Answers

I need to use something like this fairly regularly within my Mac and iOS applications, so I use the following helper function (originally described in this answer):

void runOnMainQueueWithoutDeadlocking(void (^block)(void)) {     if ([NSThread isMainThread])     {         block();     }     else     {         dispatch_sync(dispatch_get_main_queue(), block);     } } 

which you call via

runOnMainQueueWithoutDeadlocking(^{     //Do stuff }); 

This is pretty much the process you describe above, and I've talked to several other developers who have independently crafted something like this for themselves.

I used [NSThread isMainThread] instead of checking dispatch_get_current_queue(), because the caveats section for that function once warned against using this for identity testing and the call was deprecated in iOS 6.

like image 81
Brad Larson Avatar answered Sep 23 '22 08:09

Brad Larson