From b8e8d107c4af4160333c48a169c60b0b77ef23f2 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 6 Sep 2024 11:02:27 -0400 Subject: [PATCH] Don't use `Synchronization.Atomic` for `deliverExpectationCheckedEvents`. This PR replaces the new use of `Atomic` with a `Locked` in the implementation of `deliverExpectationCheckedEvents`. Why? Because we're running into some environments where the Synchronization module isn't available (e.g. older host macOSes) and this is simpler. The performance profile is comparable: on my system, running the `repeatedlyExpect()` test takes 0.55s instead of 0.49s to call `#expect()` 1,000,000 times, so it's still a significant win over the implementation we had earlier. --- Package.swift | 1 - .../Testing/Running/Runner.RuntimeState.swift | 19 ++++++------------- Sources/Testing/Support/Locked.swift | 9 +++++++++ .../shared/AvailabilityDefinitions.cmake | 1 - 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Package.swift b/Package.swift index 4034c47e3..a97b5e55f 100644 --- a/Package.swift +++ b/Package.swift @@ -148,7 +148,6 @@ extension Array where Element == PackageDescription.SwiftSetting { .enableExperimentalFeature("AvailabilityMacro=_clockAPI:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0"), .enableExperimentalFeature("AvailabilityMacro=_regexAPI:macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0"), .enableExperimentalFeature("AvailabilityMacro=_swiftVersionAPI:macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0"), - .enableExperimentalFeature("AvailabilityMacro=_synchronizationAPI:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0"), .enableExperimentalFeature("AvailabilityMacro=_typedThrowsAPI:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0"), .enableExperimentalFeature("AvailabilityMacro=_distantFuture:macOS 99.0, iOS 99.0, watchOS 99.0, tvOS 99.0, visionOS 99.0"), diff --git a/Sources/Testing/Running/Runner.RuntimeState.swift b/Sources/Testing/Running/Runner.RuntimeState.swift index 34ad152c5..919b2b6d2 100644 --- a/Sources/Testing/Running/Runner.RuntimeState.swift +++ b/Sources/Testing/Running/Runner.RuntimeState.swift @@ -8,8 +8,6 @@ // See https://swift.org/CONTRIBUTORS.txt for Swift project authors // -private import Synchronization - extension Runner { /// A type which collects the task-scoped runtime state for a running /// ``Runner`` instance, the tests it runs, and other objects it interacts @@ -113,8 +111,8 @@ extension Configuration { /// - Returns: A unique number identifying `self` that can be /// passed to `_removeFromAll(identifiedBy:)`` to unregister it. private func _addToAll() -> UInt64 { - if deliverExpectationCheckedEvents, #available(_synchronizationAPI, *) { - Self._deliverExpectationCheckedEventsCount.add(1, ordering: .sequentiallyConsistent) + if deliverExpectationCheckedEvents { + Self._deliverExpectationCheckedEventsCount.increment() } return Self._all.withLock { all in let id = all.nextID @@ -133,8 +131,8 @@ extension Configuration { let configuration = Self._all.withLock { all in all.instances.removeValue(forKey: id) } - if let configuration, configuration.deliverExpectationCheckedEvents, #available(_synchronizationAPI, *) { - Self._deliverExpectationCheckedEventsCount.subtract(1, ordering: .sequentiallyConsistent) + if let configuration, configuration.deliverExpectationCheckedEvents { + Self._deliverExpectationCheckedEventsCount.decrement() } } @@ -143,8 +141,7 @@ extension Configuration { /// /// On older Apple platforms, this property is not available and ``all`` is /// directly consulted instead (which is less efficient.) - @available(_synchronizationAPI, *) - private static let _deliverExpectationCheckedEventsCount = Atomic(0) + private static let _deliverExpectationCheckedEventsCount = Locked(rawValue: 0) /// Whether or not events of the kind /// ``Event/Kind-swift.enum/expectationChecked(_:)`` should be delivered to @@ -155,11 +152,7 @@ extension Configuration { /// for these events, consult the per-instance /// ``Configuration/deliverExpectationCheckedEvents`` property. static var deliverExpectationCheckedEvents: Bool { - if #available(_synchronizationAPI, *) { - _deliverExpectationCheckedEventsCount.load(ordering: .sequentiallyConsistent) > 0 - } else { - all.contains(where: \.deliverExpectationCheckedEvents) - } + _deliverExpectationCheckedEventsCount.rawValue > 0 } } diff --git a/Sources/Testing/Support/Locked.swift b/Sources/Testing/Support/Locked.swift index 7be1fba22..5481b1117 100644 --- a/Sources/Testing/Support/Locked.swift +++ b/Sources/Testing/Support/Locked.swift @@ -177,6 +177,15 @@ extension Locked where T: Numeric { @discardableResult func increment() -> T { add(1) } + + /// Decrement the current wrapped value of this instance. + /// + /// - Returns: The sum of ``rawValue`` and `-1`. + /// + /// This function is exactly equivalent to `add(-1)`. + @discardableResult func decrement() -> T { + add(-1) + } } extension Locked { diff --git a/cmake/modules/shared/AvailabilityDefinitions.cmake b/cmake/modules/shared/AvailabilityDefinitions.cmake index 0241d2a27..0685fcecd 100644 --- a/cmake/modules/shared/AvailabilityDefinitions.cmake +++ b/cmake/modules/shared/AvailabilityDefinitions.cmake @@ -13,6 +13,5 @@ add_compile_options( "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_clockAPI:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0\">" "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_regexAPI:macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0\">" "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_swiftVersionAPI:macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0\">" - "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_synchronizationAPI:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0\">" "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_typedThrowsAPI:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0\">" "SHELL:$<$:-Xfrontend -define-availability -Xfrontend \"_distantFuture:macOS 99.0, iOS 99.0, watchOS 99.0, tvOS 99.0, visionOS 99.0\">")