I do not want done to fire before Hello AND Cruel AND World! have
fired at least once each, much like the default DispatchGroup
behavior.
I think you’ve misunderstood what I’m saying above. Consider Example 1, below. It prints:
Pompey
Huey
Alice
done
Crassus
Dewey
Bob
Caesar
Louie
Mallory
That is, it prints done
after the first item in each work item list is done.
However, your more concrete example makes it clear that I’m solving the wrong problem (-: There’s a bunch of different ways you could solve your problem but Example 2, also below, stays within the Dispatch world.
Note In this code I’m using timers, locationTimer
and so on, to simulate your various event sources.
This code uses mutable state, locationGroup
and so on, which can be problematic in concurrent code, but that state is very tightly confined.
Another option is to use the much-neglected DispatchSourceUserDataOr
. See Example 3. I kinda like this approach because the OR construct matches your overall semantics.
Share and Enjoy
Quinn “The Eskimo!” @ DTS @ Apple
Example 1
import Dispatch
func printAsync(_ str: String, completionHandler: @escaping () -> Void) {
print(str)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
completionHandler()
}
}
func printWork(_ work: ArraySlice<String>, notify group: DispatchGroup?) {
guard let first = work.first else {
return
}
printAsync(first) {
printWork(work.dropFirst(), notify: nil)
}
if let group = group {
group.leave()
}
}
func main() {
let group = DispatchGroup()
group.enter()
printWork(["Pompey", "Crassus", "Caesar"][...], notify: group)
group.enter()
printWork(["Huey", "Dewey", "Louie"][...], notify: group)
group.enter()
printWork(["Alice", "Bob", "Mallory"][...], notify: group)
group.notify(queue: .main) {
print("done")
}
dispatchMain()
}
main()
Example 2
import Dispatch
let locationTimer = DispatchSource.makeTimerSource(queue: .main)
func startLocation(notify group: DispatchGroup) {
group.enter()
locationTimer.schedule(deadline: .now() + 0.1, repeating: 0.5)
var locationGroup: DispatchGroup? = group
locationTimer.setEventHandler {
print("did change location")
if let group = locationGroup {
locationGroup = nil
group.leave()
}
}
locationTimer.activate()
}
let authTimer = DispatchSource.makeTimerSource(queue: .main)
func startAuth(notify group: DispatchGroup) {
group.enter()
authTimer.schedule(deadline: .now() + 0.1, repeating: 1.0)
var authGroup: DispatchGroup? = group
authTimer.setEventHandler {
print("did change auth state")
if let group = authGroup {
authGroup = nil
group.leave()
}
}
authTimer.activate()
}
let listenerTimer = DispatchSource.makeTimerSource(queue: .main)
func startListener(notify group: DispatchGroup) {
group.enter()
listenerTimer.schedule(deadline: .now() + 0.1, repeating: 1.5)
var listenerGroup: DispatchGroup? = group
listenerTimer.setEventHandler {
print("did attach listener")
if let group = listenerGroup {
listenerGroup = nil
group.leave()
}
}
listenerTimer.activate()
}
func main() {
let group = DispatchGroup()
startLocation(notify: group)
startAuth(notify: group)
startListener(notify: group)
group.notify(queue: .main) {
print("ready")
}
dispatchMain()
}
main()
Example 2
import Dispatch
let locationTimer = DispatchSource.makeTimerSource(queue: .main)
func startLocation(notify source: DispatchSourceUserDataOr) {
locationTimer.schedule(deadline: .now() + 0.1, repeating: 0.5)
locationTimer.setEventHandler {
print("did change location")
source.or(data: 0x01)
}
locationTimer.activate()
}
let authTimer = DispatchSource.makeTimerSource(queue: .main)
func startAuth(notify source: DispatchSourceUserDataOr) {
authTimer.schedule(deadline: .now() + 0.1, repeating: 1.0)
authTimer.setEventHandler {
print("did change auth state")
source.or(data: 0x02)
}
authTimer.activate()
}
let listenerTimer = DispatchSource.makeTimerSource(queue: .main)
func startListener(notify source: DispatchSourceUserDataOr) {
listenerTimer.schedule(deadline: .now() + 0.1, repeating: 1.5)
listenerTimer.setEventHandler {
print("did attach listener")
source.or(data: 0x04)
}
listenerTimer.activate()
}
func main() {
let source = DispatchSource.makeUserDataOrSource(queue: .main)
var received: UInt = 0
source.setEventHandler {
guard received != 0x07 else { return }
received |= source.data
if received == 0x07 {
print("ready")
}
}
source.activate()
startLocation(notify: source)
startAuth(notify: source)
startListener(notify: source)
dispatchMain()
}
main()