DispatchQueue crashing with main.sync in Swift
Asked Answered
I

4

22

Please explain to me why I am getting this crash?

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

in this

DispatchQueue.main.sync { print("sync") }

This is my code.

    override func viewDidLoad() {
    super.viewDidLoad()


    print("Start")
    DispatchQueue.main.async {
        print("async")

    }
    DispatchQueue.main.sync {
        print("sync")
    }
    print("Finish")
}
Ire answered 13/3, 2018 at 14:11 Comment(0)
B
30

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. It is called deadlock.

Two (or sometimes more) items — in most cases, threads — are said to be deadlocked if they all get stuck waiting for each other to complete or perform another action. The first can’t finish because it’s waiting for the second to finish. But the second can’t finish because it’s waiting for the first to finish.

You need to be careful though. Imagine if you call sync and target the current queue you’re already running on. This will result in a deadlock situation.

Use sync to keep track of your work with dispatch barriers, or when you need to wait for the operation to finish before you can use the data processed by the closure.

When to use sync?

When we need to wait until the task is finished. F.e. when we are making sure that some function/method is not double called. F.e. we have synchronization and trying to prevent it to be double called until it's completely finished. When you need to wait for something done on a DIFFERENT queue and only then continue working on your current queue

Synchronous vs. Asynchronous

With GCD, you can dispatch a task either synchronously or asynchronously.

A synchronous function returns control to the caller after the task is completed.

An asynchronous function returns immediately, ordering the task to be done but not waiting for it. Thus, an asynchronous function does not block the current thread of execution from proceeding on to the next function.

Berky answered 13/3, 2018 at 15:49 Comment(0)
C
13

@sankalap, Dispatch.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".

Cloutman answered 13/3, 2018 at 14:45 Comment(3)
And a deadlock isn't a crash.Dogged
@Dogged you are right. I too agree deadlock is not a crash.Cloutman
This is a deadlock. It crashes because GCD detects this particular form of deadlock and aborts the program. If you reproduce the problem and look at the debugger's disassembly of _dispatch_sync_wait, you'll see the message “BUG IN CLIENT OF LIBDISPATCH: dispatch_sync called on queue already owned by current thread”.Aneroid
E
9

As per Apple documentation on executing dispatch_sync on a queue you're currently on will crash your code:

Calling this function and targeting the current queue results in deadlock.

Escobedo answered 13/3, 2018 at 14:26 Comment(2)
A deadlock is not a crash. Big difference.Dogged
@Dogged You're right. Seems to me the behaviour is undefined, this could be one of the outcomes.Escobedo
M
1

Because the current queue is the main queue, when you continue to call sync on the main queue, the system will understand that current main queue must wait some code complete in current queue, but no code at current queue (main queue), so you wait forever:

Apple document: Calling this function and targeting the current queue results in deadlock.

Mozambique answered 6/5, 2022 at 10:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.