8
8
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9
9
//
10
10
11
- private import _TestingInternals
12
- private import Synchronization
11
+ internal import _TestingInternals
12
+
13
+ /// A protocol defining a type, generally platform-specific, that satisfies the
14
+ /// requirements of a lock or mutex.
15
+ protocol Lockable {
16
+ /// Initialize the lock at the given address.
17
+ ///
18
+ /// - Parameters:
19
+ /// - lock: A pointer to uninitialized memory that should be initialized as
20
+ /// an instance of this type.
21
+ static func initializeLock( at lock: UnsafeMutablePointer < Self > )
22
+
23
+ /// Deinitialize the lock at the given address.
24
+ ///
25
+ /// - Parameters:
26
+ /// - lock: A pointer to initialized memory that should be deinitialized.
27
+ static func deinitializeLock( at lock: UnsafeMutablePointer < Self > )
28
+
29
+ /// Acquire the lock at the given address.
30
+ ///
31
+ /// - Parameters:
32
+ /// - lock: The address of the lock to acquire.
33
+ static func unsafelyAcquireLock( at lock: UnsafeMutablePointer < Self > )
34
+
35
+ /// Relinquish the lock at the given address.
36
+ ///
37
+ /// - Parameters:
38
+ /// - lock: The address of the lock to relinquish.
39
+ static func unsafelyRelinquishLock( at lock: UnsafeMutablePointer < Self > )
40
+ }
41
+
42
+ // MARK: -
13
43
14
44
/// A type that wraps a value requiring access from a synchronous caller during
15
45
/// concurrent execution.
@@ -22,48 +52,30 @@ private import Synchronization
22
52
/// concurrency tools.
23
53
///
24
54
/// This type is not part of the public interface of the testing library.
25
- struct Locked < T> {
26
- /// A type providing storage for the underlying lock and wrapped value.
27
- #if SWT_TARGET_OS_APPLE && canImport(os)
28
- private typealias _Storage = ManagedBuffer < T , os_unfair_lock_s >
29
- #else
30
- private final class _Storage {
31
- let mutex : Mutex < T >
32
-
33
- init ( _ rawValue: consuming sending T) {
34
- mutex = Mutex ( rawValue)
55
+ struct LockedWith < L, T> : RawRepresentable where L: Lockable {
56
+ /// A type providing heap-allocated storage for an instance of ``Locked``.
57
+ private final class _Storage : ManagedBuffer < T , L > {
58
+ deinit {
59
+ withUnsafeMutablePointerToElements { lock in
60
+ L . deinitializeLock ( at: lock)
61
+ }
35
62
}
36
63
}
37
- #endif
38
64
39
65
/// Storage for the underlying lock and wrapped value.
40
- private nonisolated ( unsafe) var _storage: _Storage
41
- }
42
-
43
- extension Locked : Sendable where T: Sendable { }
66
+ private nonisolated ( unsafe) var _storage: ManagedBuffer < T , L >
44
67
45
- extension Locked : RawRepresentable {
46
68
init ( rawValue: T ) {
47
- #if SWT_TARGET_OS_APPLE && canImport(os)
48
- _storage = . create( minimumCapacity: 1 , makingHeaderWith: { _ in rawValue } )
69
+ _storage = _Storage. create ( minimumCapacity: 1 , makingHeaderWith: { _ in rawValue } )
49
70
_storage. withUnsafeMutablePointerToElements { lock in
50
- lock . initialize ( to : . init ( ) )
71
+ L . initializeLock ( at : lock )
51
72
}
52
- #else
53
- nonisolated ( unsafe) let rawValue = rawValue
54
- _storage = _Storage ( rawValue)
55
- #endif
56
73
}
57
74
58
75
var rawValue : T {
59
- withLock { rawValue in
60
- nonisolated ( unsafe) let rawValue = rawValue
61
- return rawValue
62
- }
76
+ withLock { $0 }
63
77
}
64
- }
65
78
66
- extension Locked {
67
79
/// Acquire the lock and invoke a function while it is held.
68
80
///
69
81
/// - Parameters:
@@ -76,27 +88,55 @@ extension Locked {
76
88
/// This function can be used to synchronize access to shared data from a
77
89
/// synchronous caller. Wherever possible, use actor isolation or other Swift
78
90
/// concurrency tools.
79
- func withLock< R> ( _ body: ( inout T ) throws -> sending R) rethrows -> sending R where R: ~ Copyable {
80
- #if SWT_TARGET_OS_APPLE && canImport(os)
81
- nonisolated ( unsafe) let result = try _storage. withUnsafeMutablePointers { rawValue, lock in
82
- os_unfair_lock_lock ( lock)
91
+ nonmutating func withLock< R> ( _ body: ( inout T ) throws -> R ) rethrows -> R where R: ~ Copyable {
92
+ try _storage. withUnsafeMutablePointers { rawValue, lock in
93
+ L . unsafelyAcquireLock ( at: lock)
83
94
defer {
84
- os_unfair_lock_unlock ( lock)
95
+ L . unsafelyRelinquishLock ( at : lock)
85
96
}
86
97
return try body ( & rawValue. pointee)
87
98
}
88
- return result
89
- #else
90
- try _storage. mutex. withLock { rawValue in
91
- try body ( & rawValue)
99
+ }
100
+
101
+ /// Acquire the lock and invoke a function while it is held, yielding both the
102
+ /// protected value and a reference to the underlying lock guarding it.
103
+ ///
104
+ /// - Parameters:
105
+ /// - body: A closure to invoke while the lock is held.
106
+ ///
107
+ /// - Returns: Whatever is returned by `body`.
108
+ ///
109
+ /// - Throws: Whatever is thrown by `body`.
110
+ ///
111
+ /// This function is equivalent to ``withLock(_:)`` except that the closure
112
+ /// passed to it also takes a reference to the underlying lock guarding this
113
+ /// instance's wrapped value. This function can be used when platform-specific
114
+ /// functionality such as a `pthread_cond_t` is needed. Because the caller has
115
+ /// direct access to the lock and is able to unlock and re-lock it, it is
116
+ /// unsafe to modify the protected value.
117
+ ///
118
+ /// - Warning: Callers that unlock the lock _must_ lock it again before the
119
+ /// closure returns. If the lock is not acquired when `body` returns, the
120
+ /// effect is undefined.
121
+ nonmutating func withUnsafeUnderlyingLock< R> ( _ body: ( UnsafeMutablePointer < L > , T ) throws -> R ) rethrows -> R where R: ~ Copyable {
122
+ try withLock { value in
123
+ try _storage. withUnsafeMutablePointerToElements { lock in
124
+ try body ( lock, value)
125
+ }
92
126
}
93
- #endif
94
127
}
95
128
}
96
129
130
+ extension LockedWith : Sendable where T: Sendable { }
131
+
132
+ /// A type that wraps a value requiring access from a synchronous caller during
133
+ /// concurrent execution and which uses the default platform-specific lock type
134
+ /// for the current platform.
135
+ typealias Locked < T> = LockedWith < DefaultLock , T >
136
+
97
137
// MARK: - Additions
98
138
99
- extension Locked where T: AdditiveArithmetic & Sendable {
139
+ extension LockedWith where T: AdditiveArithmetic {
100
140
/// Add something to the current wrapped value of this instance.
101
141
///
102
142
/// - Parameters:
@@ -112,7 +152,7 @@ extension Locked where T: AdditiveArithmetic & Sendable {
112
152
}
113
153
}
114
154
115
- extension Locked where T: Numeric & Sendable {
155
+ extension LockedWith where T: Numeric {
116
156
/// Increment the current wrapped value of this instance.
117
157
///
118
158
/// - Returns: The sum of ``rawValue`` and `1`.
@@ -132,7 +172,7 @@ extension Locked where T: Numeric & Sendable {
132
172
}
133
173
}
134
174
135
- extension Locked {
175
+ extension LockedWith {
136
176
/// Initialize an instance of this type with a raw value of `nil`.
137
177
init < V> ( ) where T == V ? {
138
178
self . init ( rawValue: nil )
@@ -148,10 +188,3 @@ extension Locked {
148
188
self . init ( rawValue: [ ] )
149
189
}
150
190
}
151
-
152
- // MARK: - POSIX conveniences
153
-
154
- #if os(FreeBSD) || os(OpenBSD)
155
- typealias pthread_mutex_t = _TestingInternals . pthread_mutex_t ?
156
- typealias pthread_cond_t = _TestingInternals . pthread_cond_t ?
157
- #endif
0 commit comments