Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/groue/Semaphore
A Synchronization Primitive for Swift Concurrency
https://github.com/groue/Semaphore
async await concurrency counting-semaphore semaphore
Last synced: 19 days ago
JSON representation
A Synchronization Primitive for Swift Concurrency
- Host: GitHub
- URL: https://github.com/groue/Semaphore
- Owner: groue
- License: mit
- Created: 2022-10-01T10:04:09.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-07-28T10:12:54.000Z (4 months ago)
- Last Synced: 2024-07-28T11:45:06.133Z (4 months ago)
- Topics: async, await, concurrency, counting-semaphore, semaphore
- Language: Swift
- Homepage:
- Size: 42 KB
- Stars: 483
- Watchers: 12
- Forks: 15
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-ios - Semaphore
README
# Semaphore
**A Synchronization Primitive for Swift Concurrency**
**Requirements**: iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+ • Swift 5.10+ / Xcode 15.3+
📖 **[Documentation](https://swiftpackageindex.com/groue/Semaphore/documentation)**
---
This package provides `AsyncSemaphore`, a [traditional counting semaphore](https://en.wikipedia.org/wiki/Semaphore_(programming)).
Unlike [`DispatchSemaphore`], it does not block any thread. Instead, Swift concurrency tasks are suspended "awaiting" for the semaphore.
### Usage
You can use a semaphore to suspend a task and resume it later:
```swift
let semaphore = AsyncSemaphore(value: 0)Task {
// Suspends the task until a signal occurs.
await semaphore.wait()
await doSomething()
}// Resumes the suspended task.
semaphore.signal()
```An actor can use a semaphore so that its methods can't run concurrently, avoiding the "actor reentrancy problem":
```swift
actor MyActor {
private let semaphore = AsyncSemaphore(value: 1)
func serializedMethod() async {
// Makes sure no two tasks can execute
// serializedMethod() concurrently.
await semaphore.wait()
defer { semaphore.signal() }
await doSomething()
await doSomethingElse()
}
}
```A semaphore can generally limit the number of concurrent accesses to a resource:
```swift
class Downloader {
private let semaphore: AsyncSemaphore/// Creates a Downloader that can run at most
/// `maxDownloadCount` concurrent downloads.
init(maxDownloadCount: Int) {
semaphore = AsyncSemaphore(value: maxDownloadCount)
}func download(...) async throws -> Data {
try await semaphore.waitUnlessCancelled()
defer { semaphore.signal() }
return try await ...
}
}
```You can see in the latest example that the `wait()` method has a `waitUnlessCancelled` variant that throws `CancellationError` if the task is cancelled before a signal occurs.
For a nice introduction to semaphores, see [The Beauty of Semaphores in Swift 🚦](https://medium.com/@roykronenfeld/semaphores-in-swift-e296ea80f860). The article discusses [`DispatchSemaphore`], but it can easily be ported to Swift concurrency: get inspiration from the above examples.
[`DispatchSemaphore`]: https://developer.apple.com/documentation/dispatch/dispatchsemaphore