1010//
1111//===----------------------------------------------------------------------===//
1212
13+ import Synchronization
14+
1315/// A scope to contain a group of statistics.
1416public final class StatisticsGroup : Sendable {
1517 /// The name for this group.
1618 public let name : String
1719
18- private let _statistics = LockedValue < [ Statistic ] > ( [ ] )
20+ private let _statistics = LockedValue < [ any _StatisticBackend ] > ( [ ] )
1921
2022 /// The list of statistics in the group.
21- public var statistics : [ Statistic ] { return _statistics. withLock { $0 } }
23+ public var statistics : [ any _StatisticBackend ] { return _statistics. withLock { $0 } }
2224
2325 public init ( _ name: String ) {
2426 self . name = name
2527 }
2628
27- public func register( _ statistic: Statistic ) {
29+ public func register( _ statistic: any _StatisticBackend ) {
2830 _statistics. withLock { $0. append ( statistic) }
2931 }
3032
@@ -40,20 +42,16 @@ public final class StatisticsGroup: Sendable {
4042///
4143/// Currently statistics are always integers and are not thread safe (unless building in TSan mode); clients should implement their own locking if an accurate count is required.
4244// FIXME: This should unconditionally be implemented using atomics, not conditionally be using a queue based on TSan...
43- public final class Statistic : @unchecked Sendable {
45+ @available ( macOS 15 . 0 , iOS 18 . 0 , tvOS 18 . 0 , watchOS 11 . 0 , visionOS 2 , * )
46+ public final class _Statistic : @unchecked Sendable , _StatisticBackend {
4447 /// The name of the statistics.
4548 public let name : String
4649
4750 /// The description of the statistics.
4851 public let description : String
4952
50- #if ENABLE_THREAD_SANITIZER
51- /// Queue to serialize access to the statistic value.
52- let _queue = SWBQueue ( label: " com.apple.dt.SWBUtil.Statistics " )
53- #endif
54-
5553 /// The value of the statistic.
56- var _value : Int = 0
54+ private let _value = Atomic < Int > ( 0 )
5755
5856 public init ( _ name: String , _ description: String , _ group: StatisticsGroup = allStatistics) {
5957 self . name = name
@@ -64,28 +62,19 @@ public final class Statistic: @unchecked Sendable {
6462
6563 /// Get the current value of the statistic.
6664 public var value : Int {
67- return sync { _value }
65+ return _value. load ( ordering : . relaxed )
6866 }
6967
7068 /// Increment the statistic.
7169 public func increment( _ n: Int = 1 ) {
72- sync { _value += n }
70+ _value. wrappingAdd ( n , ordering : . relaxed )
7371 }
7472
7573 /// Zero all of the statistics.
7674 ///
7775 /// This is useful when using statistics to probe program behavior from within tests, and the test can guarantee no concurrent access.
7876 public func zero( ) {
79- sync { _value = 0 }
80- }
81-
82- /// Helper method to execute a block on our serial queue (if it's enabled).
83- public func sync< T> ( execute work: ( ) throws -> T ) rethrows -> T {
84- #if ENABLE_THREAD_SANITIZER
85- return try _queue. blocking_sync { try work ( ) }
86- #else
87- return try work ( )
88- #endif
77+ _value. store ( 0 , ordering: . relaxed)
8978 }
9079}
9180
@@ -97,3 +86,44 @@ public let allStatistics = StatisticsGroup("swift-build")
9786public func += ( statistic: Statistic , rhs: Int = 1 ) {
9887 statistic. increment ( rhs)
9988}
89+
90+ // MARK: Back-deployment
91+
92+ public final class Statistic : @unchecked Sendable , _StatisticBackend {
93+ public let name : String
94+ private let _statistic : ( any _StatisticBackend ) ?
95+
96+ public init ( _ name: String , _ description: String , _ group: StatisticsGroup = allStatistics) {
97+ self . name = name
98+ if #available( macOS 15 . 0 , iOS 18 . 0 , tvOS 18 . 0 , watchOS 11 . 0 , visionOS 2 . 0 , * ) {
99+ _statistic = _Statistic ( name, description, group)
100+ } else {
101+ _statistic = nil
102+ }
103+ }
104+
105+ public var value : Int {
106+ _statistic? . value ?? 0
107+ }
108+
109+ public func increment( _ n: Int ) {
110+ _statistic? . increment ( n)
111+ }
112+
113+ public func zero( ) {
114+ _statistic? . zero ( )
115+ }
116+ }
117+
118+ public protocol _StatisticBackend {
119+ var name : String { get }
120+ var value : Int { get }
121+ func increment( _ n: Int )
122+ func zero( )
123+ }
124+
125+ extension _StatisticBackend {
126+ public func increment( ) {
127+ self . increment ( 1 )
128+ }
129+ }
0 commit comments