Skip to content

Commit e0165b5

Browse files
gjcairoktoso
andauthored
Fix Sendability warnings (#137)
* Fix Sendability warnings * Apply suggestions from code review * Fix another warning and @unknown default issue --------- Co-authored-by: Konrad `ktoso` Malawski <[email protected]>
1 parent d067b0e commit e0165b5

File tree

4 files changed

+34
-4
lines changed

4 files changed

+34
-4
lines changed

Sources/CoreMetrics/Locks.swift

+4
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ extension Lock {
131131
}
132132
}
133133

134+
extension Lock: @unchecked Sendable {}
135+
134136
/// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
135137
///
136138
/// This object provides a lock on top of a single `pthread_rwlock_t`. This kind
@@ -273,3 +275,5 @@ extension ReadWriteLock {
273275
try self.withWriterLock(body)
274276
}
275277
}
278+
279+
extension ReadWriteLock: @unchecked Sendable {}

Sources/CoreMetrics/Metrics.swift

+5-3
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,8 @@ public enum MetricsSystem {
600600
return try self._factory.withWriterLock(body)
601601
}
602602

603-
private final class FactoryBox {
603+
// This can be `@unchecked Sendable` because we're manually gating access to mutable state with a lock.
604+
private final class FactoryBox: @unchecked Sendable {
604605
private let lock = ReadWriteLock()
605606
fileprivate var _underlying: MetricsFactory
606607
private var initialized = false
@@ -797,9 +798,10 @@ internal final class AccumulatingRoundingFloatingPointCounter: FloatingPointCoun
797798
}
798799

799800
/// Wraps a RecorderHandler, adding support for incrementing values by storing an accumulated value and recording increments to the underlying CounterHandler after crossing integer boundaries.
800-
internal final class AccumulatingMeter: MeterHandler {
801+
/// - Note: we can annotate this class as `@unchecked Sendable` because we are manually gating access to mutable state (i.e., the `value` property) via a Lock.
802+
internal final class AccumulatingMeter: MeterHandler, @unchecked Sendable {
801803
private let recorderHandler: RecorderHandler
802-
// FIXME: use atomics when available
804+
// FIXME: use swift-atomics when floating point support is available
803805
private var value: Double = 0
804806
private let lock = Lock()
805807

Sources/Metrics/Metrics.swift

+12
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ extension Timer {
6060
/// - duration: The duration to record.
6161
@inlinable
6262
public func record(_ duration: DispatchTimeInterval) {
63+
// This wrapping in a optional is a workaround because DispatchTimeInterval
64+
// is a non-frozen public enum and Dispatch is built with library evolution
65+
// mode turned on.
66+
// This means we should have an `@unknown default` case, but this breaks
67+
// on non-Darwin platforms.
68+
// Switching over an optional means that the `.none` case will map to
69+
// `default` (which means we'll always have a valid case to go into
70+
// the default case), but in reality this case will never exist as this
71+
// optional will never be nil.
72+
let duration = Optional(duration)
6373
switch duration {
6474
case .nanoseconds(let value):
6575
self.recordNanoseconds(value)
@@ -71,6 +81,8 @@ extension Timer {
7181
self.recordSeconds(value)
7282
case .never:
7383
self.record(0)
84+
default:
85+
self.record(0)
7486
}
7587
}
7688
}

Tests/MetricsTests/MetricsTests.swift

+13-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,17 @@ class MetricsExtensionsTests: XCTestCase {
185185
// https://bugs.swift.org/browse/SR-6310
186186
extension DispatchTimeInterval {
187187
func nano() -> Int {
188-
switch self {
188+
// This wrapping in a optional is a workaround because DispatchTimeInterval
189+
// is a non-frozen public enum and Dispatch is built with library evolution
190+
// mode turned on.
191+
// This means we should have an `@unknown default` case, but this breaks
192+
// on non-Darwin platforms.
193+
// Switching over an optional means that the `.none` case will map to
194+
// `default` (which means we'll always have a valid case to go into
195+
// the default case), but in reality this case will never exist as this
196+
// optional will never be nil.
197+
let interval = Optional(self)
198+
switch interval {
189199
case .nanoseconds(let value):
190200
return value
191201
case .microseconds(let value):
@@ -196,6 +206,8 @@ extension DispatchTimeInterval {
196206
return value * 1_000_000_000
197207
case .never:
198208
return 0
209+
default:
210+
return 0
199211
}
200212
}
201213
}

0 commit comments

Comments
 (0)