File: Clock.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (234 lines) | stat: -rw-r--r-- 7,352 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 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 Swift project authors
//

private import _TestingInternals

@_spi(Experimental) @_spi(ForToolsIntegrationOnly)
extension Test {
  /// A clock used to track time when events occur during testing.
  ///
  /// This clock tracks time using both the [suspending clock](https://developer.apple.com/documentation/swift/suspendingclock)
  /// and the wall clock. Only the suspending clock is used for comparing and
  /// calculating; the wall clock is used for presentation when needed.
  public struct Clock: Sendable {
    /// An instant on the testing clock.
    public struct Instant: Sendable {
      /// The suspending-clock time corresponding to this instant.
      fileprivate(set) var suspending: TimeValue = {
#if SWT_TARGET_OS_APPLE
        // The testing library's availability on Apple platforms is earlier than
        // that of the Swift Clock API, so we don't use `SuspendingClock`
        // directly on them and instead derive a value from platform-specific
        // API. SuspendingClock corresponds to CLOCK_UPTIME_RAW on Darwin.
        // SEE: https://github.com/swiftlang/swift/blob/main/stdlib/public/Concurrency/Clock.cpp
        var uptime = timespec()
        _ = clock_gettime(CLOCK_UPTIME_RAW, &uptime)
        return TimeValue(uptime)
#else
        /// The corresponding suspending-clock time.
        TimeValue(SuspendingClock.Instant.now)
#endif
      }()

#if !SWT_NO_UTC_CLOCK
      /// The wall-clock time corresponding to this instant.
      fileprivate(set) var wall: TimeValue = {
        var wall = timespec()
        timespec_get(&wall, TIME_UTC)
        return TimeValue(wall)
      }()
#endif

      /// The current time according to the testing clock.
      public static var now: Self {
        Self()
      }
    }

    public init() {}
  }
}

// MARK: -

@_spi(Experimental) @_spi(ForToolsIntegrationOnly)
@available(_clockAPI, *)
extension SuspendingClock.Instant {
  /// Initialize this instant to the equivalent of the same instant on the
  /// testing library's clock.
  ///
  /// - Parameters:
  ///   - testClockInstant: The equivalent instant on ``Test/Clock``.
  public init(_ testClockInstant: Test.Clock.Instant) {
    self.init(testClockInstant.suspending)
  }
}

extension Test.Clock.Instant {
#if !SWT_NO_UTC_CLOCK
  /// The duration since 1970 represented by this instance as a tuple of seconds
  /// and attoseconds.
  ///
  /// The value of this property is the equivalent of `self` on the wall clock.
  /// It is suitable for display to the user, but not for fine timing
  /// calculations.
  public var timeComponentsSince1970: (seconds: Int64, attoseconds: Int64) {
    wall.components
  }

  /// The duration since 1970 represented by this instance.
  ///
  /// The value of this property is the equivalent of `self` on the wall clock.
  /// It is suitable for display to the user, but not for fine timing
  /// calculations.
  @available(_clockAPI, *)
  public var durationSince1970: Duration {
    Duration(wall)
  }
#endif

  /// Get the number of nanoseconds from this instance to another.
  ///
  /// - Parameters:
  ///   - other: The later instant.
  ///
  /// - Returns: The number of nanoseconds between `self` and `other`. If
  ///   `other` is ordered before this instance, the result is negative.
  func nanoseconds(until other: Self) -> Int64 {
    if other < self {
      return -other.nanoseconds(until: self)
    }
    let otherNanoseconds = (other.suspending.seconds * 1_000_000_000) + (other.suspending.attoseconds / 1_000_000_000)
    let selfNanoseconds = (suspending.seconds * 1_000_000_000) + (suspending.attoseconds / 1_000_000_000)
    return otherNanoseconds - selfNanoseconds
  }
}

// MARK: - Sleeping

extension Test.Clock {
  /// Suspend the current task for the given duration.
  ///
  /// - Parameters:
  ///   - duration: How long to suspend for.
  ///
  /// - Throws: `CancellationError` if the current task was cancelled while it
  ///   was sleeping.
  ///
  /// This function is not part of the public interface of the testing library.
  /// It is primarily used by the testing library's own tests. External clients
  /// can use ``sleep(for:tolerance:)`` or ``sleep(until:tolerance:)`` instead.
  @available(_clockAPI, *)
  static func sleep(for duration: Duration) async throws {
#if SWT_NO_UNSTRUCTURED_TASKS
    let timeValue = TimeValue(duration)
    var ts = timespec(timeValue)
    var tsRemaining = ts
    while 0 != nanosleep(&ts, &tsRemaining) {
      try Task.checkCancellation()
      ts = tsRemaining
    }
#else
    return try await SuspendingClock().sleep(for: duration)
#endif
  }
}

// MARK: - Clock

@available(_clockAPI, *)
extension Test.Clock: _Concurrency.Clock {
  public typealias Duration = SuspendingClock.Duration

  public var now: Instant {
    .now
  }

  public var minimumResolution: Duration {
#if SWT_TARGET_OS_APPLE
    var res = timespec()
    _ = clock_getres(CLOCK_UPTIME_RAW, &res)
    return Duration(TimeValue(res))
#else
    SuspendingClock().minimumResolution
#endif
  }

  public func sleep(until deadline: Instant, tolerance: Duration?) async throws {
    let duration = Instant.now.duration(to: deadline)
#if SWT_NO_UNSTRUCTURED_TASKS
    try await Self.sleep(for: duration)
#else
    try await SuspendingClock().sleep(for: duration, tolerance: tolerance)
#endif
  }
}

// MARK: - Equatable, Hashable, Comparable

extension Test.Clock.Instant: Equatable, Hashable, Comparable {
  public static func ==(lhs: Self, rhs: Self) -> Bool {
    lhs.suspending == rhs.suspending
  }

  public func hash(into hasher: inout Hasher) {
    hasher.combine(suspending)
  }

  public static func <(lhs: Self, rhs: Self) -> Bool {
    lhs.suspending < rhs.suspending
  }
}

// MARK: - InstantProtocol

@available(_clockAPI, *)
extension Test.Clock.Instant: InstantProtocol {
  public typealias Duration = Swift.Duration

  public func advanced(by duration: Duration) -> Self {
    var result = self

    result.suspending = TimeValue(Duration(result.suspending) + duration)
#if !SWT_NO_UTC_CLOCK
    result.wall = TimeValue(Duration(result.wall) + duration)
#endif

    return result
  }

  public func duration(to other: Test.Clock.Instant) -> Duration {
    Duration(other.suspending) - Duration(suspending)
  }
}

// MARK: - Duration descriptions

extension Test.Clock.Instant {
  /// Get a description of the duration between this instance and another.
  ///
  /// - Parameters:
  ///   - other: The later instant.
  ///
  /// - Returns: A string describing the duration between `self` and `other`,
  ///   up to millisecond accuracy.
  func descriptionOfDuration(to other: Test.Clock.Instant) -> String {
#if SWT_TARGET_OS_APPLE
    let (seconds, nanosecondsRemaining) = nanoseconds(until: other).quotientAndRemainder(dividingBy: 1_000_000_000)
    return String(describing: TimeValue((seconds, nanosecondsRemaining * 1_000_000_000)))
#else
    return String(describing: TimeValue(Duration(other.suspending) - Duration(suspending)))
#endif
  }
}

// MARK: - Codable

extension Test.Clock.Instant: Codable {}