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
45
44
46
- /// A threading lock based on `libpthread` instead of `libdispatch`.
45
+ /// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
47
46
///
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
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
+ 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
54
62
55
63
/// Create a new lock.
56
64
public init ( ) {
65
+ #if canImport(WASILibc)
66
+ // WASILibc is single threaded, provides no locks
67
+ #elseif os(Windows)
68
+ InitializeSRWLock ( self . rwlock)
69
+ #else
57
70
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
59
73
}
60
74
61
75
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
62
82
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) " )
64
84
self . rwlock. deallocate ( )
85
+ #endif
65
86
}
66
87
67
88
/// Acquire a reader lock.
68
89
///
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.
71
92
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
72
99
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
74
102
}
75
103
76
104
/// Acquire a writer lock.
77
105
///
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.
80
108
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
81
115
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
83
118
}
84
119
85
120
/// Release the lock.
86
121
///
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.
89
125
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
90
135
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
92
138
}
93
139
}
94
140
95
141
extension ReadWriteLock {
96
142
/// Acquire the reader lock for the duration of the given block.
97
143
///
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.
101
147
///
102
- /// - Parameter body: The block to execute while holding the lock.
148
+ /// - Parameter body: The block to execute while holding the reader lock.
103
149
/// - Returns: The value returned by the block.
104
150
@inlinable
105
151
public func withReaderLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
@@ -112,11 +158,11 @@ extension ReadWriteLock {
112
158
113
159
/// Acquire the writer lock for the duration of the given block.
114
160
///
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.
118
164
///
119
- /// - Parameter body: The block to execute while holding the lock.
165
+ /// - Parameter body: The block to execute while holding the writer lock.
120
166
/// - Returns: The value returned by the block.
121
167
@inlinable
122
168
public func withWriterLock< T> ( _ body: ( ) throws -> T ) rethrows -> T {
@@ -126,6 +172,18 @@ extension ReadWriteLock {
126
172
}
127
173
return try body ( )
128
174
}
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
+ }
129
187
}
130
188
131
189
/// A wrapper providing locked access to a value.
0 commit comments