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)
@@ -43,66 +47,114 @@ import wasi_pthread
43
47
#error("Unsupported runtime")
44
48
#endif
45
49
46
- /// A threading lock based on `libpthread` instead of `libdispatch`.
50
+
51
+ /// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
47
52
///
48
- /// This object provides a lock on top of a single `pthread_mutex_t `. This kind
53
+ /// This object provides a lock on top of a single `pthread_rwlock_t `. This kind
49
54
/// 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 )
55
+ /// one used by NIO. On Windows, the lock is based on the substantially similar
56
+ /// `SRWLOCK` type.
57
+ internal final class ReadWriteLock : @unchecked Sendable {
58
+ #if canImport(WASILibc)
59
+ // WASILibc is single threaded, provides no locks
60
+ #elseif os(Windows)
61
+ fileprivate let rwlock : UnsafeMutablePointer < SRWLOCK > =
62
+ UnsafeMutablePointer . allocate ( capacity: 1 )
63
+ fileprivate var shared : Bool = true
64
+ #else
65
+ fileprivate let rwlock : UnsafeMutablePointer < pthread_rwlock_t > =
66
+ UnsafeMutablePointer . allocate ( capacity: 1 )
67
+ #endif
54
68
55
69
/// Create a new lock.
56
70
public init ( ) {
71
+ #if canImport(WASILibc)
72
+ // WASILibc is single threaded, provides no locks
73
+ #elseif os(Windows)
74
+ InitializeSRWLock ( self . rwlock)
75
+ #else
57
76
let err = pthread_rwlock_init ( self . rwlock, nil )
58
- precondition ( err == 0 , " pthread_rwlock_init failed with error \( err) " )
77
+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
78
+ #endif
59
79
}
60
80
61
81
deinit {
82
+ #if canImport(WASILibc)
83
+ // WASILibc is single threaded, provides no locks
84
+ #elseif os(Windows)
85
+ // SRWLOCK does not need to be free'd
86
+ self . rwlock. deallocate ( )
87
+ #else
62
88
let err = pthread_rwlock_destroy ( self . rwlock)
63
- precondition ( err == 0 , " pthread_rwlock_destroy failed with error \( err) " )
89
+ precondition ( err == 0 , " \( #function ) failed in pthread_rwlock with error \( err) " )
64
90
self . rwlock. deallocate ( )
91
+ #endif
65
92
}
66
93
67
94
/// Acquire a reader lock.
68
95
///
69
- /// Whenever possible, consider using `withLock` instead of this method and
70
- /// `unlock`, to simplify lock handling.
71
- public func lockRead( ) {
96
+ /// Whenever possible, consider using `withReaderLock` instead of this
97
+ /// method and `unlock`, to simplify lock handling.
98
+ fileprivate func lockRead( ) {
99
+ #if canImport(WASILibc)
100
+ // WASILibc is single threaded, provides no locks
101
+ #elseif os(Windows)
102
+ AcquireSRWLockShared ( self . rwlock)
103
+ self . shared = true
104
+ #else
72
105
let err = pthread_rwlock_rdlock ( self . rwlock)
73
- precondition ( err == 0 , " pthread_rwlock_rdlock failed with error \( err) " )
106
+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
107
+ #endif
74
108
}
75
109
76
110
/// Acquire a writer lock.
77
111
///
78
- /// Whenever possible, consider using `withLock` instead of this method and
79
- /// `unlock`, to simplify lock handling.
80
- public func lockWrite( ) {
112
+ /// Whenever possible, consider using `withWriterLock` instead of this
113
+ /// method and `unlock`, to simplify lock handling.
114
+ fileprivate func lockWrite( ) {
115
+ #if canImport(WASILibc)
116
+ // WASILibc is single threaded, provides no locks
117
+ #elseif os(Windows)
118
+ AcquireSRWLockExclusive ( self . rwlock)
119
+ self . shared = false
120
+ #else
81
121
let err = pthread_rwlock_wrlock ( self . rwlock)
82
- precondition ( err == 0 , " pthread_rwlock_wrlock failed with error \( err) " )
122
+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
123
+ #endif
83
124
}
84
125
85
126
/// Release the lock.
86
127
///
87
- /// Whenever possible, consider using `withLock` instead of this method and
88
- /// `lock`, to simplify lock handling.
89
- public func unlock( ) {
128
+ /// Whenever possible, consider using `withReaderLock` and `withWriterLock`
129
+ /// instead of this method and `lockRead` and `lockWrite`, to simplify lock
130
+ /// handling.
131
+ fileprivate func unlock( ) {
132
+ #if canImport(WASILibc)
133
+ // WASILibc is single threaded, provides no locks
134
+ #elseif os(Windows)
135
+ if self . shared {
136
+ ReleaseSRWLockShared ( self . rwlock)
137
+ } else {
138
+ ReleaseSRWLockExclusive ( self . rwlock)
139
+ }
140
+ #else
90
141
let err = pthread_rwlock_unlock ( self . rwlock)
91
- precondition ( err == 0 , " pthread_rwlock_unlock failed with error \( err) " )
142
+ precondition ( err == 0 , " \( #function) failed in pthread_rwlock with error \( err) " )
143
+ #endif
92
144
}
93
145
}
94
146
95
147
extension ReadWriteLock {
96
148
/// Acquire the reader lock for the duration of the given block.
97
149
///
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.
150
+ /// This convenience method should be preferred to `lockRead ` and `unlock`
151
+ /// in most situations, as it ensures that the lock will be released
152
+ /// regardless of how `body` exits.
101
153
///
102
- /// - Parameter body: The block to execute while holding the lock.
154
+ /// - Parameter body: The block to execute while holding the reader lock.
103
155
/// - Returns: The value returned by the block.
104
156
@inlinable
105
- public func withReaderLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
157
+ internal func withReaderLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
106
158
self . lockRead ( )
107
159
defer {
108
160
self . unlock ( )
@@ -112,20 +164,32 @@ extension ReadWriteLock {
112
164
113
165
/// Acquire the writer lock for the duration of the given block.
114
166
///
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.
167
+ /// This convenience method should be preferred to `lockWrite ` and `unlock`
168
+ /// in most situations, as it ensures that the lock will be released
169
+ /// regardless of how `body` exits.
118
170
///
119
- /// - Parameter body: The block to execute while holding the lock.
171
+ /// - Parameter body: The block to execute while holding the writer lock.
120
172
/// - Returns: The value returned by the block.
121
173
@inlinable
122
- public func withWriterLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
174
+ internal func withWriterLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
123
175
self . lockWrite ( )
124
176
defer {
125
177
self . unlock ( )
126
178
}
127
179
return try body ( )
128
180
}
181
+
182
+ // specialise Void return (for performance)
183
+ @inlinable
184
+ internal func withReaderLockVoid( _ body: ( ) throws -> Void ) rethrows {
185
+ try self . withReaderLock ( body)
186
+ }
187
+
188
+ // specialise Void return (for performance)
189
+ @inlinable
190
+ internal func withWriterLockVoid( _ body: ( ) throws -> Void ) rethrows {
191
+ try self . withWriterLock ( body)
192
+ }
129
193
}
130
194
131
195
/// A wrapper providing locked access to a value.
0 commit comments