diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f5c509e..236277b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ jobs: strategy: matrix: xcode: + - "26.2" - "26.5" steps: - uses: actions/checkout@v5 @@ -26,6 +27,13 @@ jobs: run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app - name: Print Swift version run: swift --version + - name: Install Simulator Runtimes + if: matrix.xcode != '26.2' + uses: nick-fields/retry@v3 + with: + timeout_minutes: 15 + max_attempts: 3 + command: xcodebuild -downloadAllPlatforms - name: Run ${{ matrix.config }} tests run: CONFIG=${{ matrix.config }} make test-all @@ -36,6 +44,7 @@ jobs: matrix: config: ["debug", "release"] swift: + - "6.0" - "6.3" container: swift:${{ matrix.swift }} steps: diff --git a/Sources/Clocks/ImmediateClock.swift b/Sources/Clocks/ImmediateClock.swift index f182e7f..ac33425 100644 --- a/Sources/Clocks/ImmediateClock.swift +++ b/Sources/Clocks/ImmediateClock.swift @@ -133,18 +133,17 @@ } } - public private(set) var now: Instant + public internal(set) var now: Instant public private(set) var minimumResolution: Duration = .zero - private let lock = NSLock() + let lock = NSLock() public init(now: Instant = .init()) { self.now = now } - public func sleep(until deadline: Instant, tolerance: Duration?) async throws { + public func sleep(until deadline: Instant, tolerance: Duration?) throws { try Task.checkCancellation() self.lock.sync { self.now = deadline } - await Task.megaYield() } } diff --git a/Sources/Clocks/NonsendingClock.swift b/Sources/Clocks/NonsendingClock.swift new file mode 100644 index 0000000..497f230 --- /dev/null +++ b/Sources/Clocks/NonsendingClock.swift @@ -0,0 +1,34 @@ +#if compiler(>=6.2) + @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) + public protocol NonsendingClock: Clock { + nonisolated(nonsending) + func sleep(until deadline: Instant, tolerance: Instant.Duration?) async throws + } + + @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) + extension NonsendingClock { + nonisolated(nonsending) + public func sleep(for duration: Duration, tolerance: Instant.Duration? = nil) async throws + { + try await sleep(until: now.advanced(by: duration), tolerance: tolerance) + } + } + + @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) + extension AnyClock: NonsendingClock {} + + @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) + extension ContinuousClock: NonsendingClock {} + + @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) + extension ImmediateClock: NonsendingClock {} + + @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) + extension SuspendingClock: NonsendingClock {} + + @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) + extension TestClock: NonsendingClock {} + + @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *) + extension UnimplementedClock: NonsendingClock {} +#endif