Why async blocks execution blocking main thread?

I have an app that uses

AFNetworking
for sending requests and after it finished downloading, it calls block where I create data models from JSON. During model initialization, there is async image loading with the following code

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(){

    NSData *urlData = [NSData dataWithContentsOfURL:downloadURL];

}

I know that

dataWithContentsOfURL:
is synchronous, and bydocumentation

... this method can block the current thread for tens of seconds on a slow network...

But as it's executing asynchronously in other thread, it shouldn't block the main thread. After some investigating, I found that starting from

URLSession:task:didCompleteWithError:
method that is placed inside of
AFURLSessionManager.m
has the following blocks hierarchy:


URLSession:task:didCompleteWithError: //1

|

dispatch_async(url_session_manager_processing_queue(), ^{ //2

//url_session_manager_processing_queue() - this is the default queue

|

dispatch_group_async(url_session_manager_completion_group(), dispatch_get_main_queue(), ^{ //3

|

//Inside of my callback

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //4

|

NSData *urlData = [NSData dataWithContentsOfURL:downloadURL];


If I set

AFHTTPSessionManager
's
completionQueue
property:

_sharedClient.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);


Then

dispatch_group_sync ... //3
using Default queue, and the main thread is not blocked. Is there any explanation why without setting
completionQueue
property my main thread is blocked? (Stack trace showing
semaphore_wait_trap
in main thread)

Did you make sure you weren't accidentally using synchronous GCD queues? Just because you use dispatch_async doesn't mean that the queue itself is asynchronous; it means that you don't care exactly when the block is executed.

Yes, I checked all the places but there are no dispatch_sync code. Maybe it will be useful:

Stack trace of thread where block from default queue executing, there is 40-60 such threads:

http://pastebin.com/Zs7hmE1W, XXX here mean my functions

Stack trace of the main thread:

http://pastebin.com/hB879qUR

Stack trace of the main thread:

#0  0x2262b8f8 in semaphore_wait_trap ()
#1  0x00da734a in _dispatch_semaphore_wait_slow ()
#2  0x22efbaf4 in CFURLConnectionSendSynchronousRequest ()
#3  0x22f1608e in +[NSURLConnection sendSynchronousRequest:returningResponse:error:] ()
#4  0x23146b90 in -[NSData(NSData) initWithContentsOfURL:] ()
#5  0x23146aae in +[NSData(NSData) dataWithContentsOfURL:] ()
#6  0x0024b7ca in __42-XXX_block_invoke
#7  0x00d98ba6 in _dispatch_call_block_and_release ()
#8  0x00da4efe in _dispatch_root_queue_drain ()
#9  0x00da47f0 in _dispatch_worker_thread3 ()
#10 0x226e2e0c in _pthread_wqthread ()
#11 0x226e29fc in start_wqthread ()
Enqueued from com.apple.main-thread (Thread 1)Queue : com.apple.main-thread (serial)
#0  0x00da56fa in _dispatch_async_f_slow ()
…

That doesn’t look like a stack trace for the main thread. Frames 7…11 are not what you’d typically expect to find at the top of the main thread. This looks like a dispatch worker thread.

Perhaps you’re confused by the Enqueued from com.apple.main-thread at the end of the trace. This is a recently-added debugging feature that allows you to see the backtrace of the code that enqueue the block. Note that the frame numbers get reset back to 0 at that boundary.

On a related note, what is

downloadURL
is this context? Is it a network URL? Or a file URL?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

No, no, dispatch_sync is NOT the same thing as a synchronous queue. It's a property of the queue itself and is the same whether you sync or async a block into it. Where that comes into play is how the calling code gets treated as the block waits and then runs.

Maybe I misunderstood your message, but stack trace in your message is from one of the Default queue's thread. It's main thread stack trace

#0  0x2262b8f8 in semaphore_wait_trap ()
#1  0x00da7746 in _dispatch_group_wait_slow ()
#2  0x26dd41ba in -[_CSIRenditionBlockData expandCSIBitmapData:fromSlice:makeReadOnly:] ()
#3  0x26ddabae in __csiCompressImageproviderCopyIOSurfaceWithOptions ()
#4  0x25023cca in CA::Render::create_image(CGImage*, CGColorSpace*, unsigned int) ()
#5  0x2502320c in CA::Render::copy_image(CGImage*, CGColorSpace*, unsigned int, double) ()
#6  0x25023b86 in CA::Render::prepare_image(CGImage*, CGColorSpace*, unsigned int, double) ()
#7  0x24ff7f66 in CA::Layer::prepare_commit(CA::Transaction*) ()
#8  0x24ff6f22 in CA::Context::commit_transaction(CA::Transaction*) ()
#9  0x24ff6bc2 in CA::Transaction::commit() ()
#10 0x24ff022e in CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) ()
#11 0x22973330 in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#12 0x22971626 in __CFRunLoopDoObservers ()
#13 0x22971a64 in __CFRunLoopRun ()
#14 0x228c0288 in CFRunLoopRunSpecific ()
#15 0x228c007c in CFRunLoopRunInMode ()
#16 0x23edcaf8 in GSEventRunModal ()
#17 0x26feb2c4 in UIApplicationMain ()
#18 0x00188fee in main
#19 0x2256c872 in start ()

downloadURL is the network link on an image.

If you about is queue concurrent or serial, then default queue is concurrent and main - serial

OK, good that you know that. I wasn't sure if you understood the distinction between the two types of queues.

Why async blocks execution blocking main thread?
 
 
Q