mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Remove the hacky support for mapping clocks to a Dispatch clock ID, in favour of clocks publishing traits and having the Dispatch executor select the clock on the basis of those traits.
229 lines
7.2 KiB
Swift
229 lines
7.2 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2020 - 2025 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if !$Embedded
|
|
|
|
import Swift
|
|
|
|
// We can't import Dispatch from here, sadly, because it apparently has a
|
|
// transitive dependency on Combine (which in turn depends on _Concurrency).
|
|
|
|
// import Dispatch
|
|
|
|
// .. Dispatch Interface .......................................................
|
|
|
|
// .. Main Executor ............................................................
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
public class DispatchMainExecutor: RunLoopExecutor, @unchecked Sendable {
|
|
var threaded = false
|
|
|
|
public init() {}
|
|
|
|
public func run() throws {
|
|
if threaded {
|
|
fatalError("DispatchMainExecutor does not support recursion")
|
|
}
|
|
|
|
threaded = true
|
|
_dispatchMain()
|
|
}
|
|
|
|
public func stop() {
|
|
fatalError("DispatchMainExecutor cannot be stopped")
|
|
}
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
extension DispatchMainExecutor: SerialExecutor {
|
|
|
|
public func enqueue(_ job: consuming ExecutorJob) {
|
|
_dispatchEnqueueMain(UnownedJob(job))
|
|
}
|
|
|
|
public var isMainExecutor: Bool { true }
|
|
|
|
public var supportsScheduling: Bool { true }
|
|
|
|
public func enqueue<C: Clock>(_ job: consuming ExecutorJob,
|
|
at instant: C.Instant,
|
|
tolerance: C.Duration? = nil,
|
|
clock: C) {
|
|
let tolSec, tolNanosec: CLongLong
|
|
if let tolerance = tolerance {
|
|
(tolSec, tolNanosec) = delay(from: tolerance, clock: clock)
|
|
} else {
|
|
tolSec = 0
|
|
tolNanosec = -1
|
|
}
|
|
|
|
let (clockID, seconds, nanoseconds) = timestamp(for: instant, clock: clock)
|
|
|
|
_dispatchEnqueueWithDeadline(CBool(false),
|
|
CLongLong(seconds), CLongLong(nanoseconds),
|
|
CLongLong(tolSec), CLongLong(tolNanosec),
|
|
clockID.rawValue,
|
|
UnownedJob(job))
|
|
}
|
|
|
|
public func checkIsolated() {
|
|
_dispatchAssertMainQueue()
|
|
}
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
extension DispatchMainExecutor: EventableExecutor {
|
|
|
|
/// Register a new event with a given handler.
|
|
public func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent {
|
|
let source = _createDispatchEvent(handler: handler)
|
|
|
|
// Stash the pointer in the id of the ExecutorEvent struct
|
|
let eventId = unsafeBitCast(source, to: Int.self)
|
|
return ExecutorEvent(id: eventId)
|
|
}
|
|
|
|
/// Deregister the given event.
|
|
public func deregister(event: ExecutorEvent) {
|
|
// Extract the source and cancel it
|
|
let source = unsafeBitCast(event.id, to: OpaquePointer.self)
|
|
_destroyDispatchEvent(source)
|
|
}
|
|
|
|
/// Notify the executor of an event.
|
|
public func notify(event: ExecutorEvent) {
|
|
// Extract the source, but don't release it
|
|
let source = unsafeBitCast(event.id, to: OpaquePointer.self)
|
|
_signalDispatchEvent(source)
|
|
}
|
|
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
extension DispatchMainExecutor: MainExecutor {}
|
|
|
|
// .. Task Executor ............................................................
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
public class DispatchTaskExecutor: TaskExecutor, @unchecked Sendable {
|
|
|
|
public init() {}
|
|
|
|
public func enqueue(_ job: consuming ExecutorJob) {
|
|
_dispatchEnqueueGlobal(UnownedJob(job))
|
|
}
|
|
|
|
public var isMainExecutor: Bool { false }
|
|
|
|
public var supportsScheduling: Bool { true }
|
|
|
|
public func enqueue<C: Clock>(_ job: consuming ExecutorJob,
|
|
at instant: C.Instant,
|
|
tolerance: C.Duration? = nil,
|
|
clock: C) {
|
|
let tolSec, tolNanosec: CLongLong
|
|
if let tolerance = tolerance {
|
|
(tolSec, tolNanosec) = delay(from: tolerance, clock: clock)
|
|
} else {
|
|
tolSec = 0
|
|
tolNanosec = -1
|
|
}
|
|
|
|
let (clockID, seconds, nanoseconds) = timestamp(for: instant, clock: clock)
|
|
|
|
_dispatchEnqueueWithDeadline(CBool(true),
|
|
CLongLong(seconds), CLongLong(nanoseconds),
|
|
CLongLong(tolSec), CLongLong(tolNanosec),
|
|
clockID.rawValue,
|
|
UnownedJob(job))
|
|
}
|
|
}
|
|
|
|
// .. Clock Support ............................................................
|
|
|
|
/// DispatchMainExecutor and DispatchTaskExecutor both implement this
|
|
/// protocol.
|
|
///
|
|
/// It is used to help convert instants and durations from arbitrary `Clock`s
|
|
/// to Dispatch's time base.
|
|
@available(SwiftStdlib 6.2, *)
|
|
protocol DispatchExecutor: Executor {
|
|
|
|
/// Convert an `Instant` from the specified clock to a tuple identifying
|
|
/// the Dispatch clock and the seconds and nanoseconds components.
|
|
///
|
|
/// Parameters:
|
|
///
|
|
/// - for instant: The `Instant` to convert.
|
|
/// - clock: The `Clock` instant that the `Instant` came from.
|
|
///
|
|
/// Returns: A tuple of `(clockID, seconds, nanoseconds)`.
|
|
func timestamp<C: Clock>(for instant: C.Instant, clock: C)
|
|
-> (clockID: DispatchClockID, seconds: Int64, nanoseconds: Int64)
|
|
|
|
/// Convert a `Duration` from the specified clock to a tuple containing
|
|
/// seconds and nanosecond components.
|
|
func delay<C: Clock>(from duration: C.Duration, clock: C)
|
|
-> (seconds: Int64, nanoseconds: Int64)
|
|
|
|
}
|
|
|
|
/// An enumeration identifying one of the Dispatch-supported clocks
|
|
enum DispatchClockID: CInt {
|
|
case suspending = 1
|
|
case continuous = 2
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
extension DispatchExecutor {
|
|
|
|
func timestamp<C: Clock>(for instant: C.Instant, clock: C)
|
|
-> (clockID: DispatchClockID, seconds: Int64, nanoseconds: Int64) {
|
|
if clock.traits.contains(.continuous) {
|
|
let dispatchClock: ContinuousClock = .continuous
|
|
let instant = dispatchClock.convert(instant: instant, from: clock)!
|
|
let (seconds, attoseconds) = instant._value.components
|
|
let nanoseconds = attoseconds / 1_000_000_000
|
|
return (clockID: .continuous,
|
|
seconds: Int64(seconds),
|
|
nanoseconds: Int64(nanoseconds))
|
|
} else {
|
|
let dispatchClock: SuspendingClock = .suspending
|
|
let instant = dispatchClock.convert(instant: instant, from: clock)!
|
|
let (seconds, attoseconds) = instant._value.components
|
|
let nanoseconds = attoseconds / 1_000_000_000
|
|
return (clockID: .suspending,
|
|
seconds: Int64(seconds),
|
|
nanoseconds: Int64(nanoseconds))
|
|
}
|
|
}
|
|
|
|
func delay<C: Clock>(from duration: C.Duration, clock: C)
|
|
-> (seconds: Int64, nanoseconds: Int64) {
|
|
let swiftDuration = clock.convert(from: duration)!
|
|
let (seconds, attoseconds) = swiftDuration.components
|
|
let nanoseconds = attoseconds / 1_000_000_000
|
|
return (seconds: seconds, nanoseconds: nanoseconds)
|
|
}
|
|
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
extension DispatchTaskExecutor: DispatchExecutor {
|
|
}
|
|
|
|
@available(SwiftStdlib 6.2, *)
|
|
extension DispatchMainExecutor: DispatchExecutor {
|
|
}
|
|
|
|
#endif // !$Embedded
|