10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
13
+ import Synchronization
14
+
13
15
/// A scope to contain a group of statistics.
14
16
public final class StatisticsGroup : Sendable {
15
17
/// The name for this group.
16
18
public let name : String
17
19
18
- private let _statistics = LockedValue < [ Statistic ] > ( [ ] )
20
+ private let _statistics = LockedValue < [ any _StatisticBackend ] > ( [ ] )
19
21
20
22
/// 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 } }
22
24
23
25
public init ( _ name: String ) {
24
26
self . name = name
25
27
}
26
28
27
- public func register( _ statistic: Statistic ) {
29
+ public func register( _ statistic: any _StatisticBackend ) {
28
30
_statistics. withLock { $0. append ( statistic) }
29
31
}
30
32
@@ -40,20 +42,16 @@ public final class StatisticsGroup: Sendable {
40
42
///
41
43
/// 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.
42
44
// 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 {
44
47
/// The name of the statistics.
45
48
public let name : String
46
49
47
50
/// The description of the statistics.
48
51
public let description : String
49
52
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
-
55
53
/// The value of the statistic.
56
- var _value : Int = 0
54
+ private let _value = Atomic < Int > ( 0 )
57
55
58
56
public init ( _ name: String , _ description: String , _ group: StatisticsGroup = allStatistics) {
59
57
self . name = name
@@ -64,28 +62,19 @@ public final class Statistic: @unchecked Sendable {
64
62
65
63
/// Get the current value of the statistic.
66
64
public var value : Int {
67
- return sync { _value }
65
+ return _value. load ( ordering : . relaxed )
68
66
}
69
67
70
68
/// Increment the statistic.
71
69
public func increment( _ n: Int = 1 ) {
72
- sync { _value += n }
70
+ _value. wrappingAdd ( n , ordering : . relaxed )
73
71
}
74
72
75
73
/// Zero all of the statistics.
76
74
///
77
75
/// This is useful when using statistics to probe program behavior from within tests, and the test can guarantee no concurrent access.
78
76
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)
89
78
}
90
79
}
91
80
@@ -97,3 +86,44 @@ public let allStatistics = StatisticsGroup("swift-build")
97
86
public func += ( statistic: Statistic , rhs: Int = 1 ) {
98
87
statistic. increment ( rhs)
99
88
}
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