OSAllocatedUnfairLock — A Lock That Owns Its State

|

OSAllocatedUnfairLock is a low-level mutex added in iOS 16 / macOS 13. Unlike NSLock, it’s a struct — but you can copy it freely because the actual lock storage is heap-allocated (hence “allocated”). The struct is just a handle.

The defining design choice: the protected value lives inside the lock, not alongside it. You get it only by going through withLock:

let counter = OSAllocatedUnfairLock(initialState: 0)

counter.withLock { value in
    value += 1
}

let snapshot = counter.withLock { $0 }

Compare that to NSLock, where nothing stops you from reading the value without locking:

var count = 0
let lock = NSLock()

lock.lock()
count += 1
lock.unlock()

print(count) // compiles fine — lock is bypassed entirely

“Unfair” means threads don’t get the lock in arrival order. The OS picks freely, which avoids the overhead of maintaining a queue. In practice this gives it much lower latency than NSLock for uncontended cases.

It’s non-recursive. Calling withLock again from inside withLock on the same lock is a deadlock, not a re-entry.

Sendable conformance

OSAllocatedUnfairLock is Sendable when State: Sendable. Because the state is only reachable through withLock, the compiler can verify that sharing the lock across concurrency boundaries is safe — no @unchecked Sendable needed.

This makes it a clean fit for Swift 6 strict concurrency. A class that holds mutable state and an NSLock forces you to suppress the warning yourself:

// Swift 6 — compiler complains
class Counter: Sendable {
    private var value = 0        // ⚠️ stored property 'value' of 'Sendable'-conforming class is mutable
    private let lock = NSLock()
}

With OSAllocatedUnfairLock the state is inside the lock, so the compiler is satisfied without any suppression:

final class Counter: Sendable {
    private let lock = OSAllocatedUnfairLock(initialState: 0) // ✅

    func increment() { lock.withLock { $0 += 1 } }
    func value() -> Int { lock.withLock { $0 } }
}

Takeaway: reach for OSAllocatedUnfairLock when you need a fast, struct-friendly mutex and want the compiler to make it hard to access shared state without holding the lock.

← Back to DevLog
rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora