Skip to content

Instantly share code, notes, and snippets.

@russbishop
Last active August 29, 2015 14:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save russbishop/c5daa504bac721635a9b to your computer and use it in GitHub Desktop.
Save russbishop/c5daa504bac721635a9b to your computer and use it in GitHub Desktop.
Sketchy queue deadlock detection?

#Goal

Detect if dispatch_sync will deadlock and avoid it (because the current queue stack is ultimately on self.onQueue)

#Possibly Sketchy

This does appear to work; can you think of any reasons why it would fail or be dangerous?

  • Technically setting a queue specific key on the global main queue is questionable but it does work
  • A complex tree or re-entrancy can still cause a deadlock (eg if main queue is busy while bg queue wants to dispatch main, then main queue tries to doAThing)
private var queueContextKey = UnsafeMutablePointer<Void>.alloc(1)
private var queueContextValue = UnsafeMutablePointer<Void>.alloc(1)
private var mainQueueContextValue = UnsafeMutablePointer<Void>.alloc(1)
class QueueTest {
private let onQueue: dispatch_queue_t
init(useBackgroundQueue: Bool = false) {
if useBackgroundQueue {
let queueAttribs = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0)
self.onQueue = dispatch_queue_create("com.plangrid.dispatcher", queueAttribs)
dispatch_queue_set_specific(self.onQueue, queueContextKey, queueContextValue, nil)
}
else {
self.onQueue = dispatch_get_main_queue()
}
dispatch_queue_set_specific(dispatch_get_main_queue(), queueContextKey, mainQueueContextValue, nil)
}
func doAThing(f:()->Void) {
sync {
print("did a thing!")
f()
}
}
//sketchy or not?
private func isOwnQueue() -> Bool {
let value = dispatch_get_specific(queueContextKey)
return value == queueContextValue || (self.onQueue == dispatch_get_main_queue() && value == mainQueueContextValue)
}
private class func isMainQueue() -> Bool {
return dispatch_get_specific(queueContextKey) == mainQueueContextValue
}
private func sync<T>(action: ()->T) -> T {
return _sync(action)
}
private func sync(action: ()->Void) {
_sync(action)
}
private func _sync<T>(action: ()->T) -> T {
if isOwnQueue() {
//prevent deadlocks by detecting if we are on our own queue
return action()
}
else {
var result: T!
dispatch_sync(self.onQueue) {
result = action()
}
return result
}
}
}
//UIViewController or NSViewController as appropriate...
var targetMainQueue = QueueTest(useBackgroundQueue: false)
var targetBgQueue = QueueTest(useBackgroundQueue: true)
override func viewDidAppear() {
targetMainQueue.doAThing {
self.targetMainQueue.doAThing {
print("ok")
}
}
targetBgQueue.doAThing {
self.targetBgQueue.doAThing {
print("ok")
}
}
targetMainQueue.doAThing {
self.targetBgQueue.doAThing {
print("ok")
}
}
targetBgQueue.doAThing {
self.targetMainQueue.doAThing {
print("ok")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment