Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Locking abstraction over any type.
import os
/// An efficient lock to prevent contesting access to a resource across threads
///
/// This is a very thin wrapper around `os_unfair_lock` with a better Swift interface.
/// It also has a similar interface to `NSLock`
public final class Lock: @unchecked Sendable {
@usableFromInline
var _mutex = os_unfair_lock()
public init() { }
}
extension Lock {
/// Locks the `Lock`. Blocks if it is already locked.
@inlinable
public func lock() {
os_unfair_lock_lock(&_mutex)
}
/// Unlocks the `Lock`.
@inlinable
public func unlock() {
os_unfair_lock_unlock(&_mutex)
}
/// Locks the `Lock` if it is not already locked.
///
/// It is invalid to call this in a retry loop.
/// The program must be able to proceed without having aquired the lock.
@inlinable
public func `try`() -> Bool {
os_unfair_lock_trylock(&_mutex)
}
}
/// An atomic wrapper for any type, including primitive value types.
/// use this to ensure accesses and modifications are thread safe.
///
/// - note: This is not safe to be used as a property wrapper until
/// "modify accessors" are stabilised in Swift. This is because of implicit
/// copy-on-write within getters/setters that can not work for collections.
public final class Atomic<Value>: @unchecked Sendable {
@usableFromInline
var _storage: Value
@usableFromInline
let lock = Lock()
@usableFromInline
func perform<Result>(block: () throws -> Result) rethrows -> Result {
lock.lock()
defer { lock.unlock() }
return try block()
}
@inlinable
public init(initialValue value: Value) {
self._storage = value
}
@inlinable
public func get<T>(_ block: (Value) throws -> T) rethrows -> T {
try perform {
try block(_storage)
}
}
@discardableResult
@inlinable
public func modify(_ block: (inout Value) throws -> T) rethrows -> T {
try perform {
try block(&_storage)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment