mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
284 lines
11 KiB
Swift
284 lines
11 KiB
Swift
//===----------------------------------------------------------------------===//
|
||
//
|
||
// 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 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?<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))) */
|