]> git.saurik.com Git - apple/libdispatch.git/blob - src/swift/Queue.swift
libdispatch-703.50.37.tar.gz
[apple/libdispatch.git] / src / swift / Queue.swift
1 //===----------------------------------------------------------------------===//
2 //
3 // This source file is part of the Swift.org open source project
4 //
5 // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6 // Licensed under Apache License v2.0 with Runtime Library Exception
7 //
8 // See http://swift.org/LICENSE.txt for license information
9 // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10 //
11 //===----------------------------------------------------------------------===//
12
13 // dispatch/queue.h
14
15 import CDispatch
16
17 public final class DispatchSpecificKey<T> {
18 public init() {}
19 }
20
21 internal class _DispatchSpecificValue<T> {
22 internal let value: T
23 internal init(value: T) { self.value = value }
24 }
25
26 public extension DispatchQueue {
27 public struct Attributes : OptionSet {
28 public let rawValue: UInt64
29 public init(rawValue: UInt64) { self.rawValue = rawValue }
30
31 public static let concurrent = Attributes(rawValue: 1<<1)
32
33 @available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
34 public static let initiallyInactive = Attributes(rawValue: 1<<2)
35
36 fileprivate func _attr() -> dispatch_queue_attr_t? {
37 var attr: dispatch_queue_attr_t? = nil
38
39 if self.contains(.concurrent) {
40 attr = _swift_dispatch_queue_concurrent()
41 }
42 if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
43 if self.contains(.initiallyInactive) {
44 attr = CDispatch.dispatch_queue_attr_make_initially_inactive(attr)
45 }
46 }
47 return attr
48 }
49 }
50
51 public enum GlobalQueuePriority {
52 @available(OSX, deprecated: 10.10, message: "Use qos attributes instead")
53 @available(*, deprecated: 8.0, message: "Use qos attributes instead")
54 case high
55
56 @available(OSX, deprecated: 10.10, message: "Use qos attributes instead")
57 @available(*, deprecated: 8.0, message: "Use qos attributes instead")
58 case `default`
59
60 @available(OSX, deprecated: 10.10, message: "Use qos attributes instead")
61 @available(*, deprecated: 8.0, message: "Use qos attributes instead")
62 case low
63
64 @available(OSX, deprecated: 10.10, message: "Use qos attributes instead")
65 @available(*, deprecated: 8.0, message: "Use qos attributes instead")
66 case background
67
68 internal var _translatedValue: Int {
69 switch self {
70 case .high: return 2 // DISPATCH_QUEUE_PRIORITY_HIGH
71 case .default: return 0 // DISPATCH_QUEUE_PRIORITY_DEFAULT
72 case .low: return -2 // DISPATCH_QUEUE_PRIORITY_LOW
73 case .background: return Int(Int16.min) // DISPATCH_QUEUE_PRIORITY_BACKGROUND
74 }
75 }
76 }
77
78 public enum AutoreleaseFrequency {
79 case inherit
80
81 @available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
82 case workItem
83
84 @available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
85 case never
86
87 internal func _attr(attr: dispatch_queue_attr_t?) -> dispatch_queue_attr_t? {
88 if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
89 switch self {
90 case .inherit:
91 // DISPATCH_AUTORELEASE_FREQUENCY_INHERIT
92 return CDispatch.dispatch_queue_attr_make_with_autorelease_frequency(attr, dispatch_autorelease_frequency_t(0))
93 case .workItem:
94 // DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM
95 return CDispatch.dispatch_queue_attr_make_with_autorelease_frequency(attr, dispatch_autorelease_frequency_t(1))
96 case .never:
97 // DISPATCH_AUTORELEASE_FREQUENCY_NEVER
98 return CDispatch.dispatch_queue_attr_make_with_autorelease_frequency(attr, dispatch_autorelease_frequency_t(2))
99 }
100 } else {
101 return attr
102 }
103 }
104 }
105
106 public class func concurrentPerform(iterations: Int, execute work: (Int) -> Void) {
107 _swift_dispatch_apply_current(iterations, work)
108 }
109
110 public class var main: DispatchQueue {
111 return DispatchQueue(queue: _swift_dispatch_get_main_queue())
112 }
113
114 @available(OSX, deprecated: 10.10, message: "")
115 @available(*, deprecated: 8.0, message: "")
116 public class func global(priority: GlobalQueuePriority) -> DispatchQueue {
117 return DispatchQueue(queue: CDispatch.dispatch_get_global_queue(priority._translatedValue, 0))
118 }
119
120 @available(OSX 10.10, iOS 8.0, *)
121 public class func global(qos: DispatchQoS.QoSClass = .default) -> DispatchQueue {
122 return DispatchQueue(queue: CDispatch.dispatch_get_global_queue(Int(qos.rawValue.rawValue), 0))
123 }
124
125 public class func getSpecific<T>(key: DispatchSpecificKey<T>) -> T? {
126 let k = Unmanaged.passUnretained(key).toOpaque()
127 if let p = CDispatch.dispatch_get_specific(k) {
128 let v = Unmanaged<_DispatchSpecificValue<T>>
129 .fromOpaque(p)
130 .takeUnretainedValue()
131 return v.value
132 }
133 return nil
134 }
135
136 public convenience init(
137 label: String,
138 qos: DispatchQoS = .unspecified,
139 attributes: Attributes = [],
140 autoreleaseFrequency: AutoreleaseFrequency = .inherit,
141 target: DispatchQueue? = nil)
142 {
143 var attr = attributes._attr()
144 if autoreleaseFrequency != .inherit {
145 attr = autoreleaseFrequency._attr(attr: attr)
146 }
147 if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified {
148 attr = CDispatch.dispatch_queue_attr_make_with_qos_class(attr, qos.qosClass.rawValue.rawValue, Int32(qos.relativePriority))
149 }
150
151 if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
152 self.init(__label: label, attr: attr, queue: target)
153 } else {
154 self.init(__label: label, attr: attr)
155 if let tq = target { self.setTarget(queue: tq) }
156 }
157 }
158
159 public var label: String {
160 return String(validatingUTF8: dispatch_queue_get_label(self.__wrapped))!
161 }
162
163 @available(OSX 10.10, iOS 8.0, *)
164 public func sync(execute workItem: DispatchWorkItem) {
165 CDispatch.dispatch_sync(self.__wrapped, workItem._block)
166 }
167
168 @available(OSX 10.10, iOS 8.0, *)
169 public func async(execute workItem: DispatchWorkItem) {
170 CDispatch.dispatch_async(self.__wrapped, workItem._block)
171 }
172
173 @available(OSX 10.10, iOS 8.0, *)
174 public func async(group: DispatchGroup, execute workItem: DispatchWorkItem) {
175 CDispatch.dispatch_group_async(group.__wrapped, self.__wrapped, workItem._block)
176 }
177
178 public func async(
179 group: DispatchGroup? = nil,
180 qos: DispatchQoS = .unspecified,
181 flags: DispatchWorkItemFlags = [],
182 execute work: @escaping @convention(block) () -> Void)
183 {
184 if group == nil && qos == .unspecified {
185 // Fast-path route for the most common API usage
186 if flags.isEmpty {
187 CDispatch.dispatch_async(self.__wrapped, work)
188 return
189 } else if flags == .barrier {
190 CDispatch.dispatch_barrier_async(self.__wrapped, work)
191 return
192 }
193 }
194
195 var block: @convention(block) () -> Void = work
196 if #available(OSX 10.10, iOS 8.0, *), (qos != .unspecified || !flags.isEmpty) {
197 let workItem = DispatchWorkItem(qos: qos, flags: flags, block: work)
198 block = workItem._block
199 }
200
201 if let g = group {
202 CDispatch.dispatch_group_async(g.__wrapped, self.__wrapped, block)
203 } else {
204 CDispatch.dispatch_async(self.__wrapped, block)
205 }
206 }
207
208 private func _syncBarrier(block: () -> ()) {
209 CDispatch.dispatch_barrier_sync(self.__wrapped, block)
210 }
211
212 private func _syncHelper<T>(
213 fn: (() -> ()) -> (),
214 execute work: () throws -> T,
215 rescue: ((Swift.Error) throws -> (T))) rethrows -> T
216 {
217 var result: T?
218 var error: Swift.Error?
219 fn {
220 do {
221 result = try work()
222 } catch let e {
223 error = e
224 }
225 }
226 if let e = error {
227 return try rescue(e)
228 } else {
229 return result!
230 }
231 }
232
233 @available(OSX 10.10, iOS 8.0, *)
234 private func _syncHelper<T>(
235 fn: (DispatchWorkItem) -> (),
236 flags: DispatchWorkItemFlags,
237 execute work: () throws -> T,
238 rescue: @escaping ((Swift.Error) throws -> (T))) rethrows -> T
239 {
240 var result: T?
241 var error: Swift.Error?
242 let workItem = DispatchWorkItem(flags: flags, noescapeBlock: {
243 do {
244 result = try work()
245 } catch let e {
246 error = e
247 }
248 })
249 fn(workItem)
250 if let e = error {
251 return try rescue(e)
252 } else {
253 return result!
254 }
255 }
256
257 public func sync<T>(execute work: () throws -> T) rethrows -> T {
258 return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 })
259 }
260
261 public func sync<T>(flags: DispatchWorkItemFlags, execute work: () throws -> T) rethrows -> T {
262 if flags == .barrier {
263 return try self._syncHelper(fn: _syncBarrier, execute: work, rescue: { throw $0 })
264 } else if #available(OSX 10.10, iOS 8.0, *), !flags.isEmpty {
265 return try self._syncHelper(fn: sync, flags: flags, execute: work, rescue: { throw $0 })
266 } else {
267 return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 })
268 }
269 }
270
271 public func asyncAfter(
272 deadline: DispatchTime,
273 qos: DispatchQoS = .unspecified,
274 flags: DispatchWorkItemFlags = [],
275 execute work: @escaping @convention(block) () -> Void)
276 {
277 if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty {
278 let item = DispatchWorkItem(qos: qos, flags: flags, block: work)
279 CDispatch.dispatch_after(deadline.rawValue, self.__wrapped, item._block)
280 } else {
281 CDispatch.dispatch_after(deadline.rawValue, self.__wrapped, work)
282 }
283 }
284
285 public func asyncAfter(
286 wallDeadline: DispatchWallTime,
287 qos: DispatchQoS = .unspecified,
288 flags: DispatchWorkItemFlags = [],
289 execute work: @escaping @convention(block) () -> Void)
290 {
291 if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty {
292 let item = DispatchWorkItem(qos: qos, flags: flags, block: work)
293 CDispatch.dispatch_after(wallDeadline.rawValue, self.__wrapped, item._block)
294 } else {
295 CDispatch.dispatch_after(wallDeadline.rawValue, self.__wrapped, work)
296 }
297 }
298
299 @available(OSX 10.10, iOS 8.0, *)
300 public func asyncAfter(deadline: DispatchTime, execute: DispatchWorkItem) {
301 CDispatch.dispatch_after(deadline.rawValue, self.__wrapped, execute._block)
302 }
303
304 @available(OSX 10.10, iOS 8.0, *)
305 public func asyncAfter(wallDeadline: DispatchWallTime, execute: DispatchWorkItem) {
306 CDispatch.dispatch_after(wallDeadline.rawValue, self.__wrapped, execute._block)
307 }
308
309 @available(OSX 10.10, iOS 8.0, *)
310 public var qos: DispatchQoS {
311 var relPri: Int32 = 0
312 let cls = DispatchQoS.QoSClass(rawValue: _OSQoSClass(qosClass: dispatch_queue_get_qos_class(self.__wrapped, &relPri))!)!
313 return DispatchQoS(qosClass: cls, relativePriority: Int(relPri))
314 }
315
316 public func getSpecific<T>(key: DispatchSpecificKey<T>) -> T? {
317 let k = Unmanaged.passUnretained(key).toOpaque()
318 if let p = dispatch_queue_get_specific(self.__wrapped, k) {
319 let v = Unmanaged<_DispatchSpecificValue<T>>
320 .fromOpaque(p)
321 .takeUnretainedValue()
322 return v.value
323 }
324 return nil
325 }
326
327 public func setSpecific<T>(key: DispatchSpecificKey<T>, value: T) {
328 let v = _DispatchSpecificValue(value: value)
329 let k = Unmanaged.passUnretained(key).toOpaque()
330 let p = Unmanaged.passRetained(v).toOpaque()
331 dispatch_queue_set_specific(self.__wrapped, k, p, _destructDispatchSpecificValue)
332 }
333 }
334
335 private func _destructDispatchSpecificValue(ptr: UnsafeMutableRawPointer?) {
336 if let p = ptr {
337 Unmanaged<AnyObject>.fromOpaque(p).release()
338 }
339 }
340
341 @_silgen_name("_swift_dispatch_queue_concurrent")
342 internal func _swift_dispatch_queue_concurrent() -> dispatch_queue_attr_t
343
344 @_silgen_name("_swift_dispatch_get_main_queue")
345 internal func _swift_dispatch_get_main_queue() -> dispatch_queue_t
346
347 @_silgen_name("_swift_dispatch_apply_current_root_queue")
348 internal func _swift_dispatch_apply_current_root_queue() -> dispatch_queue_t
349
350 @_silgen_name("_swift_dispatch_apply_current")
351 internal func _swift_dispatch_apply_current(_ iterations: Int, _ block: @convention(block) (Int) -> Void)