| //===----------------------------------------------------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // dispatch/queue.h |
| |
| import CDispatch |
| import _SwiftDispatchOverlayShims |
| |
| public final class DispatchSpecificKey<T> { |
| public init() {} |
| } |
| |
| internal class _DispatchSpecificValue<T> { |
| internal let value: T |
| internal init(value: T) { self.value = value } |
| } |
| |
| public extension DispatchQueue { |
| public struct Attributes : OptionSet { |
| public let rawValue: UInt64 |
| public init(rawValue: UInt64) { self.rawValue = rawValue } |
| |
| public static let concurrent = Attributes(rawValue: 1<<1) |
| |
| @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) |
| public static let initiallyInactive = Attributes(rawValue: 1<<2) |
| |
| fileprivate func _attr() -> dispatch_queue_attr_t? { |
| var attr: dispatch_queue_attr_t? = nil |
| |
| if self.contains(.concurrent) { |
| attr = _swift_dispatch_queue_concurrent() |
| } |
| if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { |
| if self.contains(.initiallyInactive) { |
| attr = CDispatch.dispatch_queue_attr_make_initially_inactive(attr) |
| } |
| } |
| return attr |
| } |
| } |
| |
| public enum GlobalQueuePriority { |
| @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") |
| @available(*, deprecated: 8.0, message: "Use qos attributes instead") |
| case high |
| |
| @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") |
| @available(*, deprecated: 8.0, message: "Use qos attributes instead") |
| case `default` |
| |
| @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") |
| @available(*, deprecated: 8.0, message: "Use qos attributes instead") |
| case low |
| |
| @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") |
| @available(*, deprecated: 8.0, message: "Use qos attributes instead") |
| case background |
| |
| internal var _translatedValue: Int { |
| switch self { |
| case .high: return 2 // DISPATCH_QUEUE_PRIORITY_HIGH |
| case .default: return 0 // DISPATCH_QUEUE_PRIORITY_DEFAULT |
| case .low: return -2 // DISPATCH_QUEUE_PRIORITY_LOW |
| case .background: return Int(Int16.min) // DISPATCH_QUEUE_PRIORITY_BACKGROUND |
| } |
| } |
| } |
| |
| public enum AutoreleaseFrequency { |
| case inherit |
| |
| @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) |
| case workItem |
| |
| @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) |
| case never |
| |
| internal func _attr(attr: dispatch_queue_attr_t?) -> dispatch_queue_attr_t? { |
| if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { |
| switch self { |
| case .inherit: |
| // DISPATCH_AUTORELEASE_FREQUENCY_INHERIT |
| return CDispatch.dispatch_queue_attr_make_with_autorelease_frequency(attr, dispatch_autorelease_frequency_t(0)) |
| case .workItem: |
| // DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM |
| return CDispatch.dispatch_queue_attr_make_with_autorelease_frequency(attr, dispatch_autorelease_frequency_t(1)) |
| case .never: |
| // DISPATCH_AUTORELEASE_FREQUENCY_NEVER |
| return CDispatch.dispatch_queue_attr_make_with_autorelease_frequency(attr, dispatch_autorelease_frequency_t(2)) |
| } |
| } else { |
| return attr |
| } |
| } |
| } |
| |
| public class func concurrentPerform(iterations: Int, execute work: (Int) -> Void) { |
| _swift_dispatch_apply_current(iterations, work) |
| } |
| |
| public class var main: DispatchQueue { |
| return DispatchQueue(queue: _swift_dispatch_get_main_queue()) |
| } |
| |
| @available(macOS, deprecated: 10.10, message: "") |
| @available(*, deprecated: 8.0, message: "") |
| public class func global(priority: GlobalQueuePriority) -> DispatchQueue { |
| return DispatchQueue(queue: CDispatch.dispatch_get_global_queue(Int(priority._translatedValue), 0)) |
| } |
| |
| @available(macOS 10.10, iOS 8.0, *) |
| public class func global(qos: DispatchQoS.QoSClass = .default) -> DispatchQueue { |
| return DispatchQueue(queue: CDispatch.dispatch_get_global_queue(Int(qos.rawValue.rawValue), 0)) |
| } |
| |
| public class func getSpecific<T>(key: DispatchSpecificKey<T>) -> T? { |
| let k = Unmanaged.passUnretained(key).toOpaque() |
| if let p = CDispatch.dispatch_get_specific(k) { |
| let v = Unmanaged<_DispatchSpecificValue<T>> |
| .fromOpaque(p) |
| .takeUnretainedValue() |
| return v.value |
| } |
| return nil |
| } |
| |
| public convenience init( |
| label: String, |
| qos: DispatchQoS = .unspecified, |
| attributes: Attributes = [], |
| autoreleaseFrequency: AutoreleaseFrequency = .inherit, |
| target: DispatchQueue? = nil) |
| { |
| var attr = attributes._attr() |
| if autoreleaseFrequency != .inherit { |
| attr = autoreleaseFrequency._attr(attr: attr) |
| } |
| if #available(macOS 10.10, iOS 8.0, *), qos != .unspecified { |
| attr = CDispatch.dispatch_queue_attr_make_with_qos_class(attr, qos.qosClass.rawValue.rawValue, Int32(qos.relativePriority)) |
| } |
| |
| if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { |
| self.init(__label: label, attr: attr, queue: target) |
| } else { |
| self.init(__label: label, attr: attr) |
| if let tq = target { self.setTarget(queue: tq) } |
| } |
| } |
| |
| public var label: String { |
| return String(validatingUTF8: dispatch_queue_get_label(self.__wrapped))! |
| } |
| |
| /// |
| /// Submits a block for synchronous execution on this queue. |
| /// |
| /// Submits a work item to a dispatch queue like `async(execute:)`, however |
| /// `sync(execute:)` will not return until the work item has finished. |
| /// |
| /// Work items submitted to a queue with `sync(execute:)` do not observe certain |
| /// queue attributes of that queue when invoked (such as autorelease frequency |
| /// and QoS class). |
| /// |
| /// Calls to `sync(execute:)` targeting the current queue will result |
| /// in deadlock. Use of `sync(execute:)` is also subject to the same |
| /// multi-party deadlock problems that may result from the use of a mutex. |
| /// Use of `async(execute:)` is preferred. |
| /// |
| /// As an optimization, `sync(execute:)` invokes the work item on the thread which |
| /// submitted it, except when the queue is the main queue or |
| /// a queue targetting it. |
| /// |
| /// - parameter execute: The work item to be invoked on the queue. |
| /// - SeeAlso: `async(execute:)` |
| /// |
| @available(macOS 10.10, iOS 8.0, *) |
| public func sync(execute workItem: DispatchWorkItem) { |
| CDispatch.dispatch_sync(self.__wrapped, workItem._block) |
| } |
| |
| /// |
| /// Submits a work item for asynchronous execution on a dispatch queue. |
| /// |
| /// `async(execute:)` is the fundamental mechanism for submitting |
| /// work items to a dispatch queue. |
| /// |
| /// Calls to `async(execute:)` always return immediately after the work item has |
| /// been submitted, and never wait for the work item to be invoked. |
| /// |
| /// The target queue determines whether the work item will be invoked serially or |
| /// concurrently with respect to other work items submitted to that same queue. |
| /// Serial queues are processed concurrently with respect to each other. |
| /// |
| /// - parameter execute: The work item to be invoked on the queue. |
| /// - SeeAlso: `sync(execute:)` |
| /// |
| @available(macOS 10.10, iOS 8.0, *) |
| public func async(execute workItem: DispatchWorkItem) { |
| CDispatch.dispatch_async(self.__wrapped, workItem._block) |
| } |
| |
| /// |
| /// Submits a work item to a dispatch queue and associates it with the given |
| /// dispatch group. The dispatch group may be used to wait for the completion |
| /// of the work items it references. |
| /// |
| /// - parameter group: the dispatch group to associate with the submitted block. |
| /// - parameter execute: The work item to be invoked on the queue. |
| /// - SeeAlso: `sync(execute:)` |
| /// |
| @available(macOS 10.10, iOS 8.0, *) |
| public func async(group: DispatchGroup, execute workItem: DispatchWorkItem) { |
| CDispatch.dispatch_group_async(group.__wrapped, self.__wrapped, workItem._block) |
| } |
| |
| /// |
| /// Submits a work item to a dispatch queue and optionally associates it with a |
| /// dispatch group. The dispatch group may be used to wait for the completion |
| /// of the work items it references. |
| /// |
| /// - parameter group: the dispatch group to associate with the submitted |
| /// work item. If this is `nil`, the work item is not associated with a group. |
| /// - parameter flags: flags that control the execution environment of the |
| /// - parameter qos: the QoS at which the work item should be executed. |
| /// Defaults to `DispatchQoS.unspecified`. |
| /// - parameter flags: flags that control the execution environment of the |
| /// work item. |
| /// - parameter execute: The work item to be invoked on the queue. |
| /// - SeeAlso: `sync(execute:)` |
| /// - SeeAlso: `DispatchQoS` |
| /// - SeeAlso: `DispatchWorkItemFlags` |
| /// |
| public func async( |
| group: DispatchGroup? = nil, |
| qos: DispatchQoS = .unspecified, |
| flags: DispatchWorkItemFlags = [], |
| execute work: @escaping @convention(block) () -> Void) |
| { |
| if group == nil && qos == .unspecified { |
| // Fast-path route for the most common API usage |
| if flags.isEmpty { |
| CDispatch.dispatch_async(self.__wrapped, work) |
| return |
| } else if flags == .barrier { |
| CDispatch.dispatch_barrier_async(self.__wrapped, work) |
| return |
| } |
| } |
| |
| var block: @convention(block) () -> Void = work |
| if #available(macOS 10.10, iOS 8.0, *), (qos != .unspecified || !flags.isEmpty) { |
| let workItem = DispatchWorkItem(qos: qos, flags: flags, block: work) |
| block = workItem._block |
| } |
| |
| if let g = group { |
| CDispatch.dispatch_group_async(g.__wrapped, self.__wrapped, block) |
| } else { |
| CDispatch.dispatch_async(self.__wrapped, block) |
| } |
| } |
| |
| private func _syncBarrier(block: () -> ()) { |
| CDispatch.dispatch_barrier_sync(self.__wrapped, block) |
| } |
| |
| private func _syncHelper<T>( |
| fn: (() -> ()) -> (), |
| execute work: () throws -> T, |
| rescue: ((Swift.Error) throws -> (T))) rethrows -> T |
| { |
| var result: T? |
| var error: Swift.Error? |
| withoutActuallyEscaping(work) { _work in |
| fn { |
| do { |
| result = try _work() |
| } catch let e { |
| error = e |
| } |
| } |
| } |
| if let e = error { |
| return try rescue(e) |
| } else { |
| return result! |
| } |
| } |
| |
| @available(macOS 10.10, iOS 8.0, *) |
| private func _syncHelper<T>( |
| fn: (DispatchWorkItem) -> (), |
| flags: DispatchWorkItemFlags, |
| execute work: () throws -> T, |
| rescue: @escaping ((Swift.Error) throws -> (T))) rethrows -> T |
| { |
| var result: T? |
| var error: Swift.Error? |
| let workItem = DispatchWorkItem(flags: flags, noescapeBlock: { |
| do { |
| result = try work() |
| } catch let e { |
| error = e |
| } |
| }) |
| fn(workItem) |
| if let e = error { |
| return try rescue(e) |
| } else { |
| return result! |
| } |
| } |
| |
| /// |
| /// Submits a block for synchronous execution on this queue. |
| /// |
| /// Submits a work item to a dispatch queue like `sync(execute:)`, and returns |
| /// the value, of type `T`, returned by that work item. |
| /// |
| /// - parameter execute: The work item to be invoked on the queue. |
| /// - returns the value returned by the work item. |
| /// - SeeAlso: `sync(execute:)` |
| /// |
| public func sync<T>(execute work: () throws -> T) rethrows -> T { |
| return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 }) |
| } |
| |
| /// |
| /// Submits a block for synchronous execution on this queue. |
| /// |
| /// Submits a work item to a dispatch queue like `sync(execute:)`, and returns |
| /// the value, of type `T`, returned by that work item. |
| /// |
| /// - parameter flags: flags that control the execution environment of the |
| /// - parameter execute: The work item to be invoked on the queue. |
| /// - returns the value returned by the work item. |
| /// - SeeAlso: `sync(execute:)` |
| /// - SeeAlso: `DispatchWorkItemFlags` |
| /// |
| public func sync<T>(flags: DispatchWorkItemFlags, execute work: () throws -> T) rethrows -> T { |
| if flags == .barrier { |
| return try self._syncHelper(fn: _syncBarrier, execute: work, rescue: { throw $0 }) |
| } else if #available(macOS 10.10, iOS 8.0, *), !flags.isEmpty { |
| return try self._syncHelper(fn: sync, flags: flags, execute: work, rescue: { throw $0 }) |
| } else { |
| return try self._syncHelper(fn: sync, execute: work, rescue: { throw $0 }) |
| } |
| } |
| |
| /// |
| /// Submits a work item to a dispatch queue for asynchronous execution after |
| /// a specified time. |
| /// |
| /// - parameter: deadline the time after which the work item should be executed, |
| /// given as a `DispatchTime`. |
| /// - parameter qos: the QoS at which the work item should be executed. |
| /// Defaults to `DispatchQoS.unspecified`. |
| /// - parameter flags: flags that control the execution environment of the |
| /// work item. |
| /// - parameter execute: The work item to be invoked on the queue. |
| /// - SeeAlso: `async(execute:)` |
| /// - SeeAlso: `asyncAfter(deadline:execute:)` |
| /// - SeeAlso: `DispatchQoS` |
| /// - SeeAlso: `DispatchWorkItemFlags` |
| /// - SeeAlso: `DispatchTime` |
| /// |
| public func asyncAfter( |
| deadline: DispatchTime, |
| qos: DispatchQoS = .unspecified, |
| flags: DispatchWorkItemFlags = [], |
| execute work: @escaping @convention(block) () -> Void) |
| { |
| if #available(macOS 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty { |
| let item = DispatchWorkItem(qos: qos, flags: flags, block: work) |
| CDispatch.dispatch_after(deadline.rawValue, self.__wrapped, item._block) |
| } else { |
| CDispatch.dispatch_after(deadline.rawValue, self.__wrapped, work) |
| } |
| } |
| |
| /// |
| /// Submits a work item to a dispatch queue for asynchronous execution after |
| /// a specified time. |
| /// |
| /// - parameter: deadline the time after which the work item should be executed, |
| /// given as a `DispatchWallTime`. |
| /// - parameter qos: the QoS at which the work item should be executed. |
| /// Defaults to `DispatchQoS.unspecified`. |
| /// - parameter flags: flags that control the execution environment of the |
| /// work item. |
| /// - parameter execute: The work item to be invoked on the queue. |
| /// - SeeAlso: `async(execute:)` |
| /// - SeeAlso: `asyncAfter(wallDeadline:execute:)` |
| /// - SeeAlso: `DispatchQoS` |
| /// - SeeAlso: `DispatchWorkItemFlags` |
| /// - SeeAlso: `DispatchWallTime` |
| /// |
| public func asyncAfter( |
| wallDeadline: DispatchWallTime, |
| qos: DispatchQoS = .unspecified, |
| flags: DispatchWorkItemFlags = [], |
| execute work: @escaping @convention(block) () -> Void) |
| { |
| if #available(macOS 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty { |
| let item = DispatchWorkItem(qos: qos, flags: flags, block: work) |
| CDispatch.dispatch_after(wallDeadline.rawValue, self.__wrapped, item._block) |
| } else { |
| CDispatch.dispatch_after(wallDeadline.rawValue, self.__wrapped, work) |
| } |
| } |
| |
| /// |
| /// Submits a work item to a dispatch queue for asynchronous execution after |
| /// a specified time. |
| /// |
| /// - parameter: deadline the time after which the work item should be executed, |
| /// given as a `DispatchTime`. |
| /// - parameter execute: The work item to be invoked on the queue. |
| /// - SeeAlso: `asyncAfter(deadline:qos:flags:execute:)` |
| /// - SeeAlso: `DispatchTime` |
| /// |
| @available(macOS 10.10, iOS 8.0, *) |
| public func asyncAfter(deadline: DispatchTime, execute: DispatchWorkItem) { |
| CDispatch.dispatch_after(deadline.rawValue, self.__wrapped, execute._block) |
| } |
| |
| /// |
| /// Submits a work item to a dispatch queue for asynchronous execution after |
| /// a specified time. |
| /// |
| /// - parameter: deadline the time after which the work item should be executed, |
| /// given as a `DispatchWallTime`. |
| /// - parameter execute: The work item to be invoked on the queue. |
| /// - SeeAlso: `asyncAfter(wallDeadline:qos:flags:execute:)` |
| /// - SeeAlso: `DispatchTime` |
| /// |
| @available(macOS 10.10, iOS 8.0, *) |
| public func asyncAfter(wallDeadline: DispatchWallTime, execute: DispatchWorkItem) { |
| CDispatch.dispatch_after(wallDeadline.rawValue, self.__wrapped, execute._block) |
| } |
| |
| @available(macOS 10.10, iOS 8.0, *) |
| public var qos: DispatchQoS { |
| var relPri: Int32 = 0 |
| let cls = DispatchQoS.QoSClass(rawValue: _OSQoSClass(qosClass: dispatch_queue_get_qos_class(self.__wrapped, &relPri))!)! |
| return DispatchQoS(qosClass: cls, relativePriority: Int(relPri)) |
| } |
| |
| public func getSpecific<T>(key: DispatchSpecificKey<T>) -> T? { |
| let k = Unmanaged.passUnretained(key).toOpaque() |
| if let p = dispatch_queue_get_specific(self.__wrapped, k) { |
| let v = Unmanaged<_DispatchSpecificValue<T>> |
| .fromOpaque(p) |
| .takeUnretainedValue() |
| return v.value |
| } |
| return nil |
| } |
| |
| public func setSpecific<T>(key: DispatchSpecificKey<T>, value: T?) { |
| let k = Unmanaged.passUnretained(key).toOpaque() |
| let v = value.map { _DispatchSpecificValue(value: $0) } |
| let p = v.map { Unmanaged.passRetained($0).toOpaque() } |
| dispatch_queue_set_specific(self.__wrapped, k, p, _destructDispatchSpecificValue) |
| } |
| |
| #if os(Android) |
| public static func setThreadDetachCallback(_ cb: @escaping @convention(c) () -> Void) { |
| _dispatch_install_thread_detach_callback(cb) |
| } |
| #endif |
| } |
| |
| private func _destructDispatchSpecificValue(ptr: UnsafeMutableRawPointer?) { |
| if let p = ptr { |
| Unmanaged<AnyObject>.fromOpaque(p).release() |
| } |
| } |