How do I create a deadlock in Grand Central Dispatch?
An intentional deadlock on a certain queue:
dispatch_queue_t queue = dispatch_queue_create("my.label", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
// outer block is waiting for this inner block to complete,
// inner block won't start before outer block finishes
// => deadlock
});
// this will never be reached
});
It's clear here that the outer and inner blocks are operating on the same queue. Most cases where this will occur is in places where it's less obvious what queue the caller of the dispatch_sync
is operating on. This usually occurs in a (deeply) nested stack where you're executing code in some class that was originally launched on a certain queue, and by accident you call a dispatch_sync
to the same queue.
Simple code that creates deadlock:
dispatch_queue_t q = dispatch_queue_create("deadlock queue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(q, ^{
NSLog(@"2");
dispatch_sync(q, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
Log output:
1
5
2
Here internal block is scheduled to be run on serial queue q
but it cannot run until current block is finished, while current block, in turn, waits internal to finish as we called it synchronously.
The simplest way to block is to dispatch_sync
on the current queue:
dispatch_sync(dispatch_get_current_queue(), ^{});
This blocks when the current queue is a serial queue, for example the main queue.