forked from mozilla-mobile/firefox-ios
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Deferred.swift
155 lines (131 loc) · 4.32 KB
/
Deferred.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//
// Deferred.swift
// AsyncNetworkServer
//
// Created by John Gallagher on 7/19/14.
// Copyright (c) 2014 Big Nerd Ranch. All rights reserved.
//
import Foundation
// TODO: Replace this with a class var
private var DeferredDefaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
public class Deferred<T> {
typealias UponBlock = (dispatch_queue_t, T -> ())
private typealias Protected = (protectedValue: T?, uponBlocks: [UponBlock])
private var protected: LockProtected<Protected>
private let defaultQueue: dispatch_queue_t
public init(value: T? = nil, defaultQueue: dispatch_queue_t = DeferredDefaultQueue) {
protected = LockProtected(item: (value, []))
self.defaultQueue = defaultQueue
}
// Check whether or not the receiver is filled
public var isFilled: Bool {
return protected.withReadLock { $0.protectedValue != nil }
}
private func _fill(value: T, assertIfFilled: Bool) {
let (filledValue, blocks) = protected.withWriteLock { data -> (T, [UponBlock]) in
if assertIfFilled {
precondition(data.protectedValue == nil, "Cannot fill an already-filled Deferred")
data.protectedValue = value
} else if data.protectedValue == nil {
data.protectedValue = value
}
let blocks = data.uponBlocks
data.uponBlocks.removeAll(keepCapacity: false)
return (data.protectedValue!, blocks)
}
for (queue, block) in blocks {
dispatch_async(queue) { block(filledValue) }
}
}
public func fill(value: T) {
_fill(value, assertIfFilled: true)
}
public func fillIfUnfilled(value: T) {
_fill(value, assertIfFilled: false)
}
public func peek() -> T? {
return protected.withReadLock { $0.protectedValue }
}
public func uponQueue(queue: dispatch_queue_t, block: T -> ()) {
let maybeValue: T? = protected.withWriteLock{ data in
if data.protectedValue == nil {
data.uponBlocks.append( (queue, block) )
}
return data.protectedValue
}
if let value = maybeValue {
dispatch_async(queue) { block(value) }
}
}
}
extension Deferred {
public var value: T {
// fast path - return if already filled
if let v = peek() {
return v
}
// slow path - block until filled
let group = dispatch_group_create()
var result: T!
dispatch_group_enter(group)
self.upon { result = $0; dispatch_group_leave(group) }
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
return result
}
}
extension Deferred {
public func bindQueue<U>(queue: dispatch_queue_t, f: T -> Deferred<U>) -> Deferred<U> {
let d = Deferred<U>()
self.uponQueue(queue) {
f($0).uponQueue(queue) {
d.fill($0)
}
}
return d
}
public func mapQueue<U>(queue: dispatch_queue_t, f: T -> U) -> Deferred<U> {
return bindQueue(queue) { t in Deferred<U>(value: f(t)) }
}
}
extension Deferred {
public func upon(block: T ->()) {
uponQueue(defaultQueue, block: block)
}
public func bind<U>(f: T -> Deferred<U>) -> Deferred<U> {
return bindQueue(defaultQueue, f: f)
}
public func map<U>(f: T -> U) -> Deferred<U> {
return mapQueue(defaultQueue, f: f)
}
}
extension Deferred {
public func both<U>(other: Deferred<U>) -> Deferred<(T,U)> {
return self.bind { t in other.map { u in (t, u) } }
}
}
public func all<T>(deferreds: [Deferred<T>]) -> Deferred<[T]> {
if deferreds.count == 0 {
return Deferred(value: [])
}
let combined = Deferred<[T]>()
var results: [T] = []
results.reserveCapacity(deferreds.count)
var block: (T -> ())!
block = { t in
results.append(t)
if results.count == deferreds.count {
combined.fill(results)
} else {
deferreds[results.count].upon(block)
}
}
deferreds[0].upon(block)
return combined
}
public func any<T>(deferreds: [Deferred<T>]) -> Deferred<Deferred<T>> {
let combined = Deferred<Deferred<T>>()
for d in deferreds {
d.upon { _ in combined.fillIfUnfilled(d) }
}
return combined
}