Skip to content

Commit add9e15

Browse files
authored
more Sendable conformances & Sendable Context (#381)
1 parent f0e80d1 commit add9e15

18 files changed

+244
-29
lines changed

Sources/TSCBasic/CodableResult.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public struct CodableResult<Success, Failure>: Codable where Success: Codable, F
4949
}
5050
}
5151

52+
extension CodableResult: Sendable where Success: Sendable, Failure: Sendable {}
53+
5254
extension CodableResult where Failure == StringError {
5355
public init(body: () throws -> Success) {
5456
do {

Sources/TSCBasic/Condition.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,7 @@ public struct Condition {
5050
return try body()
5151
}
5252
}
53+
54+
#if compiler(>=5.7)
55+
extension Condition: Sendable {}
56+
#endif

Sources/TSCBasic/DeltaAlgorithm.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
/// It is not an error to provide a predicate that does not satisfy these
2828
/// requirements, and the algorithm will generally produce reasonable results.
2929
/// However, it may run substantially more tests than with a good predicate.
30-
public struct DeltaAlgorithm<Change: Hashable> {
30+
public struct DeltaAlgorithm<Change: Hashable>: Sendable {
3131

3232
public init() {}
3333

Sources/TSCBasic/DiagnosticsEngine.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ extension DiagnosticData {
2121
public protocol DiagnosticLocation: Sendable, CustomStringConvertible {
2222
}
2323

24-
public struct Diagnostic: CustomStringConvertible {
24+
public struct Diagnostic: CustomStringConvertible, Sendable {
2525
/// The behavior associated with this diagnostic.
26-
public enum Behavior {
26+
public enum Behavior: Sendable {
2727
/// An error which will halt the operation.
2828
case error
2929

@@ -38,7 +38,7 @@ public struct Diagnostic: CustomStringConvertible {
3838
case ignored
3939
}
4040

41-
public struct Message {
41+
public struct Message: Sendable {
4242
/// The diagnostic's behavior.
4343
public let behavior: Behavior
4444

@@ -191,7 +191,7 @@ extension Diagnostic.Message {
191191
}
192192
}
193193

194-
public struct StringDiagnostic: DiagnosticData {
194+
public struct StringDiagnostic: DiagnosticData, Sendable {
195195
/// The diagnostic description.
196196
public let description: String
197197

Sources/TSCBasic/FileSystem.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import Foundation
1313
import Dispatch
1414
import SystemPackage
1515

16-
public struct FileSystemError: Error, Equatable {
17-
public enum Kind: Equatable {
16+
public struct FileSystemError: Error, Equatable, Sendable {
17+
public enum Kind: Equatable, Sendable {
1818
/// Access to the path is denied.
1919
///
2020
/// This is used when an operation cannot be completed because a component of

Sources/TSCBasic/HashAlgorithms.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import CryptoKit
1313
#endif
1414

15-
public protocol HashAlgorithm {
15+
public protocol HashAlgorithm: Sendable {
1616

1717
/// Hashes the input bytes, returning the digest.
1818
///
@@ -31,7 +31,7 @@ extension HashAlgorithm {
3131
/// SHA-256 implementation from Secure Hash Algorithm 2 (SHA-2) set of
3232
/// cryptographic hash functions (FIPS PUB 180-2).
3333
/// Uses CryptoKit where available
34-
public struct SHA256: HashAlgorithm {
34+
public struct SHA256: HashAlgorithm, Sendable {
3535
private let underlying: HashAlgorithm
3636

3737
public init() {
@@ -197,7 +197,7 @@ struct InternalSHA256: HashAlgorithm {
197197
#if canImport(CryptoKit)
198198
@available(*, deprecated, message: "use SHA256 which abstract over platform differences")
199199
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
200-
public struct CryptoKitSHA256: HashAlgorithm {
200+
public struct CryptoKitSHA256: HashAlgorithm, Sendable {
201201
let underlying = _CryptoKitSHA256()
202202
public init() {}
203203
public func hash(_ bytes: ByteString) -> ByteString {

Sources/TSCBasic/KeyedPair.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,5 @@ public struct KeyedPair<T, K: Hashable>: Hashable {
5050
return lhs.key == rhs.key
5151
}
5252
}
53+
54+
extension KeyedPair: Sendable where T: Sendable, K: Sendable {}

Sources/TSCBasic/LazyCache.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import class Foundation.NSLock
3030
/// ```
3131
///
3232
/// See: https://bugs.swift.org/browse/SR-1042
33+
@available(*, deprecated, message: "This implementation does not work -- https://github.com/apple/swift-tools-support-core/issues/385")
3334
public struct LazyCache<Class, T> {
3435
// FIXME: It would be nice to avoid a per-instance lock, but this type isn't
3536
// intended for creating large numbers of instances of. We also really want
@@ -57,3 +58,8 @@ public struct LazyCache<Class, T> {
5758
}
5859
}
5960
}
61+
62+
#if swift(>=5.6)
63+
@available(*, unavailable) // until https://github.com/apple/swift-tools-support-core/issues/385 is fixed
64+
extension LazyCache: Sendable {}
65+
#endif

Sources/TSCBasic/OrderedDictionary.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,5 @@ extension OrderedDictionary: RandomAccessCollection {
123123
return (key, value)
124124
}
125125
}
126+
127+
extension OrderedDictionary: Sendable where Key: Sendable, Value: Sendable {}

Sources/TSCBasic/OrderedSet.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,5 @@ public func == <T>(lhs: OrderedSet<T>, rhs: OrderedSet<T>) -> Bool {
128128
}
129129

130130
extension OrderedSet: Hashable where Element: Hashable { }
131+
132+
extension OrderedSet: Sendable where Element: Sendable { }

Sources/TSCBasic/misc.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ public struct CodableRange<Bound> where Bound: Comparable & Codable {
264264
}
265265
}
266266

267+
extension CodableRange: Sendable where Bound: Sendable {}
268+
267269
extension CodableRange: Codable {
268270
private enum CodingKeys: String, CodingKey {
269271
case lowerBound, upperBound

Sources/TSCUtility/Context.swift

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,127 @@
99
*/
1010

1111
import Foundation
12+
import _Concurrency
1213

13-
/// Typealias for an any typed dictionary for arbitrary usage to store context.
14-
public typealias Context = [ObjectIdentifier: Any]
14+
15+
public struct Context {
16+
private var backing: [ObjectIdentifier: Any] = [:]
17+
18+
#if compiler(>=5.5.2)
19+
@available(*, deprecated, message: "Values should be Sendable")
20+
@_disfavoredOverload
21+
public init(dictionaryLiteral keyValuePairs: (ObjectIdentifier, Any)...) {
22+
self.backing = Dictionary(uniqueKeysWithValues: keyValuePairs)
23+
}
24+
25+
public init(dictionaryLiteral keyValuePairs: (ObjectIdentifier, Sendable)...) {
26+
self.backing = Dictionary(uniqueKeysWithValues: keyValuePairs)
27+
}
28+
29+
@available(*, deprecated, message: "Values should be Sendable")
30+
@_disfavoredOverload
31+
public subscript(key: ObjectIdentifier) -> Any? {
32+
get {
33+
return self.backing[key]
34+
}
35+
set {
36+
self.backing[key] = newValue
37+
}
38+
}
39+
40+
public subscript<Value>(key: ObjectIdentifier, as type: Value.Type = Value.self) -> Value? where Value: Sendable {
41+
get {
42+
return self.backing[key] as? Value
43+
}
44+
set {
45+
self.backing[key] = newValue
46+
}
47+
}
48+
#else
49+
public init(dictionaryLiteral keyValuePairs: (ObjectIdentifier, Any)...) {
50+
self.backing = Dictionary(uniqueKeysWithValues: keyValuePairs)
51+
}
52+
53+
@_disfavoredOverload
54+
public subscript(key: ObjectIdentifier) -> Any? {
55+
get {
56+
return self.backing[key]
57+
}
58+
set {
59+
self.backing[key] = newValue
60+
}
61+
}
62+
63+
public subscript<Value>(key: ObjectIdentifier, as type: Value.Type = Value.self) -> Value? {
64+
get {
65+
return self.backing[key] as? Value
66+
}
67+
set {
68+
self.backing[key] = newValue
69+
}
70+
}
71+
#endif
72+
}
73+
74+
#if compiler(>=5.7)
75+
extension Context: /* until we can remove the support for 'Any' values */ @unchecked Sendable {}
76+
#else
77+
#if compiler(>=5.5.2)
78+
extension Context: UnsafeSendable {}
79+
#endif
80+
#endif
81+
82+
@available(*, deprecated, renamed: "init()")
83+
extension Context: ExpressibleByDictionaryLiteral {
84+
public typealias Key = ObjectIdentifier
85+
public typealias Value = Any
86+
}
1587

1688
extension Context {
89+
#if compiler(>=5.5.2)
90+
/// Get the value for the given type.
91+
@available(*, deprecated, message: "Values should be Sendable")
92+
@_disfavoredOverload
93+
public func get<T>(_ type: T.Type = T.self) -> T {
94+
guard let value = getOptional(type) else {
95+
fatalError("no type \(T.self) in context")
96+
}
97+
return value
98+
}
99+
100+
public func get<T: Sendable>(_ type: T.Type = T.self) -> T {
101+
guard let value = self.getOptional(type) else {
102+
fatalError("no type \(T.self) in context")
103+
}
104+
return value
105+
}
106+
107+
/// Get the value for the given type, if present.
108+
@available(*, deprecated, message: "Values should be Sendable")
109+
@_disfavoredOverload
110+
public func getOptional<T>(_ type: T.Type = T.self) -> T? {
111+
guard let value = self[ObjectIdentifier(T.self)] else {
112+
return nil
113+
}
114+
return value as? T
115+
}
116+
117+
/// Get the value for the given type, if present.
118+
public func getOptional<T: Sendable>(_ type: T.Type = T.self) -> T? {
119+
return self[ObjectIdentifier(T.self)]
120+
}
121+
122+
/// Set a context value for a type.
123+
@available(*, deprecated, message: "Values should be Sendable")
124+
@_disfavoredOverload
125+
public mutating func set<T>(_ value: T) {
126+
self[ObjectIdentifier(T.self)] = value
127+
}
128+
129+
public mutating func set<Value: Sendable>(_ value: Value) {
130+
self[ObjectIdentifier(Value.self)] = value
131+
}
132+
#else
17133
/// Get the value for the given type.
18134
public func get<T>(_ type: T.Type = T.self) -> T {
19135
guard let value = getOptional(type) else {
@@ -34,4 +150,5 @@ extension Context {
34150
public mutating func set<T>(_ value: T) {
35151
self[ObjectIdentifier(T.self)] = value
36152
}
153+
#endif
37154
}

Sources/TSCUtility/SerializedDiagnostics.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Foundation
1111
import TSCBasic
1212

1313
/// Represents diagnostics serialized in a .dia file by the Swift compiler or Clang.
14-
public struct SerializedDiagnostics {
14+
public struct SerializedDiagnostics: Sendable {
1515
public enum Error: Swift.Error {
1616
case badMagic
1717
case unexpectedTopLevelRecord
@@ -61,7 +61,7 @@ extension SerializedDiagnostics.Error: CustomNSError {
6161
extension SerializedDiagnostics {
6262
public struct Diagnostic {
6363

64-
public enum Level: UInt64 {
64+
public enum Level: UInt64, Sendable {
6565
case ignored, note, warning, error, fatal, remark
6666
}
6767
/// The diagnostic message text.
@@ -168,7 +168,7 @@ extension SerializedDiagnostics {
168168
}
169169
}
170170

171-
public struct FixIt {
171+
public struct FixIt: Sendable {
172172
/// Start location.
173173
public var start: SourceLocation
174174
/// End location.
@@ -178,6 +178,12 @@ extension SerializedDiagnostics {
178178
}
179179
}
180180

181+
#if compiler(>=5.7)
182+
extension SerializedDiagnostics.Diagnostic: Sendable {}
183+
#else
184+
extension SerializedDiagnostics.Diagnostic: UnsafeSendable {}
185+
#endif
186+
181187
extension SerializedDiagnostics {
182188
private struct Reader: BitstreamVisitor {
183189
var diagnosticRecords: [[OwnedRecord]] = []

Sources/TSCUtility/Tracing.swift

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import Foundation
1212

13-
public enum TracingEventType: String, Codable {
13+
public enum TracingEventType: String, Codable, Sendable {
1414
case asyncBegin
1515
case asyncEnd
1616
}
@@ -51,7 +51,7 @@ public protocol TracingEventProtocol {
5151
)
5252
}
5353

54-
public protocol TracingCollectionProtocol {
54+
public protocol TracingCollectionProtocol: Sendable {
5555
var events: [TracingEventProtocol] { get set }
5656
init(_ events: [TracingEventProtocol])
5757
}
@@ -62,7 +62,7 @@ extension TracingCollectionProtocol {
6262
}
6363
}
6464

65-
public struct TracingEvent: TracingEventProtocol, Codable {
65+
public struct TracingEvent: TracingEventProtocol, Codable, Sendable {
6666
public let cat: String
6767
public let name: String
6868
public let id: String
@@ -135,13 +135,37 @@ public struct TracingEvent: TracingEventProtocol, Codable {
135135
#endif
136136
}
137137

138-
public class TracingCollection: TracingCollectionProtocol {
139-
public var events: [TracingEventProtocol] = []
138+
public final class TracingCollection {
139+
private let lock = NSLock()
140+
private var _events: [TracingEventProtocol] = []
141+
142+
public var events: [TracingEventProtocol] {
143+
get {
144+
return self.lock.withLock {
145+
self._events
146+
}
147+
}
148+
149+
set {
150+
self.lock.withLock {
151+
self._events = newValue
152+
}
153+
}
154+
}
155+
140156
public required init(_ events: [TracingEventProtocol] = []) {
141157
self.events = events
142158
}
143159
}
144160

161+
#if compiler(>=5.7)
162+
extension TracingCollection: @unchecked /* because self-locked */ Sendable {}
163+
extension TracingCollection: TracingCollectionProtocol {}
164+
#else
165+
extension TracingCollection: UnsafeSendable {}
166+
extension TracingCollection: TracingCollectionProtocol {}
167+
#endif
168+
145169
extension Context {
146170
public static func withTracing(_ collection: TracingCollectionProtocol) -> Context {
147171
return Context(dictionaryLiteral: (ObjectIdentifier(TracingCollectionProtocol.self), collection as Any))
@@ -154,10 +178,7 @@ extension Context {
154178

155179
public var tracing: TracingCollectionProtocol? {
156180
get {
157-
guard let collection = self[ObjectIdentifier(TracingCollectionProtocol.self)] as? TracingCollectionProtocol else {
158-
return nil
159-
}
160-
return collection
181+
return self[ObjectIdentifier(TracingCollectionProtocol.self), as: TracingCollectionProtocol.self]
161182
}
162183
set {
163184
self[ObjectIdentifier(TracingCollectionProtocol.self)] = newValue

0 commit comments

Comments
 (0)