I might be wrong now, but using a DispatchSemaphore
in an actor
has proved to be problematic in the past when I was trying to do something similar.
I think the work around was to use continuations
instead. (When I find the example code, I will add it here.)
@John_McCall, could you shed some light on this to educate us please?
Code Using Continuations
// Channel.swift
extension V3 {
actor Channel<T> {
private var store = [T?] ()
private var waiters = [UnsafeContinuation <T?, Never>] ()
func put (_ u: T?) {
store.append(u)
if ! waiters.isEmpty {
waiters.removeFirst().resume (returning: store.removeFirst ())
}
}
func get () async -> T? {
if store.isEmpty {
return await withUnsafeContinuation {
waiters.append ($0)
}
}
else {
let value = store.removeFirst()
return value
}
}
var state: (packets: Int, waiters: Int) {
return (store.count, waiters.count)
}
var description: String {
return "\(state)"
}
}
}
Original Example
actor Semaphore {
private var count: Int
private var waiters: [CheckedContinuation<Void, Never>] = []
init(count: Int = 0) {
self.count = count
}
func wait() async {
count -= 1
if count >= 0 { return }
await withCheckedContinuation {
waiters.append($0)
}
}
func release(count: Int = 1) {
assert(count >= 1)
self.count += count
for _ in 0..<count {
if waiters.isEmpty { return }
waiters.removeFirst().resume()
}
}
}