Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active June 8, 2024 05:43
Show Gist options
  • Save rnapier/f030f922a7c6f3dc4300d8ff8fc23033 to your computer and use it in GitHub Desktop.
Save rnapier/f030f922a7c6f3dc4300d8ff8fc23033 to your computer and use it in GitHub Desktop.
Mutex backport
// Version of Swift 6 Mutex, with minimal API (just withLock), portable to at least iOS 15, probably much earlier.
// I cannot yet promise it's actually correct.
@frozen
public struct Mutex<Value> {
private let buffer: ManagedBuffer<Value, os_unfair_lock>
public init(_ initialValue: Value) {
buffer = ManagedBuffer<Value, os_unfair_lock>.create(minimumCapacity: 1) { _ in initialValue }
buffer.withUnsafeMutablePointerToElements { lockPtr in
lockPtr.initialize(to: os_unfair_lock())
}
}
public func withLock<Result: Sendable>(
_ body: @Sendable (inout Value) throws -> Result
) rethrows -> Result {
try buffer.withUnsafeMutablePointers { value, lock in
os_unfair_lock_lock(lock)
defer { os_unfair_lock_unlock(lock) }
return try body(&value.pointee)
}
}
}
extension Mutex: @unchecked Sendable where Value: Sendable {}
// Based on Jared Sinclair's test: https://github.com/jaredsinclair/etcetera/blob/master/Tests/EtceteraTests/LockTests.swift
final class MutexTests: XCTestCase {
func testLock() {
let lock = Mutex(0)
let dispatchBlockCount = 16
let iterationCountPerBlock = 100_000
let queues = [
DispatchQueue.global(qos: .userInteractive),
DispatchQueue.global(qos: .default),
DispatchQueue.global(qos: .utility),
]
let group = DispatchGroup()
for block in 0..<dispatchBlockCount {
group.enter()
let queue = queues[block % queues.count]
queue.async {
for _ in 0..<iterationCountPerBlock {
lock.withLock { value in
value = value + 2
value = value - 1
}
}
group.leave()
}
}
_ = group.wait(timeout: DispatchTime.distantFuture)
let finalValue = lock.withLock { $0 }
XCTAssertEqual(finalValue, dispatchBlockCount * iterationCountPerBlock)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment