Skip to content

Instantly share code, notes, and snippets.

@steipete
Created September 13, 2012 09:39
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steipete/3713233 to your computer and use it in GitHub Desktop.
Save steipete/3713233 to your computer and use it in GitHub Desktop.
I often use dispatch queues for locking, and this function just makes life SO MUCH EASIER. Accidental deadlocks in more complex code paths are a PITA otherwise. But Apple deprecated dispatch_get_current_queue with iOS6?
nline void pspdf_dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_block_t block) {
dispatch_get_current_queue() == queue ? block() : dispatch_sync(queue, block);
}
@steipete
Copy link
Author

"NSThread has currentThread. NSOperationQueue has currentQueue. Why doesn't dispatch -- which is supposed to be the most powerful of the mechanisms -- deserve this "dangerous" capability?" https://devforums.apple.com/message/710745#710745

@ctp
Copy link

ctp commented Sep 13, 2012

The deprecation is related to the first two paragraphs in the man page for dispatch_sync(3).

However, this code suffers from a conceptual problem regardless of the deprecation status of dispatch_get_current_queue(). Consider the case where the block passed in to queue has an ordering requirement with respect to other blocks already on the queue (placed there by dispatch_sync() from another queue, or dispatch_async()). In that case, executing block() immediately jumps the queue.

We've seen this pattern lead to very hard-to-debug issues and don't use it at all at work.

@steipete
Copy link
Author

I begin to understand; I used this "anti-pattern" whenever I needed recursive locking of methods that both can be called outside and within the class (where the access might already be locked). Much like the GCDAyncUdpSocket example in the dev forum discussion.

Because my code doesn't depend on ordering requirements, this solution works fine for me, and it's often the "easy way out" if you find a deadlock in dispatch_sync. Some of it also originated because I still supported iOS 4.2 back then, where no concurrent locks where available.

I don't see an easy way out in my current code base though (http://cl.ly/image/3b1a123R3q0g) - seems I either have to introduce a lot more queues for more fine-grained locking and replace some parts where the dispatch_queue really was just used as an recursive lock with @synchronized or NSRecursiveLock.

Are there resources that show best practice on that? I've watched the WWDC GCD videos, but I would love to see proper GCD usage on a more complex, highly multithreaded code base.

@bagelturf
Copy link

dispatch_get_specific can be used to get the value of a key that was attached to the tree of queues. That lets you know where you are in the queue tree and whether it is safe, from your design's standpoint, to run synchronously. iOS 5.0+

@jspahrsummers
Copy link

@ctp That's extremely disappointing. It's already hard enough to use synchronous dispatches in any safe way, and I've written a lot of code to make this pattern scale (and behave more predictably): https://github.com/jspahrsummers/SafeDispatch

@ctp
Copy link

ctp commented Sep 14, 2012

@jspahrsummers - Disappointing? It's part of the dispatch_sync() contract and makes sense if you think about what's happening with respect to the queue.

I'm not sure what to tell you relative to your SafeDispatch project - I'm afraid I don't have the time right now to go through and figure out what to recommend if this pattern appears there.

The man page for dispatch_sync() describes one approach to the problem if you must use a recursive locking pattern; perhaps that's something which might help out (I don't know).

@jspahrsummers
Copy link

@ctp I agree that it's part of the contract, and does violate ordering, but, in my opinion, such issues are preferable to deadlocks. All of these problems are difficult to verify statically (with a compiler or as a human), so it's really a question of which failure you would rather have.

I could see an argument either way, honestly, which sounds to me like it should be up to the caller. GCD is already a fairly low-level API – I think allowing users to make decisions like this fits with its design.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment