Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dispatch_sync inside dispatch_sync causes deadlock

I just read this on objc.io Going Fully Asynchronous but can't find good explanation

dispatch_queue_t queueA; // assume we have this
dispatch_sync(queueA, ^(){  // (a)
    dispatch_sync(queueA, ^(){ // (b)
        foo();
    });
});

Once we hit the second dispatch_sync we’ll deadlock: We can’t dispatch onto queueA, because someone (the current thread) is already on that queue and is never going to leave it.

As long as I understand

  1. dispatch_sync just add the work item (I avoid using the word "block" as it may confuse) to the queueA, then this work item will be send to queueA 's target queue, then GCD will preserve a thread threadWorkItem for this work item
  2. When I reach (b), I'm in the thread threadWorkItem (suppose threadWorkItem is the name of this thread), so I think enqueuing another work item to queueA is no problem. But some people say that at this time, queueA is preserved, queueA is blocked -> causes deadlock, which confuses me

I already read many threads related to this, such as Deadlock with dispatch_sync, Why can't we use a dispatch_sync on the current queue?, Why is this dispatch_sync() call freezing?, ... but can't find good explanation. Some say dispatch_sync blocks the queue, some say it blocks the current thread, ... :(

So why does it cause deadlock ?

like image 420
onmyway133 Avatar asked Dec 09 '22 07:12

onmyway133


1 Answers

The dispatch_sync blocks the current thread until the dispatched code finishes, and if you're dispatching synchronously from a serial queue, you therefore are effectively blocking the queue, too. So if you synchronously dispatch from serial queue to itself, it results in a deadlock.

But to be clear, dispatch_sync blocks the current thread, not the current queue. When dealing with a concurrent queue, a different worker thread will be used for the subsequently dispatched block and no deadlock results.

You appear to be responding to a discussion at the end of the Dispatch Queues chapter of the Concurrency Programming Guide, which says:

Do not call the dispatch_sync function from a task that is executing on the same queue that you pass to your function call. Doing so will deadlock the queue. If you need to dispatch to the current queue, do so asynchronously using the dispatch_async function.

That is not entirely correct. If (a) you are doing this with a concurrent queue; and (b) there are available worker threads, this will not cause a deadlock. But it's a bad practice and should be avoided, nonetheless.

like image 69
Rob Avatar answered Dec 25 '22 01:12

Rob