How to dispatch on main queue synchronously without a deadlock?

Solution 1:

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.

Solution 2:

For syncing on the main queue or on the main thread (that is not the same) I use:

import Foundation

private let mainQueueKey    = UnsafeMutablePointer<Void>.alloc(1)
private let mainQueueValue  = UnsafeMutablePointer<Void>.alloc(1)


public func dispatch_sync_on_main_queue(block: () -> Void)
{
    struct dispatchonce  { static var token : dispatch_once_t = 0  }
    dispatch_once(&dispatchonce.token,
    {
        dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueValue, nil)
    })

    if dispatch_get_specific(mainQueueKey) == mainQueueValue
    {
        block()
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(),block)
    }
}

extension NSThread
{
    public class func runBlockOnMainThread(block: () -> Void )
    {
        if NSThread.isMainThread()
        {
            block()
        }
        else
        {
            dispatch_sync(dispatch_get_main_queue(),block)
        }
    }

    public class func runBlockOnMainQueue(block: () -> Void)
    {
        dispatch_sync_on_main_queue(block)
    }
}