Reputation: 9915
I don't understand the workings of a DispatchQueue
and wanted to learn more about how they implement the foundational queueing theory requirements. I tried to inspect a queue using:
dump(DispatchQueue.global())
And this gave this output:
- <OS_dispatch_queue_global: com.apple.root.default-qos[0x10c041f00] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}> #0
- super: OS_dispatch_queue
- super: OS_dispatch_object
- super: OS_object
- super: NSObject
I got that the label is com.apple.root.default-qos
, and this is specified in the Apple docs and the class is the packaged OS_dispatch_queue_global
. I understand qos
is queryable on the queue itself and that makes sense as well. Width I think just means the allocated memory size.
What I don't understand are the relevances of xref
, ref
and sref
, I think they are internal ids for the queues but I am not sure. I think they are related to fundamental queueing concepts (multithreading came to mind) but would be great to hone into this in more detail.
Is the autoreleaseFrequency
hidden from this debug description? Also, what does in-barrier = 0
mean? I tried creating a custom queue and this was replaced by in-flight = 0
.. so confused about that as well.
Any ideas on how these undocumented variables relate to queueing theory? I think these are undocumented internals of the API, so any educated and justified explanations would be fine!
Thanks.
Upvotes: 1
Views: 899
Reputation: 9915
This is a fairly broad question about the internals of grand-central-dispatch
. I had difficulty understanding the dump
ed output because the original WWDC '10 videos and slides for GCD are no longer public. I also didn't know about the open-source libdispatch
repo (thanks Rob). That needn't be a problem, but there are no related QAs on SO explaining the topic in detail.
According to the WWDC '10 GCD transcripts (Thanks Rob), the main idea behind the API was to simplify the boilerplate associated with using the #selector
API for multithreading.
GCD
Apple released a new block
-based API instead of going with function pointers, to also enable type-safe code that wouldn't crash if the block had the wrong type signature. Using typedef
s also made code cleaner when used in function parameters, local variables and @property
declarations. Queues allow you to capture code and some state as a chunk of data that get managed, enqueued and executed automatically behind the scenes.
The same session mentions how GCD manages low-level threads under the hood. It enqueues blocks to execute on threads when they need to be executed and then releases those threads (PThread
s to be precise) when they are no longer referenced. GCD manages threads automatically and doesn't expose this API - when a DispatchWorkItem
is dequeued GCD creates a thread for this block to execute on.
performSelector
performSelector:onThread:withObject:waitUntilDone:
has numerous drawbacks that suggest poor design for the modern challenges of concurrency, waiting, synchronisation. leads to pyramids of doom when switching threads in a func
. Furthermore, the NSObject.performSelector
family of threading methods are inflexible and limited:
NSOperation
API. NSOperation
s are a high-level, verbose API that became more powerful after incorporating elements of dispatch (low-level API that became GCD) in iOS 4.Selector
errors (type safety).DispatchQueue
internalsI believe the xref
, ref
and sref
are internal registers that manage reference counts for automatic reference counting. GCD calls dispatch_retain
and dispatch_release
in most cases when needed, so we don't need to worry about releasing a queue after all its blocks have been executed. However, there were cases when a developer could call retain
and release
manually when trying to ensure the queue is retained even when not directly in use. These registers allow libDispatch
to crash when release
is called on a queue with a positive reference count, for better error handling.
When calling a block with DispatchQueue.global().async
or similar, I believe this increments the reference count of that queue (xref
and ref
).
The variables in the question are not documented explicitly, but from what I can tell:
xref
counts the number of external references to a general DispatchQueue
.ref
counts the total number of references to a general DispatchQueue
.sref
counts the number of references to API serial/concurrent/runloop queues, sources and mach channels (these need to be tracked differently as they are represented using different types).in-barrier
looks like an internal state flag (DispatchWorkItemFlag
) to track whether new work items submitted to a concurrent queue should be scheduled or not. Only once the barrier work item finishes, the queue returns to scheduling work items that were submitted after the barrier. in-flight
means that there is no barrier in force currently.
state
is also not documented explicitly but I presume points to memory where the block can access variables from the scope where the block was scheduled.
Upvotes: 3