Files
swift-mirror/stdlib/public/Darwin/Dispatch/Schedulers+DispatchQueue.swift
2020-03-24 11:30:45 -07:00

284 lines
11 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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
//
//===----------------------------------------------------------------------===//
// Only support 64bit
#if !(os(iOS) && (arch(i386) || arch(arm)))
import Combine
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
private func clampedIntProduct(_ m1: Int, _ m2: UInt64) -> Int {
assert(m2 > 0, "multiplier must be positive")
guard m1 < Int.max, m2 < Int.max else { return Int.max }
let (result, overflow) = m1.multipliedReportingOverflow(by: Int(m2))
if overflow {
return m1 > 0 ? Int.max : Int.min
}
return result
}
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension DispatchTimeInterval {
fileprivate var nanoseconds: Int {
switch self {
case .seconds(let s): return clampedIntProduct(s, NSEC_PER_SEC)
case .milliseconds(let ms): return clampedIntProduct(ms, NSEC_PER_MSEC)
case .microseconds(let us): return clampedIntProduct(us, NSEC_PER_USEC)
case .nanoseconds(let ns): return ns
case .never: return Int.max
}
}
}
// This is Strideable except: <rdar://problem/35158274>
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension DispatchTime /* : Strideable */ {
typealias Stride = DispatchTimeInterval
public func distance(to other: DispatchTime) -> DispatchTimeInterval {
let lhs = other.rawValue
let rhs = rawValue
if lhs >= rhs {
return DispatchTimeInterval.nanoseconds(Int(lhs - rhs))
} else {
return DispatchTimeInterval.nanoseconds(0 - Int(rhs - lhs))
}
}
public func advanced(by n: DispatchTimeInterval) -> DispatchTime {
return self + n
}
}
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension DispatchQueue: Scheduler {
/// The scheduler time type used by the dispatch queue.
public struct SchedulerTimeType: Strideable, Codable, Hashable {
/// The dispatch time represented by this type.
public var dispatchTime: DispatchTime
/// Creates a dispatch queue time type instance.
///
/// - Parameter time: The dispatch time to represent.
public init(_ time: DispatchTime) {
dispatchTime = time
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let time = DispatchTime(uptimeNanoseconds: try container.decode(UInt64.self))
self.init(time)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(dispatchTime.uptimeNanoseconds)
}
/// Returns the distance to another dispatch queue time.
///
/// - Parameter other: Another dispatch queue time.
/// - Returns: The time interval between this time and the provided time.
public func distance(to other: SchedulerTimeType) -> Stride {
return Stride(self.dispatchTime.distance(to: other.dispatchTime))
}
/// Returns a dispatch queue scheduler time calculated by advancing this instances time by the given interval.
///
/// - Parameter n: A time interval to advance.
/// - Returns: A dispatch queue time advanced by the given interval from this instances time.
public func advanced(by n: Stride) -> SchedulerTimeType {
return SchedulerTimeType(self.dispatchTime.advanced(by: n.timeInterval))
}
public func hash(into hasher: inout Hasher) {
hasher.combine(dispatchTime.rawValue)
}
public struct Stride: SchedulerTimeIntervalConvertible, Comparable, SignedNumeric, ExpressibleByFloatLiteral, Hashable, Codable {
/// If created via floating point literal, the value is converted to nanoseconds via multiplication.
public typealias FloatLiteralType = Double
/// Nanoseconds, same as DispatchTimeInterval.
public typealias IntegerLiteralType = Int
public typealias Magnitude = Int
/// The value of this time interval in nanoseconds.
public var magnitude: Int
/// A `DispatchTimeInterval` created with the value of this type in nanoseconds.
public var timeInterval: DispatchTimeInterval {
return .nanoseconds(magnitude)
}
/// Creates a dispatch queue time interval from the given dispatch time interval.
///
/// - Parameter timeInterval: A dispatch time interval.
public init(_ timeInterval: DispatchTimeInterval) {
magnitude = Int(timeInterval.nanoseconds)
}
/// Creates a dispatch queue time interval from a floating-point seconds value.
///
/// - Parameter value: The number of seconds, as a `Double`.
public init(floatLiteral value: Double) {
magnitude = Int(value * 1_000_000_000)
}
/// Creates a dispatch queue time interval from an integer seconds value.
///
/// - Parameter value: The number of seconds, as an `Int`.
public init(integerLiteral value: Int) {
magnitude = value * 1_000_000_000
}
/// Creates a dispatch queue time interval from a binary integer type.
///
/// If `exactly` cannot convert to an `Int`, the resulting time interval is `nil`.
/// - Parameter exactly: A binary integer representing a time interval.
public init?<T>(exactly source: T) where T: BinaryInteger {
if let v = Int(exactly: source) {
magnitude = v
} else {
return nil
}
}
// ---
public static func < (lhs: Stride, rhs: Stride) -> Bool {
return lhs.magnitude < rhs.magnitude
}
// ---
public static func * (lhs: Stride, rhs: Stride) -> Stride {
return Stride(.nanoseconds(lhs.magnitude * rhs.magnitude))
}
public static func + (lhs: Stride, rhs: Stride) -> Stride {
return Stride(.nanoseconds(lhs.magnitude + rhs.magnitude))
}
public static func - (lhs: Stride, rhs: Stride) -> Stride {
return Stride(.nanoseconds(lhs.magnitude - rhs.magnitude))
}
// ---
public static func -= (lhs: inout Stride, rhs: Stride) {
let result = lhs - rhs
lhs = result
}
public static func *= (lhs: inout Stride, rhs: Stride) {
let result = lhs * rhs
lhs = result
}
public static func += (lhs: inout Stride, rhs: Stride) {
let result = lhs + rhs
lhs = result
}
// ---
public static func seconds(_ s: Double) -> Stride {
return Stride(.nanoseconds(Int(s * 1_000_000_000)))
}
public static func seconds(_ s: Int) -> Stride {
return Stride(.seconds(s))
}
public static func milliseconds(_ ms: Int) -> Stride {
return Stride(.milliseconds(ms))
}
public static func microseconds(_ us: Int) -> Stride {
return Stride(.microseconds(us))
}
public static func nanoseconds(_ ns: Int) -> Stride {
return Stride(.nanoseconds(ns))
}
}
}
/// Options that affect the operation of the dispatch queue scheduler.
public struct SchedulerOptions {
/// The dispatch queue quality of service.
public var qos: DispatchQoS
/// The dispatch queue work item flags.
public var flags: DispatchWorkItemFlags
/// The dispatch group, if any, that should be used for performing actions.
public var group: DispatchGroup?
public init(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], group: DispatchGroup? = nil) {
self.qos = qos
self.flags = flags
self.group = group
}
}
public var minimumTolerance: SchedulerTimeType.Stride {
return SchedulerTimeType.Stride(DispatchTimeInterval.seconds(0))
}
public var now: DispatchQueue.SchedulerTimeType {
return SchedulerTimeType(DispatchTime.now())
}
public func schedule(options: SchedulerOptions?, _ action: @escaping () -> Void) {
let qos = options?.qos ?? .unspecified
let flags = options?.flags ?? []
if let group = options?.group {
// Distinguish on the group because it appears to not be a call-through like the others. This may need to be adjusted.
self.async(group: group, qos: qos, flags: flags, execute: action)
} else {
self.async(qos: qos, flags: flags, execute: action)
}
}
public func schedule(after date: SchedulerTimeType,
tolerance: SchedulerTimeType.Stride,
options: SchedulerOptions?,
_ action: @escaping () -> Void) {
// TODO: Tolerance ignored
let qos = options?.qos ?? .unspecified
let flags = options?.flags ?? []
self.asyncAfter(deadline: date.dispatchTime, qos: qos, flags: flags, execute: action)
}
public func schedule(after date: SchedulerTimeType,
interval: SchedulerTimeType.Stride,
tolerance: SchedulerTimeType.Stride,
options: SchedulerOptions?,
_ action: @escaping () -> Void) -> Cancellable {
let source = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(), queue: self)
source.schedule(deadline: date.dispatchTime,
repeating: interval.timeInterval,
leeway: tolerance.timeInterval)
source.setEventHandler(handler: action)
source.resume()
return AnyCancellable(source.cancel)
}
}
#endif /* !(os(iOS) && (arch(i386) || arch(arm))) */