//===----------------------------------------------------------------------===// // // 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: @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 instance’s time by the given interval. /// /// - Parameter n: A time interval to advance. /// - Returns: A dispatch queue time advanced by the given interval from this instance’s 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?(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))) */