26
26
//
27
27
//===----------------------------------------------------------------------===//
28
28
29
- #if canImport(Darwin)
29
+ #if canImport(WASILibc)
30
+ // No locking on WASILibc
31
+ #elseif canImport(Darwin)
30
32
import Darwin
33
+ #elseif os(Windows)
34
+ import WinSDK
31
35
#elseif canImport(Glibc)
32
36
import Glibc
33
37
#elseif canImport(Android)
34
38
import Android
35
39
#elseif canImport(Musl)
36
40
import Musl
37
- #elseif canImport(WASILibc)
38
- import WASILibc
39
- #if canImport(wasi_pthread)
40
- import wasi_pthread
41
- #endif
42
41
#else
43
42
#error("Unsupported runtime")
44
43
#endif
@@ -47,62 +46,211 @@ import wasi_pthread
47
46
///
48
47
/// This object provides a lock on top of a single `pthread_mutex_t`. This kind
49
48
/// of lock is safe to use with `libpthread`-based threading models, such as the
50
- /// one used by NIO.
51
- @_spi ( Locking) // Use the `package` access modifier once min Swift version is increased.
52
- public final class ReadWriteLock {
53
- private let rwlock : UnsafeMutablePointer < pthread_rwlock_t > = UnsafeMutablePointer . allocate ( capacity: 1 )
49
+ /// one used by NIO. On Windows, the lock is based on the substantially similar
50
+ /// `SRWLOCK` type.
51
+ internal final class Lock : @unchecked Sendable {
52
+ #if canImport(WASILibc)
53
+ // WASILibc is single threaded, provides no locks
54
+ #elseif os(Windows)
55
+ fileprivate let mutex : UnsafeMutablePointer < SRWLOCK > =
56
+ UnsafeMutablePointer . allocate ( capacity: 1 )
57
+ #else
58
+ fileprivate let mutex : UnsafeMutablePointer < pthread_mutex_t > =
59
+ UnsafeMutablePointer . allocate ( capacity: 1 )
60
+ #endif
61
+
62
+ /// Create a new lock.
63
+ public init ( ) {
64
+ #if canImport(WASILibc)
65
+ // WASILibc is single threaded, provides no locks
66
+ #elseif os(Windows)
67
+ InitializeSRWLock ( self . mutex)
68
+ #else
69
+ var attr = pthread_mutexattr_t ( )
70
+ pthread_mutexattr_init ( & attr)
71
+ pthread_mutexattr_settype ( & attr, . init( PTHREAD_MUTEX_ERRORCHECK) )
72
+
73
+ let err = pthread_mutex_init ( self . mutex, & attr)
74
+ precondition ( err == 0 , " \( #function) failed in pthread_mutex with error \( err) " )
75
+ #endif
76
+ }
77
+
78
+ deinit {
79
+ #if canImport(WASILibc)
80
+ // WASILibc is single threaded, provides no locks
81
+ #elseif os(Windows)
82
+ // SRWLOCK does not need to be free'd
83
+ self . mutex. deallocate ( )
84
+ #else
85
+ let err = pthread_mutex_destroy ( self . mutex)
86
+ precondition ( err == 0 , " \( #function) failed in pthread_mutex with error \( err) " )
87
+ self . mutex. deallocate ( )
88
+ #endif
89
+ }
90
+
91
+ /// Acquire the lock.
92
+ ///
93
+ /// Whenever possible, consider using `withLock` instead of this method and
94
+ /// `unlock`, to simplify lock handling.
95
+ public func lock( ) {
96
+ #if canImport(WASILibc)
97
+ // WASILibc is single threaded, provides no locks
98
+ #elseif os(Windows)
99
+ AcquireSRWLockExclusive ( self . mutex)
100
+ #else
101
+ let err = pthread_mutex_lock ( self . mutex)
102
+ precondition ( err == 0 , " \( #function) failed in pthread_mutex with error \( err) " )
103
+ #endif
104
+ }
105
+
106
+ /// Release the lock.
107
+ ///
108
+ /// Whenever possible, consider using `withLock` instead of this method and
109
+ /// `lock`, to simplify lock handling.
110
+ public func unlock( ) {
111
+ #if canImport(WASILibc)
112
+ // WASILibc is single threaded, provides no locks
113
+ #elseif os(Windows)
114
+ ReleaseSRWLockExclusive ( self . mutex)
115
+ #else
116
+ let err = pthread_mutex_unlock ( self . mutex)
117
+ precondition ( err == 0 , " \( #function) failed in pthread_mutex with error \( err) " )
118
+ #endif
119
+ }
120
+ }
121
+
122
+ extension Lock {
123
+ /// Acquire the lock for the duration of the given block.
124
+ ///
125
+ /// This convenience method should be preferred to `lock` and `unlock` in
126
+ /// most situations, as it ensures that the lock will be released regardless
127
+ /// of how `body` exits.
128
+ ///
129
+ /// - Parameter body: The block to execute while holding the lock.
130
+ /// - Returns: The value returned by the block.
131
+ @inlinable
132
+ internal func withLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
133
+ self . lock ( )
134
+ defer {
135
+ self . unlock ( )
136
+ }
137
+ return try body ( )
138
+ }
139
+
140
+ // specialise Void return (for performance)
141
+ @inlinable
142
+ internal func withLockVoid( _ body: ( ) throws -> Void ) rethrows {
143
+ try self . withLock ( body)
144
+ }
145
+ }
146
+
147
+ /// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
148
+ ///
149
+ /// This object provides a lock on top of a single `pthread_rwlock_t`. This kind
150
+ /// of lock is safe to use with `libpthread`-based threading models, such as the
151
+ /// one used by NIO. On Windows, the lock is based on the substantially similar
152
+ /// `SRWLOCK` type.
153
+ internal final class ReadWriteLock : @unchecked Sendable {
154
+ #if canImport(WASILibc)
155
+ // WASILibc is single threaded, provides no locks
156
+ #elseif os(Windows)
157
+ fileprivate let rwlock : UnsafeMutablePointer < SRWLOCK > =
158
+ UnsafeMutablePointer . allocate ( capacity: 1 )
159
+ fileprivate var shared : Bool = true
160
+ #else
161
+ fileprivate let rwlock : UnsafeMutablePointer < pthread_rwlock_t > =
162
+ UnsafeMutablePointer . allocate ( capacity: 1 )
163
+ #endif
54
164
55
165
/// Create a new lock.
56
166
public init ( ) {
167
+ #if canImport(WASILibc)
168
+ // WASILibc is single threaded, provides no locks
169
+ #elseif os(Windows)
170
+ InitializeSRWLock ( self . rwlock)
171
+ #else
57
172
let err = pthread_rwlock_init ( self . rwlock, nil )
58
- precondition ( err == 0 , " pthread_rwlock_init failed with error \( err) " )
173
+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
174
+ #endif
59
175
}
60
176
61
177
deinit {
178
+ #if canImport(WASILibc)
179
+ // WASILibc is single threaded, provides no locks
180
+ #elseif os(Windows)
181
+ // SRWLOCK does not need to be free'd
182
+ self . rwlock. deallocate ( )
183
+ #else
62
184
let err = pthread_rwlock_destroy ( self . rwlock)
63
- precondition ( err == 0 , " pthread_rwlock_destroy failed with error \( err) " )
185
+ precondition ( err == 0 , " \( #function ) failed in pthread_rwlock with error \( err) " )
64
186
self . rwlock. deallocate ( )
187
+ #endif
65
188
}
66
189
67
190
/// Acquire a reader lock.
68
191
///
69
- /// Whenever possible, consider using `withLock` instead of this method and
70
- /// `unlock`, to simplify lock handling.
71
- public func lockRead( ) {
192
+ /// Whenever possible, consider using `withReaderLock` instead of this
193
+ /// method and `unlock`, to simplify lock handling.
194
+ fileprivate func lockRead( ) {
195
+ #if canImport(WASILibc)
196
+ // WASILibc is single threaded, provides no locks
197
+ #elseif os(Windows)
198
+ AcquireSRWLockShared ( self . rwlock)
199
+ self . shared = true
200
+ #else
72
201
let err = pthread_rwlock_rdlock ( self . rwlock)
73
- precondition ( err == 0 , " pthread_rwlock_rdlock failed with error \( err) " )
202
+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
203
+ #endif
74
204
}
75
205
76
206
/// Acquire a writer lock.
77
207
///
78
- /// Whenever possible, consider using `withLock` instead of this method and
79
- /// `unlock`, to simplify lock handling.
80
- public func lockWrite( ) {
208
+ /// Whenever possible, consider using `withWriterLock` instead of this
209
+ /// method and `unlock`, to simplify lock handling.
210
+ fileprivate func lockWrite( ) {
211
+ #if canImport(WASILibc)
212
+ // WASILibc is single threaded, provides no locks
213
+ #elseif os(Windows)
214
+ AcquireSRWLockExclusive ( self . rwlock)
215
+ self . shared = false
216
+ #else
81
217
let err = pthread_rwlock_wrlock ( self . rwlock)
82
- precondition ( err == 0 , " pthread_rwlock_wrlock failed with error \( err) " )
218
+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
219
+ #endif
83
220
}
84
221
85
222
/// Release the lock.
86
223
///
87
- /// Whenever possible, consider using `withLock` instead of this method and
88
- /// `lock`, to simplify lock handling.
89
- public func unlock( ) {
224
+ /// Whenever possible, consider using `withReaderLock` and `withWriterLock`
225
+ /// instead of this method and `lockRead` and `lockWrite`, to simplify lock
226
+ /// handling.
227
+ fileprivate func unlock( ) {
228
+ #if canImport(WASILibc)
229
+ // WASILibc is single threaded, provides no locks
230
+ #elseif os(Windows)
231
+ if self . shared {
232
+ ReleaseSRWLockShared ( self . rwlock)
233
+ } else {
234
+ ReleaseSRWLockExclusive ( self . rwlock)
235
+ }
236
+ #else
90
237
let err = pthread_rwlock_unlock ( self . rwlock)
91
- precondition ( err == 0 , " pthread_rwlock_unlock failed with error \( err) " )
238
+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
239
+ #endif
92
240
}
93
241
}
94
242
95
243
extension ReadWriteLock {
96
244
/// Acquire the reader lock for the duration of the given block.
97
245
///
98
- /// This convenience method should be preferred to `lock ` and `unlock` in
99
- /// most situations, as it ensures that the lock will be released regardless
100
- /// of how `body` exits.
246
+ /// This convenience method should be preferred to `lockRead ` and `unlock`
247
+ /// in most situations, as it ensures that the lock will be released
248
+ /// regardless of how `body` exits.
101
249
///
102
- /// - Parameter body: The block to execute while holding the lock.
250
+ /// - Parameter body: The block to execute while holding the reader lock.
103
251
/// - Returns: The value returned by the block.
104
252
@inlinable
105
- public func withReaderLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
253
+ internal func withReaderLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
106
254
self . lockRead ( )
107
255
defer {
108
256
self . unlock ( )
@@ -112,37 +260,30 @@ extension ReadWriteLock {
112
260
113
261
/// Acquire the writer lock for the duration of the given block.
114
262
///
115
- /// This convenience method should be preferred to `lock ` and `unlock` in
116
- /// most situations, as it ensures that the lock will be released regardless
117
- /// of how `body` exits.
263
+ /// This convenience method should be preferred to `lockWrite ` and `unlock`
264
+ /// in most situations, as it ensures that the lock will be released
265
+ /// regardless of how `body` exits.
118
266
///
119
- /// - Parameter body: The block to execute while holding the lock.
267
+ /// - Parameter body: The block to execute while holding the writer lock.
120
268
/// - Returns: The value returned by the block.
121
269
@inlinable
122
- public func withWriterLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
270
+ internal func withWriterLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
123
271
self . lockWrite ( )
124
272
defer {
125
273
self . unlock ( )
126
274
}
127
275
return try body ( )
128
276
}
129
- }
130
277
131
- /// A wrapper providing locked access to a value.
132
- ///
133
- /// Marked as @unchecked Sendable due to the synchronization being
134
- /// performed manually using locks.
135
- @_spi ( Locking) // Use the `package` access modifier once min Swift version is increased.
136
- public final class LockedValueBox < Value: Sendable > : @unchecked Sendable {
137
- private let lock = ReadWriteLock ( )
138
- private var value : Value
139
- public init ( _ value: Value ) {
140
- self . value = value
278
+ // specialise Void return (for performance)
279
+ @inlinable
280
+ internal func withReaderLockVoid( _ body: ( ) throws -> Void ) rethrows {
281
+ try self . withReaderLock ( body)
141
282
}
142
283
143
- public func withValue < R > ( _ work : ( inout Value ) throws -> R ) rethrows -> R {
144
- try self . lock . withWriterLock {
145
- try work ( & self . value )
146
- }
284
+ // specialise Void return (for performance)
285
+ @ inlinable
286
+ internal func withWriterLockVoid ( _ body : ( ) throws -> Void ) rethrows {
287
+ try self . withWriterLock ( body )
147
288
}
148
- }
289
+ }
0 commit comments