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
4544
46- /// A threading lock based on `libpthread` instead of `libdispatch`.
45+ /// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
4746///
48- /// This object provides a lock on top of a single `pthread_mutex_t `. This kind
47+ /// This object provides a lock on top of a single `pthread_rwlock_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+ public final class ReadWriteLock : @unchecked Sendable {
52+ #if canImport(WASILibc)
53+ // WASILibc is single threaded, provides no locks
54+ #elseif os(Windows)
55+ fileprivate let rwlock : UnsafeMutablePointer < SRWLOCK > =
56+ UnsafeMutablePointer . allocate ( capacity: 1 )
57+ fileprivate var shared : Bool = true
58+ #else
59+ fileprivate let rwlock : UnsafeMutablePointer < pthread_rwlock_t > =
60+ UnsafeMutablePointer . allocate ( capacity: 1 )
61+ #endif
5462
5563 /// Create a new lock.
5664 public init ( ) {
65+ #if canImport(WASILibc)
66+ // WASILibc is single threaded, provides no locks
67+ #elseif os(Windows)
68+ InitializeSRWLock ( self . rwlock)
69+ #else
5770 let err = pthread_rwlock_init ( self . rwlock, nil )
58- precondition ( err == 0 , " pthread_rwlock_init failed with error \( err) " )
71+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
72+ #endif
5973 }
6074
6175 deinit {
76+ #if canImport(WASILibc)
77+ // WASILibc is single threaded, provides no locks
78+ #elseif os(Windows)
79+ // SRWLOCK does not need to be free'd
80+ self . rwlock. deallocate ( )
81+ #else
6282 let err = pthread_rwlock_destroy ( self . rwlock)
63- precondition ( err == 0 , " pthread_rwlock_destroy failed with error \( err) " )
83+ precondition ( err == 0 , " \( #function ) failed in pthread_rwlock with error \( err) " )
6484 self . rwlock. deallocate ( )
85+ #endif
6586 }
6687
6788 /// Acquire a reader lock.
6889 ///
69- /// Whenever possible, consider using `withLock ` instead of this method and
70- /// `unlock`, to simplify lock handling.
90+ /// Whenever possible, consider using `withReaderLock ` instead of this
91+ /// method and `unlock`, to simplify lock handling.
7192 public func lockRead( ) {
93+ #if canImport(WASILibc)
94+ // WASILibc is single threaded, provides no locks
95+ #elseif os(Windows)
96+ AcquireSRWLockShared ( self . rwlock)
97+ self . shared = true
98+ #else
7299 let err = pthread_rwlock_rdlock ( self . rwlock)
73- precondition ( err == 0 , " pthread_rwlock_rdlock failed with error \( err) " )
100+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
101+ #endif
74102 }
75103
76104 /// Acquire a writer lock.
77105 ///
78- /// Whenever possible, consider using `withLock ` instead of this method and
79- /// `unlock`, to simplify lock handling.
106+ /// Whenever possible, consider using `withWriterLock ` instead of this
107+ /// method and `unlock`, to simplify lock handling.
80108 public func lockWrite( ) {
109+ #if canImport(WASILibc)
110+ // WASILibc is single threaded, provides no locks
111+ #elseif os(Windows)
112+ AcquireSRWLockExclusive ( self . rwlock)
113+ self . shared = false
114+ #else
81115 let err = pthread_rwlock_wrlock ( self . rwlock)
82- precondition ( err == 0 , " pthread_rwlock_wrlock failed with error \( err) " )
116+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
117+ #endif
83118 }
84119
85120 /// Release the lock.
86121 ///
87- /// Whenever possible, consider using `withLock` instead of this method and
88- /// `lock`, to simplify lock handling.
122+ /// Whenever possible, consider using `withReaderLock` and `withWriterLock`
123+ /// instead of this method and `lockRead` and `lockWrite`, to simplify lock
124+ /// handling.
89125 public func unlock( ) {
126+ #if canImport(WASILibc)
127+ // WASILibc is single threaded, provides no locks
128+ #elseif os(Windows)
129+ if self . shared {
130+ ReleaseSRWLockShared ( self . rwlock)
131+ } else {
132+ ReleaseSRWLockExclusive ( self . rwlock)
133+ }
134+ #else
90135 let err = pthread_rwlock_unlock ( self . rwlock)
91- precondition ( err == 0 , " pthread_rwlock_unlock failed with error \( err) " )
136+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
137+ #endif
92138 }
93139}
94140
95141extension ReadWriteLock {
96142 /// Acquire the reader lock for the duration of the given block.
97143 ///
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.
144+ /// This convenience method should be preferred to `lockRead ` and `unlock`
145+ /// in most situations, as it ensures that the lock will be released
146+ /// regardless of how `body` exits.
101147 ///
102- /// - Parameter body: The block to execute while holding the lock.
148+ /// - Parameter body: The block to execute while holding the reader lock.
103149 /// - Returns: The value returned by the block.
104150 @inlinable
105151 public func withReaderLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
@@ -112,11 +158,11 @@ extension ReadWriteLock {
112158
113159 /// Acquire the writer lock for the duration of the given block.
114160 ///
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.
161+ /// This convenience method should be preferred to `lockWrite ` and `unlock`
162+ /// in most situations, as it ensures that the lock will be released
163+ /// regardless of how `body` exits.
118164 ///
119- /// - Parameter body: The block to execute while holding the lock.
165+ /// - Parameter body: The block to execute while holding the writer lock.
120166 /// - Returns: The value returned by the block.
121167 @inlinable
122168 public func withWriterLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
@@ -126,6 +172,18 @@ extension ReadWriteLock {
126172 }
127173 return try body ( )
128174 }
175+
176+ // specialise Void return (for performance)
177+ @inlinable
178+ internal func withReaderLockVoid( _ body: ( ) throws -> Void ) rethrows {
179+ try self . withReaderLock ( body)
180+ }
181+
182+ // specialise Void return (for performance)
183+ @inlinable
184+ internal func withWriterLockVoid( _ body: ( ) throws -> Void ) rethrows {
185+ try self . withWriterLock ( body)
186+ }
129187}
130188
131189/// A wrapper providing locked access to a value.
0 commit comments