Last active
January 8, 2022 15:17
-
-
Save bradleymackey/bdb2f5e91d568c2dc63430ae8a2336a0 to your computer and use it in GitHub Desktop.
Locking abstraction over any type.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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