//===----------------------------------------------------------------------===// // // 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 { public init() {} } internal class _DispatchSpecificValue { internal let value: T internal init(value: T) { self.value = value } } 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(iOS, deprecated: 8.0, message: "Use qos attributes instead") @available(tvOS, deprecated, message: "Use qos attributes instead") @available(watchOS, deprecated, message: "Use qos attributes instead") case high @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") @available(iOS, deprecated: 8.0, message: "Use qos attributes instead") @available(tvOS, deprecated, message: "Use qos attributes instead") @available(watchOS, deprecated, message: "Use qos attributes instead") case `default` @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") @available(iOS, deprecated: 8.0, message: "Use qos attributes instead") @available(tvOS, deprecated, message: "Use qos attributes instead") @available(watchOS, deprecated, message: "Use qos attributes instead") case low @available(macOS, deprecated: 10.10, message: "Use qos attributes instead") @available(iOS, deprecated: 8.0, message: "Use qos attributes instead") @available(tvOS, deprecated, message: "Use qos attributes instead") @available(watchOS, deprecated, 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(iOS, deprecated: 8.0, message: "") @available(tvOS, deprecated, message: "") @available(watchOS, deprecated, 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(key: DispatchSpecificKey) -> T? { let k = Unmanaged.passUnretained(key).toOpaque() if let p = CDispatch.dispatch_get_specific(k) { let v = Unmanaged<_DispatchSpecificValue> .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( 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( 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(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(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(key: DispatchSpecificKey) -> T? { let k = Unmanaged.passUnretained(key).toOpaque() if let p = dispatch_queue_get_specific(self.__wrapped, k) { let v = Unmanaged<_DispatchSpecificValue> .fromOpaque(p) .takeUnretainedValue() return v.value } return nil } public func setSpecific(key: DispatchSpecificKey, 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.fromOpaque(p).release() } }