Reputation: 11
I used to have an helper method (static) that ensures that a completion block is called on the correct queue.
+ (void)_deadlockCheckBlock:(void(^)(void))block caller:(dispatch_queue_t)caller {
NSParameterAssert(block);
NSParameterAssert(caller);
if (caller == dispatch_get_current_queue()) {
block();
}
else {
dispatch_async(caller, block);
}}
Now to overcome the deprecation of
dispatch_get_current_queue()
I've rewritten the method using the get_main_queue method.
+ (void)_deadlockCheckBlock:(void(^)(void))block caller:(dispatch_queue_t)caller {
NSParameterAssert(block);
NSParameterAssert(caller);
dispatch_sync(dispatch_get_main_queue(), ^{
//This will ensure to be on the main queue
if (caller == dispatch_get_main_queue()) {
block();
}
else {
dispatch_async(caller, block);
}
});}
Is there a better way to get the same behaviour without going on the main queue?
Upvotes: 1
Views: 230
Reputation: 41801
The problem with that (and the reason get_current_queue is deprecated) is that there can be many current queues. If you dispatch_sync from one queue to another, both are now "current" in the sense that dispatch_sync to either of them will deadlock. Same with dispatch_set_target_queue.
The reliable ways to avoid deadlock are to always use dispatch_async (or other async APIs; dispatch_group_notify for example), to avoid reentrant control flow, or to pass along enough additional information to decide properly (if using a queue as a lock you could have an 'already locked' flag that you pass along for example).
In general, code that requires recursive locks or things that simulate them (like using the current queue to decide whether to dispatch_sync) is code that may have some invariant (the one the lock is protecting) broken and is expected to work anyway, which is a somewhat scary notion.
Upvotes: 3