]> git.saurik.com Git - apple/libdispatch.git/blob - src/swift/Queue.swift
libdispatch-703.30.5.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 struct DispatchQueueAttributes : OptionSet {
18 public let rawValue: UInt64
19 public init(rawValue: UInt64) { self.rawValue = rawValue }
20
21 public static let serial = DispatchQueueAttributes(rawValue: 0<<0)
22 public static let concurrent = DispatchQueueAttributes(rawValue: 1<<1)
23
24 @available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
25 public static let initiallyInactive = DispatchQueueAttributes(rawValue: 1<<2)
26
27 @available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
28 public static let autoreleaseInherit = DispatchQueueAttributes(rawValue: 1<<3)
29
30 @available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
31 public static let autoreleaseWorkItem = DispatchQueueAttributes(rawValue: 1<<4)
32
33 @available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
34 public static let autoreleaseNever = DispatchQueueAttributes(rawValue: 1<<5)
35
36 @available(OSX 10.10, iOS 8.0, *)
37 public static let qosUserInteractive = DispatchQueueAttributes(rawValue: 1<<6)
38
39 @available(OSX 10.10, iOS 8.0, *)
40 public static let qosUserInitiated = DispatchQueueAttributes(rawValue: 1<<7)
41
42 @available(OSX 10.10, iOS 8.0, *)
43 public static let qosDefault = DispatchQueueAttributes(rawValue: 1<<8)
44
45 @available(OSX 10.10, iOS 8.0, *)
46 public static let qosUtility = DispatchQueueAttributes(rawValue: 1<<9)
47
48 @available(OSX 10.10, iOS 8.0, *)
49 public static let qosBackground = DispatchQueueAttributes(rawValue: 1<<10)
50
51 @available(*, deprecated, message: ".noQoS has no effect, it should not be used")
52 public static let noQoS = DispatchQueueAttributes(rawValue: 1<<11)
53
54 private var attr: dispatch_queue_attr_t? {
55 var attr: dispatch_queue_attr_t?
56
57 if self.contains(.concurrent) {
58 attr = _swift_dispatch_queue_concurrent()
59 }
60 if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
61 if self.contains(.initiallyInactive) {
62 attr = CDispatch.dispatch_queue_attr_make_initially_inactive(attr)
63 }
64 if self.contains(.autoreleaseWorkItem) {
65 // DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM
66 attr = CDispatch.dispatch_queue_attr_make_with_autorelease_frequency(attr, dispatch_autorelease_frequency_t(1))
67 } else if self.contains(.autoreleaseInherit) {
68 // DISPATCH_AUTORELEASE_FREQUENCY_INHERIT
69 attr = CDispatch.dispatch_queue_attr_make_with_autorelease_frequency(attr, dispatch_autorelease_frequency_t(0))
70 } else if self.contains(.autoreleaseNever) {
71 // DISPATCH_AUTORELEASE_FREQUENCY_NEVER
72 attr = CDispatch.dispatch_queue_attr_make_with_autorelease_frequency(attr, dispatch_autorelease_frequency_t(2))
73 }
74 }
75 if #available(OSX 10.10, iOS 8.0, *) {
76 if self.contains(.qosUserInteractive) {
77 attr = CDispatch.dispatch_queue_attr_make_with_qos_class(attr, _OSQoSClass.QOS_CLASS_USER_INTERACTIVE.rawValue, 0)
78 } else if self.contains(.qosUserInitiated) {
79 attr = CDispatch.dispatch_queue_attr_make_with_qos_class(attr, _OSQoSClass.QOS_CLASS_USER_INITIATED.rawValue, 0)
80 } else if self.contains(.qosDefault) {
81 attr = CDispatch.dispatch_queue_attr_make_with_qos_class(attr, _OSQoSClass.QOS_CLASS_DEFAULT.rawValue, 0)
82 } else if self.contains(.qosUtility) {
83 attr = CDispatch.dispatch_queue_attr_make_with_qos_class(attr, _OSQoSClass.QOS_CLASS_UTILITY.rawValue, 0)
84 } else if self.contains(.qosBackground) {
85 attr = CDispatch.dispatch_queue_attr_make_with_qos_class(attr, _OSQoSClass.QOS_CLASS_BACKGROUND.rawValue, 0)
86 }
87 }
88 return attr
89 }
90 }
91
92
93 public final class DispatchSpecificKey<T> {
94 public init() {}
95 }
96
97 internal class _DispatchSpecificValue<T> {
98 internal let value: T
99 internal init(value: T) { self.value = value }
100 }
101
102 public extension DispatchQueue {
103
104 public struct GlobalAttributes : OptionSet {
105 public let rawValue: UInt64
106 public init(rawValue: UInt64) { self.rawValue = rawValue }
107
108 @available(OSX 10.10, iOS 8.0, *)
109 public static let qosUserInteractive = GlobalAttributes(rawValue: 1<<0)
110
111 @available(OSX 10.10, iOS 8.0, *)
112 public static let qosUserInitiated = GlobalAttributes(rawValue: 1<<1)
113
114 @available(OSX 10.10, iOS 8.0, *)
115 public static let qosDefault = GlobalAttributes(rawValue: 1<<2)
116
117 @available(OSX 10.10, iOS 8.0, *)
118 public static let qosUtility = GlobalAttributes(rawValue: 1<<3)
119
120 @available(OSX 10.10, iOS 8.0, *)
121 public static let qosBackground = GlobalAttributes(rawValue: 1<<4)
122
123 // Avoid using our own deprecated constants here by declaring
124 // non-deprecated constants and then basing the public ones on those.
125 internal static let _priorityHigh = GlobalAttributes(rawValue: 1<<5)
126 internal static let _priorityDefault = GlobalAttributes(rawValue: 1<<6)
127 internal static let _priorityLow = GlobalAttributes(rawValue: 1<<7)
128 internal static let _priorityBackground = GlobalAttributes(rawValue: 1<<8)
129
130 @available(OSX, deprecated: 10.10, message: "Use qos attributes instead")
131 @available(*, deprecated: 8.0, message: "Use qos attributes instead")
132 public static let priorityHigh = _priorityHigh
133
134 @available(OSX, deprecated: 10.10, message: "Use qos attributes instead")
135 @available(*, deprecated: 8.0, message: "Use qos attributes instead")
136 public static let priorityDefault = _priorityDefault
137
138 @available(OSX, deprecated: 10.10, message: "Use qos attributes instead")
139 @available(*, deprecated: 8.0, message: "Use qos attributes instead")
140 public static let priorityLow = _priorityLow
141
142 @available(OSX, deprecated: 10.10, message: "Use qos attributes instead")
143 @available(*, deprecated: 8.0, message: "Use qos attributes instead")
144 public static let priorityBackground = _priorityBackground
145
146 internal var _translatedValue: Int {
147 if #available(OSX 10.10, iOS 8.0, *) {
148 if self.contains(.qosUserInteractive) { return Int(_OSQoSClass.QOS_CLASS_USER_INTERACTIVE.rawValue) }
149 else if self.contains(.qosUserInitiated) { return Int(_OSQoSClass.QOS_CLASS_USER_INITIATED.rawValue) }
150 else if self.contains(.qosDefault) { return Int(_OSQoSClass.QOS_CLASS_DEFAULT.rawValue) }
151 else if self.contains(.qosUtility) { return Int(_OSQoSClass.QOS_CLASS_UTILITY.rawValue) }
152 else { return Int(_OSQoSClass.QOS_CLASS_BACKGROUND.rawValue) }
153 }
154 if self.contains(._priorityHigh) { return 2 } // DISPATCH_QUEUE_PRIORITY_HIGH
155 else if self.contains(._priorityDefault) { return 0 } // DISPATCH_QUEUE_PRIORITY_DEFAULT
156 else if self.contains(._priorityLow) { return -2 } // // DISPATCH_QUEUE_PRIORITY_LOW
157 else if self.contains(._priorityBackground) { return Int(Int16.min) } // // DISPATCH_QUEUE_PRIORITY_BACKGROUND
158 return 0
159 }
160 }
161
162 public class func concurrentPerform(iterations: Int, execute work: @noescape (Int) -> Void) {
163 _swift_dispatch_apply_current(iterations, work)
164 }
165
166 public class var main: DispatchQueue {
167 return DispatchQueue(queue: _swift_dispatch_get_main_queue())
168 }
169
170 public class func global(attributes: GlobalAttributes = []) -> DispatchQueue {
171 // SubOptimal? Should we be caching these global DispatchQueue objects?
172 return DispatchQueue(queue:dispatch_get_global_queue(attributes._translatedValue, 0))
173 }
174
175 public class func getSpecific<T>(key: DispatchSpecificKey<T>) -> T? {
176 let k = Unmanaged.passUnretained(key).toOpaque()
177 if let p = CDispatch.dispatch_get_specific(k) {
178 let v = Unmanaged<_DispatchSpecificValue<T>>
179 .fromOpaque(p)
180 .takeUnretainedValue()
181 return v.value
182 }
183 return nil
184 }
185
186 public convenience init(
187 label: String,
188 attributes: DispatchQueueAttributes = .serial,
189 target: DispatchQueue? = nil)
190 {
191 if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
192 self.init(__label: label, attr: attributes.attr, queue: target)
193 } else {
194 self.init(__label: label, attr: attributes.attr)
195 if let tq = target { self.setTarget(queue: tq) }
196 }
197 }
198
199 public var label: String {
200 return String(validatingUTF8: dispatch_queue_get_label(self.__wrapped))!
201 }
202
203 @available(OSX 10.10, iOS 8.0, *)
204 public func sync(execute workItem: DispatchWorkItem) {
205 dispatch_sync(self.__wrapped, workItem._block)
206 }
207
208 @available(OSX 10.10, iOS 8.0, *)
209 public func async(execute workItem: DispatchWorkItem) {
210 // _swift_dispatch_{group,}_async preserves the @convention(block)
211 // for work item blocks.
212 if let g = workItem._group {
213 dispatch_group_async(g.__wrapped, self.__wrapped, workItem._block)
214 } else {
215 dispatch_async(self.__wrapped, workItem._block)
216 }
217 }
218
219 public func async(group: DispatchGroup? = nil, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute work: @convention(block) () -> Void) {
220 if group == nil && qos == .unspecified && flags.isEmpty {
221 // Fast-path route for the most common API usage
222 dispatch_async(self.__wrapped, work)
223 return
224 }
225
226 if #available(OSX 10.10, iOS 8.0, *), (qos != .unspecified || !flags.isEmpty) {
227 let workItem = DispatchWorkItem(qos: qos, flags: flags, block: work)
228 if let g = group {
229 dispatch_group_async(g.__wrapped, self.__wrapped, workItem._block)
230 } else {
231 dispatch_async(self.__wrapped, workItem._block)
232 }
233 } else {
234 if let g = group {
235 dispatch_group_async(g.__wrapped, self.__wrapped, work)
236 } else {
237 dispatch_async(self.__wrapped, work)
238 }
239 }
240 }
241
242 private func _syncBarrier(block: @noescape () -> ()) {
243 dispatch_barrier_sync(self.__wrapped, block)
244 }
245
246 private func _syncHelper<T>(
247 fn: (@noescape () -> ()) -> (),
248 execute work: @noescape () throws -> T,
249 rescue: ((Swift.Error) throws -> (T))) rethrows -> T
250 {
251 var result: T?
252 var error: Swift.Error?
253 fn {
254 do {
255 result = try work()
256 } catch let e {
257 error = e
258 }
259 }
260 if let e = error {
261 return try rescue(e)
262 } else {
263 return result!
264 }
265 }
266
267 @available(OSX 10.10, iOS 8.0, *)
268 private func _syncHelper<T>(
269 fn: (DispatchWorkItem) -> (),
270 flags: DispatchWorkItemFlags,
271 execute work: @noescape () throws -> T,
272 rescue: ((Swift.Error) throws -> (T))) rethrows -> T
273 {
274 var result: T?
275 var error: Swift.Error?
276 let workItem = DispatchWorkItem(flags: flags, noescapeBlock: {
277 do {
278 result = try work()
279 } catch let e {
280 error = e
281 }
282 })
283 fn(workItem)
284 if let e = error {
285 return try rescue(e)
286 } else {
287 return result!
288 }
289 }
290
291 public func sync<T>(execute work: @noescape () throws -> T) rethrows -> T {
292 return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 })
293 }
294
295 public func sync<T>(flags: DispatchWorkItemFlags, execute work: @noescape () throws -> T) rethrows -> T {
296 if flags == .barrier {
297 return try self._syncHelper(fn: _syncBarrier, execute: work, rescue: { throw $0 })
298 } else if #available(OSX 10.10, iOS 8.0, *), !flags.isEmpty {
299 return try self._syncHelper(fn: sync, flags: flags, execute: work, rescue: { throw $0 })
300 } else {
301 return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 })
302 }
303 }
304
305 public func after(when: DispatchTime, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute work: @convention(block) () -> Void) {
306 if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty {
307 let item = DispatchWorkItem(qos: qos, flags: flags, block: work)
308 dispatch_after(when.rawValue, self.__wrapped, item._block)
309 } else {
310 dispatch_after(when.rawValue, self.__wrapped, work)
311 }
312 }
313
314 @available(OSX 10.10, iOS 8.0, *)
315 public func after(when: DispatchTime, execute: DispatchWorkItem) {
316 dispatch_after(when.rawValue, self.__wrapped, execute._block)
317 }
318
319 public func after(walltime when: DispatchWallTime, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute work: @convention(block) () -> Void) {
320 if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty {
321 let item = DispatchWorkItem(qos: qos, flags: flags, block: work)
322 dispatch_after(when.rawValue, self.__wrapped, item._block)
323 } else {
324 dispatch_after(when.rawValue, self.__wrapped, work)
325 }
326 }
327
328 @available(OSX 10.10, iOS 8.0, *)
329 public func after(walltime when: DispatchWallTime, execute: DispatchWorkItem) {
330 dispatch_after(when.rawValue, self.__wrapped, execute._block)
331 }
332
333 @available(OSX 10.10, iOS 8.0, *)
334 public var qos: DispatchQoS {
335 var relPri: Int32 = 0
336 let cls = DispatchQoS.QoSClass(qosClass: _OSQoSClass(qosClass: dispatch_queue_get_qos_class(self.__wrapped, &relPri))!)!
337 return DispatchQoS(qosClass: cls, relativePriority: Int(relPri))
338 }
339
340 public func getSpecific<T>(key: DispatchSpecificKey<T>) -> T? {
341 let k = Unmanaged.passUnretained(key).toOpaque()
342 if let p = dispatch_queue_get_specific(self.__wrapped, k) {
343 let v = Unmanaged<_DispatchSpecificValue<T>>
344 .fromOpaque(p)
345 .takeUnretainedValue()
346 return v.value
347 }
348 return nil
349 }
350
351 public func setSpecific<T>(key: DispatchSpecificKey<T>, value: T) {
352 let v = _DispatchSpecificValue(value: value)
353 let k = Unmanaged.passUnretained(key).toOpaque()
354 let p = Unmanaged.passRetained(v).toOpaque()
355 dispatch_queue_set_specific(self.__wrapped, k, p, _destructDispatchSpecificValue)
356 }
357 }
358
359 extension DispatchQueue {
360 @available(*, deprecated, renamed: "DispatchQueue.sync(self:execute:)")
361 public func synchronously(execute work: @noescape () -> ()) {
362 sync(execute: work)
363 }
364
365 @available(OSX, introduced: 10.10, deprecated: 10.12, renamed: "DispatchQueue.sync(self:execute:)")
366 @available(iOS, introduced: 8.0, deprecated: 10.0, renamed: "DispatchQueue.sync(self:execute:)")
367 @available(*, deprecated, renamed: "DispatchQueue.sync(self:execute:)")
368 public func synchronously(execute workItem: DispatchWorkItem) {
369 sync(execute: workItem)
370 }
371
372 @available(OSX, introduced: 10.10, deprecated: 10.12, renamed: "DispatchQueue.async(self:execute:)")
373 @available(iOS, introduced: 8.0, deprecated: 10.0, renamed: "DispatchQueue.async(self:execute:)")
374 @available(*, deprecated, renamed: "DispatchQueue.async(self:execute:)")
375 public func asynchronously(execute workItem: DispatchWorkItem) {
376 async(execute: workItem)
377 }
378
379 @available(*, deprecated, renamed: "DispatchQueue.async(self:group:qos:flags:execute:)")
380 public func asynchronously(group: DispatchGroup? = nil, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute work: @convention(block) () -> Void) {
381 async(group: group, qos: qos, flags: flags, execute: work)
382 }
383
384 @available(*, deprecated, renamed: "DispatchQueue.sync(self:execute:)")
385 public func synchronously<T>(execute work: @noescape () throws -> T) rethrows -> T {
386 return try sync(execute: work)
387 }
388
389 @available(*, deprecated, renamed: "DispatchQueue.sync(self:flags:execute:)")
390 public func synchronously<T>(flags: DispatchWorkItemFlags, execute work: @noescape () throws -> T) rethrows -> T {
391 return try sync(flags: flags, execute: work)
392 }
393
394 @available(*, deprecated, renamed: "DispatchQueue.concurrentPerform(iterations:execute:)")
395 public func apply(applier iterations: Int, execute block: @noescape (Int) -> Void) {
396 DispatchQueue.concurrentPerform(iterations: iterations, execute: block)
397 }
398
399 @available(*, deprecated, renamed: "DispatchQueue.setTarget(self:queue:)")
400 public func setTargetQueue(queue: DispatchQueue) {
401 self.setTarget(queue: queue)
402 }
403 }
404
405 private func _destructDispatchSpecificValue(ptr: UnsafeMutablePointer<Void>?) {
406 if let p = ptr {
407 Unmanaged<AnyObject>.fromOpaque(p).release()
408 }
409 }
410
411 @_silgen_name("_swift_dispatch_queue_concurrent")
412 internal func _swift_dispatch_queue_concurrent() -> dispatch_queue_attr_t
413
414 @_silgen_name("_swift_dispatch_get_main_queue")
415 internal func _swift_dispatch_get_main_queue() -> dispatch_queue_t
416
417 @_silgen_name("_swift_dispatch_apply_current_root_queue")
418 internal func _swift_dispatch_apply_current_root_queue() -> dispatch_queue_t
419
420 @_silgen_name("_swift_dispatch_apply_current")
421 internal func _swift_dispatch_apply_current(_ iterations: Int, _ block: @convention(block) @noescape (Int) -> Void)