I have an iOS app, with multiple threads. In a background thread, I run some 3rd party code. The 3rd party code will occasionally call:
dispatch_sync(dispatch_get_main_queue(), block);
The callback has to be _sync, because it needs the answer and it needs to be on the main thread, because it calls UIApplication.
The problem occurs when I need to shut down the background thread. The shutdown originates from the UI and it also has to be sync. So, I sometimes see a deadlock.
I have tried to solve it, by the method described here. Basically calling the NSRunLoop in a loop, until a flag has been set by the background thread. Like this:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
However, it does not work. I can see in the debugger that the background thread is hanging on the dispatch_sync call, even though the NSRunLoop runUntilDate is called multiple times.
Here is the callstack of the background thread, which is blocked:
And here is the callstack of the main thread (it is not crashed, merely paused in the debugger):
I think it must be something about the runmodes, but I am not sure what and how to fix that. So how can I service the GCD main queue, while the main thread is busy in a loop?


That
NSRunLooptechnique can work in some situations:But this technique of manually calling
runon the main run loop while simultaneously blocking the main thread, is essentially a kludge (and a bit of an anachronism). E.g. replace the timer infinishInTenSecondswith GCD call, and this closure will never get called:The problem is that the
whileloop with theruncall will allow the run loop to process events, but it doesn’t alter the fact that the main thread is blocked by the originaldispatch_synccall.Note, the particulars in your case are undoubtedly different, but hopefully this illustrates the general class of problem introduced with the
NSRunLoopkludge.The appropriate solution is to minimize synchronous calls. E.g. if you need to do something after the task on the background queue finishes, rather than calling synchronously, like so:
You would instead dispatch asynchronously, and then, when it’s done, dispatch the subsequent code asynchronously back to the main queue: