The GCD documentation has a major omission: it doesn't discuss the existence of both overcommit and non-overcommit queues.
Here's something the implementation says about overcommit queues:
/*!
* @enum dispatch_queue_flags_t
*
* @constant DISPATCH_QUEUE_OVERCOMMIT
* The queue will create a new thread for invoking blocks, regardless of how
* busy the computer is.
*/
enum {
DISPATCH_QUEUE_OVERCOMMIT = 0x2ull,
};
A non-overcommit queue does limit the number of threads it creates.
There are two global queues for each QoS. One is overcommit and the other is non-overcommit. The Swift function DispatchQueue.global
always returns the non-overcommit queue. It prints like this:
(lldb) po DispatchQueue.global(qos: .default).description
"<OS_dispatch_queue_root: com.apple.root.default-qos>"
You can get a reference to the overcommit queue by dropping down to the C function dispatch_get_global_queue
(available in Swift with a __
prefix) and passing the private value of DISPATCH_QUEUE_OVERCOMMIT
:
(lldb) po __dispatch_get_global_queue(0, 2).description
"<OS_dispatch_queue_root: com.apple.root.default-qos.overcommit>"
Of course you should not do this in production code, because DISPATCH_QUEUE_OVERCOMMIT
is not a public API. I don't know of a way to get a reference to the overcommit queue using only public APIs.
So, back to Quinn's man page quotation:
The default target queue of all dispatch objects created by the
application is the default priority global concurrent queue.
It says “the default priority global concurrent queue” (emphasis added). But there are two default-priority global concurrent queues. And as it turns out, the default target queue is the overcommit queue. Here's my modified version of your test, as a macOS command line program:
import Dispatch
let serial = DispatchQueue(label: "serial")
let concurrent = DispatchQueue(label: "concurrent", attributes: .concurrent)
let DISPATCH_QUEUE_OVERCOMMIT = 2
let defaultOvercommit = __dispatch_get_global_queue(Int(QOS_CLASS_DEFAULT.rawValue), 2)
let serial_1 = DispatchQueue(label: "serial_1", target: serial)
let serial_2 = DispatchQueue(label: "serial_1", target: concurrent)
let group = DispatchGroup()
group.enter()
serial.async {
dispatchPrecondition(condition: .onQueue(serial))
dispatchPrecondition(condition: .onQueue(defaultOvercommit))
print("on serial")
group.leave()
}
group.enter()
concurrent.async {
// Both preconditions are met
dispatchPrecondition(condition: .onQueue(concurrent))
dispatchPrecondition(condition: .onQueue(DispatchQueue.global(qos: .default)))
print("on concurrent")
group.leave()
}
group.enter()
serial_1.async {
dispatchPrecondition(condition: .onQueue(serial))
dispatchPrecondition(condition: .onQueue(defaultOvercommit))
print("again on serial")
group.leave()
}
group.enter()
serial_2.async {
// Both preconditions are met
dispatchPrecondition(condition: .onQueue(concurrent))
dispatchPrecondition(condition: .onQueue(DispatchQueue.global(qos: .default)))
print("again on concurrent")
group.leave()
}
group.wait()
Note that I had to add the DispatchGroup
, else the program exits before any of the async blocks runs.
Anyway, here's the output:
on serial
again on concurrent
on concurrent
again on serial