Skip to content

Commit 78aa633

Browse files
committed
Fix a naive compiler warning on stdlib 5.7
1 parent 62ea95a commit 78aa633

File tree

1 file changed

+33
-13
lines changed

1 file changed

+33
-13
lines changed

Sources/Semaphore/Semaphore.swift

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public final class Semaphore {
8484
///
8585
/// It is recursive in order to handle cancellation (see the implementation
8686
/// of ``waitUnlessCancelled()``).
87-
private let lock = NSRecursiveLock()
87+
private let _lock = NSRecursiveLock()
8888

8989
// MARK: - Creating a Semaphore
9090

@@ -101,6 +101,26 @@ public final class Semaphore {
101101
precondition(suspensions.isEmpty, "Semaphore is deallocated while some task(s) are suspended waiting for a signal.")
102102
}
103103

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+
104124
// MARK: - Waiting for the Semaphore
105125

106126
/// Waits for, or decrements, a semaphore.
@@ -109,11 +129,11 @@ public final class Semaphore {
109129
/// zero, this function suspends the current task until a signal occurs,
110130
/// without blocking the underlying thread. Otherwise, no suspension happens.
111131
public func wait() async {
112-
lock.lock()
132+
lock()
113133

114134
value -= 1
115135
if value >= 0 {
116-
lock.unlock()
136+
unlock()
117137
return
118138
}
119139

@@ -124,7 +144,7 @@ public final class Semaphore {
124144
// This is not intended to be a strong fifo guarantee, but just
125145
// an attempt at some fairness.
126146
suspensions.insert(Suspension(continuation: continuation), at: 0)
127-
lock.unlock()
147+
unlock()
128148
}
129149
}
130150

@@ -137,11 +157,11 @@ public final class Semaphore {
137157
/// - Throws: If the task is canceled before a signal occurs, this function
138158
/// throws `CancellationError`.
139159
public func waitUnlessCancelled() async throws {
140-
lock.lock()
160+
lock()
141161

142162
value -= 1
143163
if value >= 0 {
144-
lock.unlock()
164+
unlock()
145165
// All code paths check for cancellation
146166
try Task.checkCancellation()
147167
return
@@ -156,7 +176,7 @@ public final class Semaphore {
156176
if case .cancelled = suspension.state {
157177
// Current task was already cancelled when withTaskCancellationHandler
158178
// was invoked.
159-
lock.unlock()
179+
unlock()
160180
continuation.resume(throwing: CancellationError())
161181
} else {
162182
// Current task was not cancelled: register the continuation
@@ -167,17 +187,17 @@ public final class Semaphore {
167187
// an attempt at some fairness.
168188
suspension.state = .suspendedUnlessCancelled(continuation)
169189
suspensions.insert(suspension, at: 0)
170-
lock.unlock()
190+
unlock()
171191
}
172192
}
173193
} onCancel: {
174194
// withTaskCancellationHandler may immediately call this block (if
175195
// the current task is cancelled), or call it later (if the task is
176196
// cancelled later). In the first case, we're still holding the lock,
177197
// 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() }
181201

182202
// We're no longer waiting for a signal
183203
value += 1
@@ -207,8 +227,8 @@ public final class Semaphore {
207227
/// Otherwise, false is returned.
208228
@discardableResult
209229
public func signal() -> Bool {
210-
lock.lock()
211-
defer { lock.unlock() }
230+
lock()
231+
defer { unlock() }
212232

213233
value += 1
214234

0 commit comments

Comments
 (0)