|
11 | 11 | //
|
12 | 12 | //===----------------------------------------------------------------------===//
|
13 | 13 |
|
14 |
| -// Vendored from NIO 2.62.0 commit 702cd7c56d5d44eeba73fdf83918339b26dc855c on 2023-12-02 |
| 14 | +// Vendored from NIO 2.83.0 |
15 | 15 |
|
16 | 16 | //===----------------------------------------------------------------------===//
|
17 | 17 | //
|
@@ -48,166 +48,190 @@ extension Locking {
|
48 | 48 | public final class ConditionLock<T: Equatable> {
|
49 | 49 | private var _value: T
|
50 | 50 | private let mutex: FastLock
|
51 |
| - #if os(Windows) |
| 51 | +#if os(Windows) |
52 | 52 | private let cond: UnsafeMutablePointer<CONDITION_VARIABLE> =
|
53 |
| - UnsafeMutablePointer.allocate(capacity: 1) |
54 |
| - #else |
| 53 | + UnsafeMutablePointer.allocate(capacity: 1) |
| 54 | +#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded)) |
55 | 55 | private let cond: UnsafeMutablePointer<pthread_cond_t> =
|
56 |
| - UnsafeMutablePointer.allocate(capacity: 1) |
57 |
| - #endif |
58 |
| - |
| 56 | + UnsafeMutablePointer.allocate(capacity: 1) |
| 57 | +#endif |
| 58 | + |
59 | 59 | /// Create the lock, and initialize the state variable to `value`.
|
60 | 60 | ///
|
61 | 61 | /// - Parameter value: The initial value to give the state variable.
|
62 | 62 | public init(value: T) {
|
63 | 63 | self._value = value
|
64 | 64 | self.mutex = FastLock()
|
65 |
| - #if os(Windows) |
| 65 | +#if os(Windows) |
66 | 66 | InitializeConditionVariable(self.cond)
|
67 |
| - #else |
| 67 | +#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded)) |
68 | 68 | let err = pthread_cond_init(self.cond, nil)
|
69 | 69 | precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)")
|
70 |
| - #endif |
| 70 | +#endif |
71 | 71 | }
|
72 |
| - |
| 72 | + |
73 | 73 | deinit {
|
74 |
| - #if os(Windows) |
| 74 | +#if os(Windows) |
75 | 75 | // condition variables do not need to be explicitly destroyed
|
76 |
| - #else |
| 76 | +#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded)) |
77 | 77 | let err = pthread_cond_destroy(self.cond)
|
78 | 78 | precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)")
|
79 |
| - #endif |
80 | 79 | self.cond.deallocate()
|
| 80 | +#endif |
81 | 81 | }
|
82 |
| - } |
83 |
| -} |
84 |
| - |
85 |
| -extension Locking.ConditionLock { |
86 |
| - /// Acquire the lock, regardless of the value of the state variable. |
87 |
| - public func lock() { |
88 |
| - self.mutex.lock() |
89 |
| - } |
90 |
| - |
91 |
| - /// Release the lock, regardless of the value of the state variable. |
92 |
| - public func unlock() { |
93 |
| - self.mutex.unlock() |
94 |
| - } |
95 |
| - |
96 |
| - /// The value of the state variable. |
97 |
| - /// |
98 |
| - /// Obtaining the value of the state variable requires acquiring the lock. |
99 |
| - /// This means that it is not safe to access this property while holding the |
100 |
| - /// lock: it is only safe to use it when not holding it. |
101 |
| - public var value: T { |
102 |
| - self.lock() |
103 |
| - defer { |
104 |
| - self.unlock() |
| 82 | + |
| 83 | + /// Acquire the lock, regardless of the value of the state variable. |
| 84 | + public func lock() { |
| 85 | + self.mutex.lock() |
105 | 86 | }
|
106 |
| - return self._value |
107 |
| - } |
108 |
| - |
109 |
| - /// Acquire the lock when the state variable is equal to `wantedValue`. |
110 |
| - /// |
111 |
| - /// - Parameter wantedValue: The value to wait for the state variable |
112 |
| - /// to have before acquiring the lock. |
113 |
| - public func lock(whenValue wantedValue: T) { |
114 |
| - self.lock() |
115 |
| - while true { |
116 |
| - if self._value == wantedValue { |
117 |
| - break |
118 |
| - } |
119 |
| - self.mutex.withLockPrimitive { mutex in |
120 |
| -#if os(Windows) |
121 |
| - let result = SleepConditionVariableSRW(self.cond, mutex, INFINITE, 0) |
122 |
| - precondition(result, "\(#function) failed in SleepConditionVariableSRW with error \(GetLastError())") |
123 |
| -#else |
124 |
| - let err = pthread_cond_wait(self.cond, mutex) |
125 |
| - precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)") |
126 |
| -#endif |
| 87 | + |
| 88 | + /// Release the lock, regardless of the value of the state variable. |
| 89 | + public func unlock() { |
| 90 | + self.mutex.unlock() |
| 91 | + } |
| 92 | + |
| 93 | + /// The value of the state variable. |
| 94 | + /// |
| 95 | + /// Obtaining the value of the state variable requires acquiring the lock. |
| 96 | + /// This means that it is not safe to access this property while holding the |
| 97 | + /// lock: it is only safe to use it when not holding it. |
| 98 | + public var value: T { |
| 99 | + self.lock() |
| 100 | + defer { |
| 101 | + self.unlock() |
127 | 102 | }
|
| 103 | + return self._value |
128 | 104 | }
|
129 |
| - } |
130 |
| - |
131 |
| - /// Acquire the lock when the state variable is equal to `wantedValue`, |
132 |
| - /// waiting no more than `timeoutSeconds` seconds. |
133 |
| - /// |
134 |
| - /// - Parameter wantedValue: The value to wait for the state variable |
135 |
| - /// to have before acquiring the lock. |
136 |
| - /// - Parameter timeoutSeconds: The number of seconds to wait to acquire |
137 |
| - /// the lock before giving up. |
138 |
| - /// - Returns: `true` if the lock was acquired, `false` if the wait timed out. |
139 |
| - public func lock(whenValue wantedValue: T, timeoutSeconds: Double) -> Bool { |
140 |
| - precondition(timeoutSeconds >= 0) |
141 |
| - |
| 105 | + |
| 106 | + /// Acquire the lock when the state variable is equal to `wantedValue`. |
| 107 | + /// |
| 108 | + /// - Parameter wantedValue: The value to wait for the state variable |
| 109 | + /// to have before acquiring the lock. |
| 110 | + public func lock(whenValue wantedValue: T) { |
| 111 | + self.lock() |
| 112 | + while true { |
| 113 | + if self._value == wantedValue { |
| 114 | + break |
| 115 | + } |
| 116 | + self.mutex.withLockPrimitive { mutex in |
142 | 117 | #if os(Windows)
|
143 |
| - var dwMilliseconds: DWORD = DWORD(timeoutSeconds * 1000) |
144 |
| - |
145 |
| - self.lock() |
146 |
| - while true { |
147 |
| - if self._value == wantedValue { |
148 |
| - return true |
149 |
| - } |
150 |
| - |
151 |
| - let dwWaitStart = timeGetTime() |
152 |
| - if !SleepConditionVariableSRW(self.cond, self.mutex._storage.mutex, |
153 |
| - dwMilliseconds, 0) { |
154 |
| - let dwError = GetLastError() |
155 |
| - if (dwError == ERROR_TIMEOUT) { |
156 |
| - self.unlock() |
157 |
| - return false |
| 118 | + let result = SleepConditionVariableSRW(self.cond, mutex, INFINITE, 0) |
| 119 | + precondition(result, "\(#function) failed in SleepConditionVariableSRW with error \(GetLastError())") |
| 120 | +#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded)) |
| 121 | + let err = pthread_cond_wait(self.cond, mutex) |
| 122 | + precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)") |
| 123 | +#endif |
158 | 124 | }
|
159 |
| - fatalError("SleepConditionVariableSRW: \(dwError)") |
160 | 125 | }
|
161 |
| - |
162 |
| - // NOTE: this may be a spurious wakeup, adjust the timeout accordingly |
163 |
| - dwMilliseconds = dwMilliseconds - (timeGetTime() - dwWaitStart) |
164 | 126 | }
|
165 |
| -#else |
166 |
| - let nsecPerSec: Int64 = 1000000000 |
167 |
| - self.lock() |
168 |
| - /* the timeout as a (seconds, nano seconds) pair */ |
169 |
| - let timeoutNS = Int64(timeoutSeconds * Double(nsecPerSec)) |
170 |
| - |
171 |
| - var curTime = timeval() |
172 |
| - gettimeofday(&curTime, nil) |
173 |
| - |
174 |
| - let allNSecs: Int64 = timeoutNS + Int64(curTime.tv_usec) * 1000 |
175 |
| - var timeoutAbs = timespec(tv_sec: curTime.tv_sec + Int((allNSecs / nsecPerSec)), |
176 |
| - tv_nsec: Int(allNSecs % nsecPerSec)) |
177 |
| - assert(timeoutAbs.tv_nsec >= 0 && timeoutAbs.tv_nsec < Int(nsecPerSec)) |
178 |
| - assert(timeoutAbs.tv_sec >= curTime.tv_sec) |
179 |
| - return self.mutex.withLockPrimitive { mutex -> Bool in |
| 127 | + |
| 128 | + /// Acquire the lock when the state variable is equal to `wantedValue`, |
| 129 | + /// waiting no more than `timeoutSeconds` seconds. |
| 130 | + /// |
| 131 | + /// - Parameter wantedValue: The value to wait for the state variable |
| 132 | + /// to have before acquiring the lock. |
| 133 | + /// - Parameter timeoutSeconds: The number of seconds to wait to acquire |
| 134 | + /// the lock before giving up. |
| 135 | + /// - Returns: `true` if the lock was acquired, `false` if the wait timed out. |
| 136 | + public func lock(whenValue wantedValue: T, timeoutSeconds: Double) -> Bool { |
| 137 | + precondition(timeoutSeconds >= 0) |
| 138 | + |
| 139 | +#if os(Windows) |
| 140 | + var dwMilliseconds: DWORD = DWORD(timeoutSeconds * 1000) |
| 141 | + |
| 142 | + self.lock() |
180 | 143 | while true {
|
181 | 144 | if self._value == wantedValue {
|
182 | 145 | return true
|
183 | 146 | }
|
184 |
| - switch pthread_cond_timedwait(self.cond, mutex, &timeoutAbs) { |
185 |
| - case 0: |
186 |
| - continue |
187 |
| - case ETIMEDOUT: |
188 |
| - self.unlock() |
189 |
| - return false |
190 |
| - case let e: |
191 |
| - fatalError("caught error \(e) when calling pthread_cond_timedwait") |
| 147 | + |
| 148 | + let dwWaitStart = timeGetTime() |
| 149 | + let result = self.mutex.withLockPrimitive { mutex in |
| 150 | + SleepConditionVariableSRW(self.cond, mutex, dwMilliseconds, 0) |
192 | 151 | }
|
| 152 | + if !result { |
| 153 | + let dwError = GetLastError() |
| 154 | + if dwError == ERROR_TIMEOUT { |
| 155 | + self.unlock() |
| 156 | + return false |
| 157 | + } |
| 158 | + fatalError("SleepConditionVariableSRW: \(dwError)") |
| 159 | + } |
| 160 | + // NOTE: this may be a spurious wakeup, adjust the timeout accordingly |
| 161 | + dwMilliseconds = dwMilliseconds - (timeGetTime() - dwWaitStart) |
193 | 162 | }
|
| 163 | +#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded)) |
| 164 | + let nsecPerSec: Int64 = 1_000_000_000 |
| 165 | + self.lock() |
| 166 | + // the timeout as a (seconds, nano seconds) pair |
| 167 | + let timeoutNS = Int64(timeoutSeconds * Double(nsecPerSec)) |
| 168 | + |
| 169 | + var curTime = timeval() |
| 170 | + gettimeofday(&curTime, nil) |
| 171 | + |
| 172 | + let allNSecs: Int64 = timeoutNS + Int64(curTime.tv_usec) * 1000 |
| 173 | +#if canImport(wasi_pthread) |
| 174 | + let tvSec = curTime.tv_sec + (allNSecs / nsecPerSec) |
| 175 | +#else |
| 176 | + let tvSec = curTime.tv_sec + Int((allNSecs / nsecPerSec)) |
| 177 | +#endif |
| 178 | + |
| 179 | + var timeoutAbs = timespec( |
| 180 | + tv_sec: tvSec, |
| 181 | + tv_nsec: Int(allNSecs % nsecPerSec) |
| 182 | + ) |
| 183 | + assert(timeoutAbs.tv_nsec >= 0 && timeoutAbs.tv_nsec < Int(nsecPerSec)) |
| 184 | + assert(timeoutAbs.tv_sec >= curTime.tv_sec) |
| 185 | + return self.mutex.withLockPrimitive { mutex -> Bool in |
| 186 | + while true { |
| 187 | + if self._value == wantedValue { |
| 188 | + return true |
| 189 | + } |
| 190 | + switch pthread_cond_timedwait(self.cond, mutex, &timeoutAbs) { |
| 191 | + case 0: |
| 192 | + continue |
| 193 | + case ETIMEDOUT: |
| 194 | + self.unlock() |
| 195 | + return false |
| 196 | + case let e: |
| 197 | + fatalError("caught error \(e) when calling pthread_cond_timedwait") |
| 198 | + } |
| 199 | + } |
| 200 | + } |
| 201 | +#else |
| 202 | + return true |
| 203 | +#endif |
194 | 204 | }
|
| 205 | + |
| 206 | + /// Release the lock, setting the state variable to `newValue`. |
| 207 | + /// |
| 208 | + /// - Parameter newValue: The value to give to the state variable when we |
| 209 | + /// release the lock. |
| 210 | + public func unlock(withValue newValue: T) { |
| 211 | + self._value = newValue |
| 212 | + self.unlock() |
| 213 | +#if os(Windows) |
| 214 | + WakeAllConditionVariable(self.cond) |
| 215 | +#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded)) |
| 216 | + let err = pthread_cond_broadcast(self.cond) |
| 217 | + precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)") |
195 | 218 | #endif
|
| 219 | + } |
196 | 220 | }
|
197 |
| - |
198 |
| - /// Release the lock, setting the state variable to `newValue`. |
| 221 | + |
| 222 | + /// A utility function that runs the body code only in debug builds, without |
| 223 | + /// emitting compiler warnings. |
199 | 224 | ///
|
200 |
| - /// - Parameter newValue: The value to give to the state variable when we |
201 |
| - /// release the lock. |
202 |
| - public func unlock(withValue newValue: T) { |
203 |
| - self._value = newValue |
204 |
| - self.unlock() |
205 |
| -#if os(Windows) |
206 |
| - WakeAllConditionVariable(self.cond) |
207 |
| -#else |
208 |
| - let err = pthread_cond_broadcast(self.cond) |
209 |
| - precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)") |
210 |
| -#endif |
| 225 | + /// This is currently the only way to do this in Swift: see |
| 226 | + /// https://forums.swift.org/t/support-debug-only-code/11037 for a discussion. |
| 227 | + @inlinable |
| 228 | + internal func debugOnly(_ body: () -> Void) { |
| 229 | + assert( |
| 230 | + { |
| 231 | + body() |
| 232 | + return true |
| 233 | + }() |
| 234 | + ) |
211 | 235 | }
|
212 | 236 | }
|
213 | 237 |
|
|
0 commit comments