1

I'm quite confused.

Code below will cause a deadlock for sure:

// Will execute
DispatchQueue.main.async { // Block 1 
  // Will execute
    DispatchQueue.main.sync { // Block 2
     // Will not be executed
    }
    // Will not be executed
}

Because

  1. After we dispatch_async on main queue, it's submits the first block to the main queue to execute
  2. At some moment, the system decides to run block1
  3. The .sync method blocks "thread / queue?" <- My question
  4. Because the "thread / queue" is blocked, block 2 can't execute before block 1 finishes, because Main queue is a serial queue, it execute task serially, one can't execute before another finishes

My question is: Does sync block current thread it's executing on or current queue? (I understand the difference between thread & queue)

Most answers on the internet says it blocks thread

  1. If block thread -> How come the sync { } block can still execute since the thread is blocked?

  2. If block queue -> Make more sense? Since the queue is blocked, we can't execute one before other finishes

I found some discussions about this:

dispatch_sync inside dispatch_sync causes deadlock

Difference Between DispatchQueue.sync vs DispatchQueue.async

What happens if dispatch on same queue?

Johnny
  • 2,589
  • 2
  • 27
  • 34
  • 2
    It blocks the thread and the queue. The main queue always dispatched on the main thread. When you call `sync` it tries to dispatch that task on the main queue, but it can't as the main queue is busy. Since the code was running on the main thread that is also blocked. – Paulw11 May 17 '22 at 09:20
  • @Paulw11 This answer makes sense, so strictly speaking, both "block the queue" or "block the thread" is technically correct, right? People don't need to argue about which is blocked, because both are blocked.. – Johnny May 17 '22 at 09:22
  • Correct. To have a deadlock you need to satisfy the criteria of "mutual exclusion" and "hold and wait". Both the main queue and the main thread are being held and the two tasks are mutually exclusive; the Async cannot complete until the sync completes and the sync cannot start until the Async completes. – Paulw11 May 17 '22 at 09:24
  • And I would say that "block the queue" *and* "block the thread" is true, not "or" due to the special relationship between the main queue and main thread and the fact that the main queue is a serial dispatch queue. – Paulw11 May 17 '22 at 09:25
  • 1
    There is no concept of blocking a queue. However, `main` being a serial queue, you can easily get into deadlocks. – Cristik May 17 '22 at 09:28

1 Answers1

3

You asked:

My question is: Does sync block current thread it's executing on or current queue?

It blocks the current thread.

When dealing with a serial queue (such as the main queue), if that queue is running something whose thread is blocked, that prevents anything else from running on that queue until the queue is free again. A serial queue can only use one thread at a time. Thus, dispatching synchronously from any serial queue to itself will result in a deadlock.

But, sync does not technically block the queue. It blocks the current thread. Notably, when dealing with a concurrent queue (such as a global queue or a custom concurrent queue), that queue can avail itself of multiple worker threads at the same time. So just because one worker thread is blocked, it will not prevent the concurrent queue from running another dispatched item on another, unblocked, worker thread. Thus, dispatching synchronously from a concurrent queue to itself will not generally deadlock (as long as you don’t exhaust the very limited worker thread pool).


E.g.

let serialQueue = DispatchQueue(label: "serial")

serialQueue.async {
    serialQueue.sync {
        // will never get here; deadlock
        print("never get here")
    }
    // will never get here either, because of the above deadlock
}

let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)

concurrentQueue.async {
    concurrentQueue.sync {
        // will get here as long as you don't exhaust the 64 worker threads in the relevant QoS thread pool
        print("ok")
    }
    // will get here
}

You asked:

  1. If block thread -> How come the sync { } block can still execute since the thread is blocked?

As you have pointed out in your own code snippet, the sync block does not execute (in the serial queue scenario).

Rob
  • 415,655
  • 72
  • 787
  • 1,044