]> git.saurik.com Git - apple/libdispatch.git/blob - src/swift/Queue.swift
libdispatch-913.30.4.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 withoutActuallyEscaping(work) { _work in
220 fn {
221 do {
222 result = try _work()
223 } catch let e {
224 error = e
225 }
226 }
227 }
228 if let e = error {
229 return try rescue(e)
230 } else {
231 return result!
232 }
233 }
234
235 @available(OSX 10.10, iOS 8.0, *)
236 private func _syncHelper<T>(
237 fn: (DispatchWorkItem) -> (),
238 flags: DispatchWorkItemFlags,
239 execute work: () throws -> T,
240 rescue: @escaping ((Swift.Error) throws -> (T))) rethrows -> T
241 {
242 var result: T?
243 var error: Swift.Error?
244 let workItem = DispatchWorkItem(flags: flags, noescapeBlock: {
245 do {
246 result = try work()
247 } catch let e {
248 error = e
249 }
250 })
251 fn(workItem)
252 if let e = error {
253 return try rescue(e)
254 } else {
255 return result!
256 }
257 }
258
259 public func sync<T>(execute work: () throws -> T) rethrows -> T {
260 return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 })
261 }
262
263 public func sync<T>(flags: DispatchWorkItemFlags, execute work: () throws -> T) rethrows -> T {
264 if flags == .barrier {
265 return try self._syncHelper(fn: _syncBarrier, execute: work, rescue: { throw $0 })
266 } else if #available(OSX 10.10, iOS 8.0, *), !flags.isEmpty {
267 return try self._syncHelper(fn: sync, flags: flags, execute: work, rescue: { throw $0 })
268 } else {
269 return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 })
270 }
271 }
272
273 public func asyncAfter(
274 deadline: DispatchTime,
275 qos: DispatchQoS = .unspecified,
276 flags: DispatchWorkItemFlags = [],
277 execute work: @escaping @convention(block) () -> Void)
278 {
279 if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty {
280 let item = DispatchWorkItem(qos: qos, flags: flags, block: work)
281 CDispatch.dispatch_after(deadline.rawValue, self.__wrapped, item._block)
282 } else {
283 CDispatch.dispatch_after(deadline.rawValue, self.__wrapped, work)
284 }
285 }
286
287 public func asyncAfter(
288 wallDeadline: DispatchWallTime,
289 qos: DispatchQoS = .unspecified,
290 flags: DispatchWorkItemFlags = [],
291 execute work: @escaping @convention(block) () -> Void)
292 {
293 if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty {
294 let item = DispatchWorkItem(qos: qos, flags: flags, block: work)
295 CDispatch.dispatch_after(wallDeadline.rawValue, self.__wrapped, item._block)
296 } else {
297 CDispatch.dispatch_after(wallDeadline.rawValue, self.__wrapped, work)
298 }
299 }
300
301 @available(OSX 10.10, iOS 8.0, *)
302 public func asyncAfter(deadline: DispatchTime, execute: DispatchWorkItem) {
303 CDispatch.dispatch_after(deadline.rawValue, self.__wrapped, execute._block)
304 }
305
306 @available(OSX 10.10, iOS 8.0, *)
307 public func asyncAfter(wallDeadline: DispatchWallTime, execute: DispatchWorkItem) {
308 CDispatch.dispatch_after(wallDeadline.rawValue, self.__wrapped, execute._block)
309 }
310
311 @available(OSX 10.10, iOS 8.0, *)
312 public var qos: DispatchQoS {
313 var relPri: Int32 = 0
314 let cls = DispatchQoS.QoSClass(rawValue: _OSQoSClass(qosClass: dispatch_queue_get_qos_class(self.__wrapped, &relPri))!)!
315 return DispatchQoS(qosClass: cls, relativePriority: Int(relPri))
316 }
317
318 public func getSpecific<T>(key: DispatchSpecificKey<T>) -> T? {
319 let k = Unmanaged.passUnretained(key).toOpaque()
320 if let p = dispatch_queue_get_specific(self.__wrapped, k) {
321 let v = Unmanaged<_DispatchSpecificValue<T>>
322 .fromOpaque(p)
323 .takeUnretainedValue()
324 return v.value
325 }
326 return nil
327 }
328
329 public func setSpecific<T>(key: DispatchSpecificKey<T>, value: T?) {
330 let k = Unmanaged.passUnretained(key).toOpaque()
331 let v = value.flatMap { _DispatchSpecificValue(value: $0) }
332 let p = v.flatMap { Unmanaged.passRetained($0).toOpaque() }
333 dispatch_queue_set_specific(self.__wrapped, k, p, _destructDispatchSpecificValue)
334 }
335
336 #if os(Android)
337 @_silgen_name("_dispatch_install_thread_detach_callback")
338 private static func _dispatch_install_thread_detach_callback(_ cb: @escaping @convention(c) () -> Void)
339
340 public static func setThreadDetachCallback(_ cb: @escaping @convention(c) () -> Void) {
341 _dispatch_install_thread_detach_callback(cb)
342 }
343 #endif
344 }
345
346 private func _destructDispatchSpecificValue(ptr: UnsafeMutableRawPointer?) {
347 if let p = ptr {
348 Unmanaged<AnyObject>.fromOpaque(p).release()
349 }
350 }
351
352 @_silgen_name("_swift_dispatch_queue_concurrent")
353 internal func _swift_dispatch_queue_concurrent() -> dispatch_queue_attr_t
354
355 @_silgen_name("_swift_dispatch_get_main_queue")
356 internal func _swift_dispatch_get_main_queue() -> dispatch_queue_t
357
358 @_silgen_name("_swift_dispatch_apply_current")
359 internal func _swift_dispatch_apply_current(_ iterations: Int, _ block: @convention(block) (Int) -> Void)