Skip to content

Commit 7b61999

Browse files
Pull in updated Lock impl from NIO
1 parent a6a00f8 commit 7b61999

File tree

1 file changed

+113
-27
lines changed
  • Sources/OpenAPIURLSession/BufferedStream

1 file changed

+113
-27
lines changed

Sources/OpenAPIURLSession/BufferedStream/Lock.swift

Lines changed: 113 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,22 @@
2727

2828
#if canImport(Darwin)
2929
import Darwin
30-
#elseif canImport(Android)
31-
import Android
32-
#elseif canImport(Glibc)
33-
import Glibc
3430
#elseif os(Windows)
31+
import ucrt
3532
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.")
3646
#endif
3747

3848
#if os(Windows)
@@ -43,8 +53,19 @@ typealias LockPrimitive = SRWLOCK
4353
typealias LockPrimitive = pthread_mutex_t
4454
#endif
4555

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+
4667
@usableFromInline
47-
enum LockOperations {}
68+
enum LockOperations: Sendable {}
4869

4970
extension LockOperations {
5071
@inlinable
@@ -53,9 +74,12 @@ extension LockOperations {
5374

5475
#if os(Windows)
5576
InitializeSRWLock(mutex)
56-
#else
77+
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
5778
var attr = pthread_mutexattr_t()
5879
pthread_mutexattr_init(&attr)
80+
debugOnly {
81+
pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK))
82+
}
5983

6084
let err = pthread_mutex_init(mutex, &attr)
6185
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
@@ -67,8 +91,8 @@ extension LockOperations {
6791
mutex.assertValidAlignment()
6892

6993
#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))
7296
let err = pthread_mutex_destroy(mutex)
7397
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
7498
#endif
@@ -80,7 +104,7 @@ extension LockOperations {
80104

81105
#if os(Windows)
82106
AcquireSRWLockExclusive(mutex)
83-
#else
107+
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
84108
let err = pthread_mutex_lock(mutex)
85109
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
86110
#endif
@@ -92,7 +116,7 @@ extension LockOperations {
92116

93117
#if os(Windows)
94118
ReleaseSRWLockExclusive(mutex)
95-
#else
119+
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
96120
let err = pthread_mutex_unlock(mutex)
97121
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
98122
#endif
@@ -135,7 +159,9 @@ final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {
135159
let buffer = Self.create(minimumCapacity: 1) { _ in
136160
value
137161
}
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
139165
let storage = buffer as! Self
140166

141167
storage.withUnsafeMutablePointers { _, lockPtr in
@@ -159,17 +185,15 @@ final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {
159185
}
160186
}
161187

162-
@usableFromInline
188+
@inlinable
163189
deinit {
164190
self.withUnsafeMutablePointerToElements { lockPtr in
165191
LockOperations.destroy(lockPtr)
166192
}
167193
}
168194

169195
@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 {
173197
try self.withUnsafeMutablePointerToElements { lockPtr in
174198
try body(lockPtr)
175199
}
@@ -185,11 +209,16 @@ final class LockStorage<Value>: ManagedBuffer<Value, LockPrimitive> {
185209
}
186210
}
187211

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
189218

190219
/// A threading lock based on `libpthread` instead of `libdispatch`.
191220
///
192-
/// - note: ``Lock`` has reference semantics.
221+
/// - Note: ``Lock`` has reference semantics.
193222
///
194223
/// This object provides a lock on top of a single `pthread_mutex_t`. This kind
195224
/// of lock is safe to use with `libpthread`-based threading models, such as the
@@ -201,7 +230,7 @@ struct Lock {
201230
internal let _storage: LockStorage<Void>
202231

203232
/// Create a new lock.
204-
@usableFromInline
233+
@inlinable
205234
init() {
206235
self._storage = .create(value: ())
207236
}
@@ -225,9 +254,7 @@ struct Lock {
225254
}
226255

227256
@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 {
231258
try self._storage.withLockPrimitive(body)
232259
}
233260
}
@@ -249,9 +276,14 @@ extension Lock {
249276
}
250277
return try body()
251278
}
279+
280+
@inlinable
281+
func withLockVoid(_ body: () throws -> Void) rethrows {
282+
try self.withLock(body)
283+
}
252284
}
253285

254-
extension Lock: Sendable {}
286+
extension Lock: @unchecked Sendable {}
255287

256288
extension UnsafeMutablePointer {
257289
@inlinable
@@ -260,20 +292,74 @@ extension UnsafeMutablePointer {
260292
}
261293
}
262294

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.
263304
@usableFromInline
264305
struct LockedValueBox<Value> {
265-
@usableFromInline
266-
let storage: LockStorage<Value>
267306

268307
@usableFromInline
308+
internal let _storage: LockStorage<Value>
309+
310+
/// Initialize the `Value`.
311+
@inlinable
269312
init(_ value: Value) {
270-
self.storage = .create(value: value)
313+
self._storage = .create(value: value)
271314
}
272315

316+
/// Access the `Value`, allowing mutation of it.
273317
@inlinable
274318
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+
}
276360
}
277361
}
278362

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

Comments
 (0)