Semaphore alternatives for structured concurrency

I'm trying to onboard with Swift structured concurrency by writing a simple actor to fetch URL responses from a rate-limited web service. I thought that this is something that DispatchSemaphore might be useful for but reading up async-await, I see that DispatchSemaphore should not be used there because of a number of issues (deadlock, etc). Is there some better way of thinking about this problem in Swift Structured Concurrency or is this something that is better done with a DispatchQueue/OperationQueue? I've read Incremental migration to Structured Concurrency which touches on similar issues but didn't come away from that with an actual solution.

I don't have time to type up a full post rn, but see swift-async-algorithms/Throttle.md at main · apple/swift-async-algorithms · GitHub

Alternatively you could also write your own async-await friendlier semaphore (though this needs to be modified if you want to support cancellation scenarios etc.)

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()
        }
    }
}
2 Likes

Thank you for the semaphore example.

I am already using it (replaced release by signal ) in a process pipeline and my channel actor no longer deadlocks! :joy:

Next, I am going to use it in a fan-in fan-out configuration to see how it goes.

Oddly enough, I am working on such synchronization scenarios between multiple task contexts in this repo.

While ago, I implemented an async semaphore with cancellation scenarios support, you can look into it for more details. The documentation for this I am already hosting on GitHub pages that should be helpful as well. To get some sample usage ideas you can look into the tests for this as well.

1 Like