2626//
2727//===----------------------------------------------------------------------===//
2828
29- #if canImport(Darwin)
29+ #if canImport(WASILibc)
30+ // No locking on WASILibc
31+ #elseif canImport(Darwin)
3032import Darwin
33+ #elseif os(Windows)
34+ import WinSDK
3135#elseif canImport(Glibc)
3236import Glibc
3337#elseif canImport(Android)
3438import Android
3539#elseif canImport(Musl)
3640import Musl
37- #elseif canImport(WASILibc)
38- import WASILibc
39- #if canImport(wasi_pthread)
40- import wasi_pthread
41- #endif
4241#else
4342#error("Unsupported runtime")
4443#endif
@@ -47,62 +46,211 @@ import wasi_pthread
4746///
4847/// This object provides a lock on top of a single `pthread_mutex_t`. This kind
4948/// 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
54164
55165 /// Create a new lock.
56166 public init ( ) {
167+ #if canImport(WASILibc)
168+ // WASILibc is single threaded, provides no locks
169+ #elseif os(Windows)
170+ InitializeSRWLock ( self . rwlock)
171+ #else
57172 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
59175 }
60176
61177 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
62184 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) " )
64186 self . rwlock. deallocate ( )
187+ #endif
65188 }
66189
67190 /// Acquire a reader lock.
68191 ///
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
72201 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
74204 }
75205
76206 /// Acquire a writer lock.
77207 ///
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
81217 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
83220 }
84221
85222 /// Release the lock.
86223 ///
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
90237 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
92240 }
93241}
94242
95243extension ReadWriteLock {
96244 /// Acquire the reader lock for the duration of the given block.
97245 ///
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.
101249 ///
102- /// - Parameter body: The block to execute while holding the lock.
250+ /// - Parameter body: The block to execute while holding the reader lock.
103251 /// - Returns: The value returned by the block.
104252 @inlinable
105- public func withReaderLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
253+ internal func withReaderLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
106254 self . lockRead ( )
107255 defer {
108256 self . unlock ( )
@@ -112,37 +260,30 @@ extension ReadWriteLock {
112260
113261 /// Acquire the writer lock for the duration of the given block.
114262 ///
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.
118266 ///
119- /// - Parameter body: The block to execute while holding the lock.
267+ /// - Parameter body: The block to execute while holding the writer lock.
120268 /// - Returns: The value returned by the block.
121269 @inlinable
122- public func withWriterLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
270+ internal func withWriterLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
123271 self . lockWrite ( )
124272 defer {
125273 self . unlock ( )
126274 }
127275 return try body ( )
128276 }
129- }
130277
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)
141282 }
142283
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 )
147288 }
148- }
289+ }
0 commit comments