A lock for Swift concurrency
This package exposes two types: AsyncLock
and AsyncRecursiveLock
. These allow you to define asynchronous critical sections. Only one task can enter a critical section at a time. Unlike a traditional lock, you can safely make async calls while these locks are held.
This is a handy tool for dealing with actor reentrancy.
Some other concurrency packages you might find useful are Queue and Semaphore.
Swift Package Manager:
dependencies: [
.package(url: "https://github.com/mattmassicotte/Lock", branch: "main")
]
These locks are non-Sendable. This is an intentional choice to disallow sharing the lock across isolation domains. If you want to do something like that, first think really hard about why and then check out Semaphore.
Note that trying to acquire an already-locked AsyncLock
will deadlock your actor.
import Lock
actor MyActor {
var value = 42
let lock = AsyncLock()
let recursiveLock = AsyncRecursiveLock()
func hasCriticalSections() async {
// no matter how many tasks call this method,
// only one will be able to execute at a time
await lock.lock()
self.value = await otherObject.getValue()
lock.unlock()
}
func hasCriticalSectionsBlock() async {
await recursiveLock.withLock {
// acquiring this multiple times within the same task is safe
await recursiveLock.withLock {
self.value = await otherObject.getValue()
}
}
}
}
Unfortunately, I haven't quite figured out how to make AsyncRecursiveLock
right yet, so it's currently not public.
I would love to hear from you! Issues or pull requests work great. Both a Matrix space and Discord are also available for live help, but I have a strong bias towards answering in the form of documentation.
I prefer collaboration, and would love to find ways to work together if you have a similar project.
I prefer indentation with tabs for improved accessibility. But, I'd rather you use the system you want and make a PR than hesitate because of whitespace.
By participating in this project you agree to abide by the Contributor Code of Conduct.