@@ -84,7 +84,7 @@ public final class Semaphore {
84
84
///
85
85
/// It is recursive in order to handle cancellation (see the implementation
86
86
/// of ``waitUnlessCancelled()``).
87
- private let lock = NSRecursiveLock ( )
87
+ private let _lock = NSRecursiveLock ( )
88
88
89
89
// MARK: - Creating a Semaphore
90
90
@@ -101,6 +101,26 @@ public final class Semaphore {
101
101
precondition ( suspensions. isEmpty, " Semaphore is deallocated while some task(s) are suspended waiting for a signal. " )
102
102
}
103
103
104
+ // MARK: - Locking
105
+ //
106
+ // Swift concurrency is... unfinished. We really need to protect our inner
107
+ // state (`value` and `suspension`) across the calls to
108
+ // `withUnsafeContinuation`. Unfortunately, this method introduces a
109
+ // suspension point. So we need a lock. But the Swift compiler is never out
110
+ // of funny jokes:
111
+ //
112
+ // > Instance method 'lock' is unavailable from asynchronous contexts;
113
+ // > Use async-safe scoped locking instead; this is an error in Swift 6
114
+ //
115
+ // This is very immature, because we don't quite have any other solution.
116
+ // Maybe the authors of Swift concurrency will find it interesting
117
+ // eventually to provide 1. building blocks that 2. solve known problems
118
+ // and 3. back deploy. So far they're busy polishing something else.
119
+ //
120
+ // So let's just hide the lock and mute this stupid warning:
121
+ func lock( ) { _lock. lock ( ) }
122
+ func unlock( ) { _lock. unlock ( ) }
123
+
104
124
// MARK: - Waiting for the Semaphore
105
125
106
126
/// Waits for, or decrements, a semaphore.
@@ -109,11 +129,11 @@ public final class Semaphore {
109
129
/// zero, this function suspends the current task until a signal occurs,
110
130
/// without blocking the underlying thread. Otherwise, no suspension happens.
111
131
public func wait( ) async {
112
- lock. lock ( )
132
+ lock ( )
113
133
114
134
value -= 1
115
135
if value >= 0 {
116
- lock . unlock ( )
136
+ unlock ( )
117
137
return
118
138
}
119
139
@@ -124,7 +144,7 @@ public final class Semaphore {
124
144
// This is not intended to be a strong fifo guarantee, but just
125
145
// an attempt at some fairness.
126
146
suspensions. insert ( Suspension ( continuation: continuation) , at: 0 )
127
- lock . unlock ( )
147
+ unlock ( )
128
148
}
129
149
}
130
150
@@ -137,11 +157,11 @@ public final class Semaphore {
137
157
/// - Throws: If the task is canceled before a signal occurs, this function
138
158
/// throws `CancellationError`.
139
159
public func waitUnlessCancelled( ) async throws {
140
- lock. lock ( )
160
+ lock ( )
141
161
142
162
value -= 1
143
163
if value >= 0 {
144
- lock . unlock ( )
164
+ unlock ( )
145
165
// All code paths check for cancellation
146
166
try Task . checkCancellation ( )
147
167
return
@@ -156,7 +176,7 @@ public final class Semaphore {
156
176
if case . cancelled = suspension. state {
157
177
// Current task was already cancelled when withTaskCancellationHandler
158
178
// was invoked.
159
- lock . unlock ( )
179
+ unlock ( )
160
180
continuation. resume ( throwing: CancellationError ( ) )
161
181
} else {
162
182
// Current task was not cancelled: register the continuation
@@ -167,17 +187,17 @@ public final class Semaphore {
167
187
// an attempt at some fairness.
168
188
suspension. state = . suspendedUnlessCancelled( continuation)
169
189
suspensions. insert ( suspension, at: 0 )
170
- lock . unlock ( )
190
+ unlock ( )
171
191
}
172
192
}
173
193
} onCancel: {
174
194
// withTaskCancellationHandler may immediately call this block (if
175
195
// the current task is cancelled), or call it later (if the task is
176
196
// cancelled later). In the first case, we're still holding the lock,
177
197
// waiting for the continuation. In the second case, we do not hold
178
- // the lock. This is the reason why we use a recursive lock.
179
- lock. lock ( )
180
- defer { lock . unlock ( ) }
198
+ // the This is the reason why we use a recursive lock.
199
+ lock ( )
200
+ defer { unlock ( ) }
181
201
182
202
// We're no longer waiting for a signal
183
203
value += 1
@@ -207,8 +227,8 @@ public final class Semaphore {
207
227
/// Otherwise, false is returned.
208
228
@discardableResult
209
229
public func signal( ) -> Bool {
210
- lock. lock ( )
211
- defer { lock . unlock ( ) }
230
+ lock ( )
231
+ defer { unlock ( ) }
212
232
213
233
value += 1
214
234
0 commit comments