|
// |
|
// DispatchForest.swift |
|
// DispatchForest |
|
// |
|
// Created by Enrico Zannini on 26/01/2021. |
|
// Copyright © 2021 Enrico Zannini. |
|
// |
|
|
|
/** |
|
* A limited forest of serial dispatch queues (Trees), each used for one subsystem. |
|
* |
|
* Trees and Branches are named following the Reverse-DNS naming convention. |
|
* Just name every branch with it's limited scope and the reverse-DNS name will be concatenated |
|
* by following the Branches up to the base Tree. |
|
* |
|
* If you wish to add more Trees to the forest you can do so by adding them to this class as `static let`. |
|
* Just be sure not to create more than it's necessary. Usually 3 to 4 Trees are all it's needed for a simple app. |
|
* |
|
* Should you need other Queues onto which dispatch different kind of work, or all the work of a smaller part of a subsystem, |
|
* you can branch one of the Trees, or even branch from another Branch if that's the case, without any concern. |
|
* You can create as many branches as you want. |
|
* |
|
* Every work scheduled on a Tree, or on any branch of that tree, will execute in a mutually exclusive manner, |
|
* on the queue of the base Tree. |
|
* The quality of service for a single work item can never be lower than the qos of both the Branch |
|
* on which the work is scheduled, and every ancestor of his, up to the base Tree itself. |
|
* Of course the system optimizes this process and takes care of resolving the priority inversion |
|
* in case that a priority inversion happens. |
|
* |
|
* As an example, in this class we added 4 Trees: the obvious main (which is ultimately just the main queue) |
|
* and network, database and analytics. |
|
* You can create your own Trees and call them as you want. |
|
*/ |
|
final class DispatchForest { |
|
|
|
fileprivate static let nomenclature = DispatchNomenclature(baseName: "com.dispatchForest") |
|
|
|
/// The main Tree, wich is just the main thread, added here just for reference. |
|
static let main: DispatchQueue = DispatchQueue.main |
|
|
|
/// A Tree used for a network subsystem. |
|
static let network: DispatchQueue = { |
|
return newTree(name: "network", qos: .default) |
|
}() |
|
|
|
/// A Tree used for a local database subsystem. |
|
static let database: DispatchQueue = { |
|
return newTree(name: "database", qos: .default) |
|
}() |
|
|
|
/// A Tree used for an analytics subsystem. QoS here is probably lower since analytics is often less important than other stuff. |
|
static let analytics: DispatchQueue = { |
|
return newTree(name: "analytics", qos: .utility) |
|
}() |
|
|
|
/** |
|
* Create a new Serial DispatchQueue Tree, without any other target queue. |
|
* |
|
* Every work scheduled on this Tree, or on any Branch of this Tree, will execute in a mutually exclusive manner. |
|
* |
|
* - parameter name: The name of this Tree, that will be concatenated with the baseName of every tree |
|
* - parameter qos: The qos of this Tree, which will be the minimum qos of the blocks scheduled to this queue and to it's Branches. |
|
*/ |
|
private class func newTree(name: String, qos: DispatchQoS = .default) -> DispatchQueue { |
|
let label = nomenclature.newTreeName(name) |
|
return DispatchQueue(label: label, qos: qos) |
|
} |
|
|
|
private init() {} |
|
|
|
} |
|
|
|
extension DispatchQueue { |
|
|
|
/** |
|
* Create a Serial Branch from this Tree, concatenating the name to the rest of the Tree name. |
|
* |
|
* Every work scheduled to this Branch will execute in a mutually exclusive manner from any other work scheduled to this Branch, |
|
* to its ancestors and to any branches that originated from them, up to the base Tree. |
|
* |
|
* - parameter name: The name is concatenated with the current label with a ".", following the reverse-DNS naming style |
|
* - parameter qos: The optional quality of service for this branch. If `nil` it will use the qos of self. |
|
* Use `unspecified` if you wish the system to infere the qos from the qos of the queue from which every block is scheduled. |
|
*/ |
|
func newBranch(name: String, qos: DispatchQoS? = nil) -> DispatchQueue { |
|
return DispatchQueue(label: DispatchForest.nomenclature.compositeName(base: self.label, name: name), |
|
qos: qos ?? self.qos, |
|
target: self) |
|
} |
|
} |
|
|
|
/** |
|
* A private class used to concatenate name of the Branches using the reverse-DNS naming convention. |
|
*/ |
|
private class DispatchNomenclature { |
|
|
|
let baseName: String |
|
|
|
init(baseName: String) { |
|
self.baseName = baseName |
|
} |
|
|
|
func newTreeName(_ name: String) -> String { |
|
return compositeName(base: baseName, name: name) |
|
} |
|
|
|
func compositeName(base: String, name: String) -> String { |
|
return "\(base).\(name)" |
|
} |
|
} |