Skip to content

Instantly share code, notes, and snippets.

@adam-zethraeus
Created March 16, 2023 17:41
Show Gist options
  • Save adam-zethraeus/52f815a820d9129aa56e72e69271255c to your computer and use it in GitHub Desktop.
Save adam-zethraeus/52f815a820d9129aa56e72e69271255c to your computer and use it in GitHub Desktop.
public struct Locked<T>: LockedValue {
public init(_ value: T) {
let lock = Self.make(for: value)
withLock = { act in
lock.lock()
defer { lock.unlock() }
return act(&lock.unsafe_wrapped)
}
}
private let withLock: ((inout T) -> Any) -> Any
public func take<aT>(_ act: (inout T) -> aT) -> aT {
withLock(act) as! aT
}
}
extension Locked {
@inline(__always)
fileprivate static func make(for value: T) -> some LockType<T> {
#if canImport(os)
if #available(iOS 16, macOS 13, *) {
return OSUnfairLocked(value)
} else {
return NSLocked(value)
}
#elseif canImport(Foundation)
return NSLocked(value)
#else
return PThreadLock(value)
#endif
}
}
private protocol LockedValue<T> {
associatedtype T
@discardableResult
init(_:T)
func take<aT>(_: (inout T) -> aT) -> aT
}
private protocol LockType<T>: AnyObject {
associatedtype T
@inline(__always)
func lock()
@inline(__always)
func unlock()
var unsafe_wrapped: T { get set }
}
#if canImport(Foundation)
import Foundation
private final class NSLocked<T>: LockType {
fileprivate var unsafe_wrapped: T
private let nslock = NSLock()
fileprivate init(_ value: T) {
self.unsafe_wrapped = value
}
@inline(__always)
fileprivate func lock() {
nslock.lock()
}
@inline(__always)
fileprivate func unlock() {
nslock.unlock()
}
}
#endif
#if canImport(os)
import os
@available(iOS 16, macOS 13, *)
private final class OSUnfairLocked<T>: LockType {
private let oslock: OSAllocatedUnfairLock<()>
fileprivate var unsafe_wrapped: T
fileprivate init(_ value: T) {
oslock = .init(initialState: ())
unsafe_wrapped = value
}
@inline(__always)
fileprivate func lock() {
oslock.lock()
}
@inline(__always)
fileprivate func unlock() {
oslock.unlock()
}
}
#endif
private final class PThreadLock<T>: LockType {
private var mutex = pthread_mutex_t()
fileprivate var unsafe_wrapped: T
fileprivate init(_ value: T) {
unsafe_wrapped = value
}
@inline(__always)
fileprivate func lock() {
pthread_mutex_lock(&mutex)
}
@inline(__always)
fileprivate func unlock() {
pthread_mutex_unlock(&mutex)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment