27
27
28
28
#if canImport(Darwin)
29
29
import Darwin
30
- #elseif canImport(Android)
31
- import Android
32
- #elseif canImport(Glibc)
33
- import Glibc
34
30
#elseif os(Windows)
31
+ import ucrt
35
32
import WinSDK
33
+ #elseif canImport(Glibc)
34
+ @preconcurrency import Glibc
35
+ #elseif canImport(Musl)
36
+ @preconcurrency import Musl
37
+ #elseif canImport(Bionic)
38
+ @preconcurrency import Bionic
39
+ #elseif canImport(WASILibc)
40
+ @preconcurrency import WASILibc
41
+ #if canImport(wasi_pthread)
42
+ import wasi_pthread
43
+ #endif
44
+ #else
45
+ #error("The concurrency Lock module was unable to identify your C library.")
36
46
#endif
37
47
38
48
#if os(Windows)
@@ -43,8 +53,19 @@ typealias LockPrimitive = SRWLOCK
43
53
typealias LockPrimitive = pthread_mutex_t
44
54
#endif
45
55
56
+
57
+ /// A utility function that runs the body code only in debug builds, without
58
+ /// emitting compiler warnings.
59
+ ///
60
+ /// This is currently the only way to do this in Swift: see
61
+ /// https://forums.swift.org/t/support-debug-only-code/11037 for a discussion.
62
+ @inlinable
63
+ internal func debugOnly( _ body: ( ) -> Void ) {
64
+ assert ( { body ( ) ; return true } ( ) )
65
+ }
66
+
46
67
@usableFromInline
47
- enum LockOperations { }
68
+ enum LockOperations : Sendable { }
48
69
49
70
extension LockOperations {
50
71
@inlinable
@@ -53,9 +74,12 @@ extension LockOperations {
53
74
54
75
#if os(Windows)
55
76
InitializeSRWLock ( mutex)
56
- #else
77
+ #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
57
78
var attr = pthread_mutexattr_t ( )
58
79
pthread_mutexattr_init ( & attr)
80
+ debugOnly {
81
+ pthread_mutexattr_settype ( & attr, . init( PTHREAD_MUTEX_ERRORCHECK) )
82
+ }
59
83
60
84
let err = pthread_mutex_init ( mutex, & attr)
61
85
precondition ( err == 0 , " \( #function) failed in pthread_mutex with error \( err) " )
@@ -67,8 +91,8 @@ extension LockOperations {
67
91
mutex. assertValidAlignment ( )
68
92
69
93
#if os(Windows)
70
- // SRWLOCK does not need to be freed
71
- #else
94
+ // SRWLOCK does not need to be free'd
95
+ #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
72
96
let err = pthread_mutex_destroy ( mutex)
73
97
precondition ( err == 0 , " \( #function) failed in pthread_mutex with error \( err) " )
74
98
#endif
@@ -80,7 +104,7 @@ extension LockOperations {
80
104
81
105
#if os(Windows)
82
106
AcquireSRWLockExclusive ( mutex)
83
- #else
107
+ #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
84
108
let err = pthread_mutex_lock ( mutex)
85
109
precondition ( err == 0 , " \( #function) failed in pthread_mutex with error \( err) " )
86
110
#endif
@@ -92,7 +116,7 @@ extension LockOperations {
92
116
93
117
#if os(Windows)
94
118
ReleaseSRWLockExclusive ( mutex)
95
- #else
119
+ #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
96
120
let err = pthread_mutex_unlock ( mutex)
97
121
precondition ( err == 0 , " \( #function) failed in pthread_mutex with error \( err) " )
98
122
#endif
@@ -135,7 +159,9 @@ final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {
135
159
let buffer = Self . create ( minimumCapacity: 1 ) { _ in
136
160
value
137
161
}
138
- // Avoid 'unsafeDowncast' as there is a miscompilation on 5.10.
162
+ // Intentionally using a force cast here to avoid a miss compiliation in 5.10.
163
+ // This is as fast as an unsafeDownCast since ManagedBuffer is inlined and the optimizer
164
+ // can eliminate the upcast/downcast pair
139
165
let storage = buffer as! Self
140
166
141
167
storage. withUnsafeMutablePointers { _, lockPtr in
@@ -159,17 +185,15 @@ final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {
159
185
}
160
186
}
161
187
162
- @usableFromInline
188
+ @inlinable
163
189
deinit {
164
190
self . withUnsafeMutablePointerToElements { lockPtr in
165
191
LockOperations . destroy ( lockPtr)
166
192
}
167
193
}
168
194
169
195
@inlinable
170
- func withLockPrimitive< T> (
171
- _ body: ( UnsafeMutablePointer < LockPrimitive > ) throws -> T
172
- ) rethrows -> T {
196
+ func withLockPrimitive< T> ( _ body: ( UnsafeMutablePointer < LockPrimitive > ) throws -> T ) rethrows -> T {
173
197
try self . withUnsafeMutablePointerToElements { lockPtr in
174
198
try body ( lockPtr)
175
199
}
@@ -185,11 +209,16 @@ final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {
185
209
}
186
210
}
187
211
188
- extension LockStorage : @unchecked Sendable { }
212
+ // This compiler guard is here becaue `ManagedBuffer` is already declaring
213
+ // Sendable unavailability after 6.1, which `LockStorage` inherits.
214
+ #if compiler(<6.2)
215
+ @available ( * , unavailable)
216
+ extension LockStorage : Sendable { }
217
+ #endif
189
218
190
219
/// A threading lock based on `libpthread` instead of `libdispatch`.
191
220
///
192
- /// - note : ``Lock`` has reference semantics.
221
+ /// - Note : ``Lock`` has reference semantics.
193
222
///
194
223
/// This object provides a lock on top of a single `pthread_mutex_t`. This kind
195
224
/// of lock is safe to use with `libpthread`-based threading models, such as the
@@ -201,7 +230,7 @@ struct Lock {
201
230
internal let _storage : LockStorage < Void >
202
231
203
232
/// Create a new lock.
204
- @usableFromInline
233
+ @inlinable
205
234
init ( ) {
206
235
self . _storage = . create( value: ( ) )
207
236
}
@@ -225,9 +254,7 @@ struct Lock {
225
254
}
226
255
227
256
@inlinable
228
- internal func withLockPrimitive< T> (
229
- _ body: ( UnsafeMutablePointer < LockPrimitive > ) throws -> T
230
- ) rethrows -> T {
257
+ internal func withLockPrimitive< T> ( _ body: ( UnsafeMutablePointer < LockPrimitive > ) throws -> T ) rethrows -> T {
231
258
try self . _storage. withLockPrimitive ( body)
232
259
}
233
260
}
@@ -249,9 +276,14 @@ extension Lock {
249
276
}
250
277
return try body ( )
251
278
}
279
+
280
+ @inlinable
281
+ func withLockVoid( _ body: ( ) throws -> Void ) rethrows {
282
+ try self . withLock ( body)
283
+ }
252
284
}
253
285
254
- extension Lock : Sendable { }
286
+ extension Lock : @ unchecked Sendable { }
255
287
256
288
extension UnsafeMutablePointer {
257
289
@inlinable
@@ -260,20 +292,74 @@ extension UnsafeMutablePointer {
260
292
}
261
293
}
262
294
295
+ /// Provides locked access to `Value`.
296
+ ///
297
+ /// - Note: ``LockedValueBox`` has reference semantics and holds the `Value`
298
+ /// alongside a lock behind a reference.
299
+ ///
300
+ /// This is no different than creating a ``Lock`` and protecting all
301
+ /// accesses to a value using the lock. But it's easy to forget to actually
302
+ /// acquire/release the lock in the correct place. ``LockedValueBox`` makes
303
+ /// that much easier.
263
304
@usableFromInline
264
305
struct LockedValueBox < Value> {
265
- @usableFromInline
266
- let storage : LockStorage < Value >
267
306
268
307
@usableFromInline
308
+ internal let _storage : LockStorage < Value >
309
+
310
+ /// Initialize the `Value`.
311
+ @inlinable
269
312
init ( _ value: Value ) {
270
- self . storage = . create( value: value)
313
+ self . _storage = . create( value: value)
271
314
}
272
315
316
+ /// Access the `Value`, allowing mutation of it.
273
317
@inlinable
274
318
func withLockedValue< T> ( _ mutate: ( inout Value ) throws -> T ) rethrows -> T {
275
- try self . storage. withLockedValue ( mutate)
319
+ try self . _storage. withLockedValue ( mutate)
320
+ }
321
+
322
+ /// Provides an unsafe view over the lock and its value.
323
+ ///
324
+ /// This can be beneficial when you require fine grained control over the lock in some
325
+ /// situations but don't want lose the benefits of ``withLockedValue(_:)`` in others by
326
+ /// switching to ``NIOLock``.
327
+ var unsafe : Unsafe {
328
+ Unsafe ( _storage: self . _storage)
329
+ }
330
+
331
+ /// Provides an unsafe view over the lock and its value.
332
+ struct Unsafe {
333
+ @usableFromInline
334
+ let _storage : LockStorage < Value >
335
+
336
+ /// Manually acquire the lock.
337
+ @inlinable
338
+ func lock( ) {
339
+ self . _storage. lock ( )
340
+ }
341
+
342
+ /// Manually release the lock.
343
+ @inlinable
344
+ func unlock( ) {
345
+ self . _storage. unlock ( )
346
+ }
347
+
348
+ /// Mutate the value, assuming the lock has been acquired manually.
349
+ ///
350
+ /// - Parameter mutate: A closure with scoped access to the value.
351
+ /// - Returns: The result of the `mutate` closure.
352
+ @inlinable
353
+ func withValueAssumingLockIsAcquired< Result> (
354
+ _ mutate: ( _ value: inout Value ) throws -> Result
355
+ ) rethrows -> Result {
356
+ try self . _storage. withUnsafeMutablePointerToHeader { value in
357
+ try mutate ( & value. pointee)
358
+ }
359
+ }
276
360
}
277
361
}
278
362
279
- extension LockedValueBox : Sendable where Value: Sendable { }
363
+ extension LockedValueBox : @unchecked Sendable where Value: Sendable { }
364
+
365
+ extension LockedValueBox . Unsafe : @unchecked Sendable where Value: Sendable { }
0 commit comments