I went through this Question but the provided solution didn't work. Can someone please explain any alternative approach or proper implementation using os_unfair_lock()?
when I am using 'OS_UNFAIR_LOCK_INIT', it seems unavailable.
Thanks!
I went through this Question but the provided solution didn't work. Can someone please explain any alternative approach or proper implementation using os_unfair_lock()?
when I am using 'OS_UNFAIR_LOCK_INIT', it seems unavailable.
Thanks!
You can use os_unfair_lock
as below,
var unfairLock = os_unfair_lock_s()
os_unfair_lock_lock(&unfairLock)
os_unfair_lock_unlock(&unfairLock)
os_unfair_lock_s
from Swift like this. –
Bal var unfairLock
is a file scope variable then it has a stable memory address, correct? –
Roee allocate
/initialize
is still the status quo). The idea of global/statics for locks doesn’t seem practical… –
Bal OSAllocatedUnfairLock
is clearly the new status quo. Lol. –
Bal In iOS 16 (and macOS 13) and later, you should use OSAllocatedUnfairLock
. As the documentation says:
it’s unsafe to use
os_unfair_lock
from Swift because it’s a value type and, therefore, doesn’t have a stable memory address. That means when you callos_unfair_lock_lock
oros_unfair_lock_unlock
and pass a lock object using the&
operator, the system may lock or unlock the wrong object.Instead, use
OSAllocatedUnfairLock
, which avoids that pitfall because it doesn’t function as a value type, despite being a structure. All copied instances of anOSAllocatedUnfairLock
control the same underlying lock allocation.
So, if you have a counter that you want to interact with in a thread-safe manner:
import os.lock
let counter = OSAllocatedUnfairLock(initialState: 0)
...
counter.withLock { value in
value += 1
}
...
counter.withLock { value in
print(value)
}
For support of earlier OS versions, see my original answer below.
In Concurrent Programming With GCD in Swift 3, they warn us that we cannot use os_unfair_lock
directly in Swift because “Swift assumes that anything that is struct
can be moved, and that doesn't work with a mutex or with a lock.”
In that video, the speaker suggests that if you must use os_unfair_lock
, that you put this in an Objective-C class (which won't move the struct
). Or if you look at some of the stdlib code, they show you can stay in Swift, but use a UnsafeMutablePointer
instead of the struct
directly. (Thanks to bscothern for confirming this pattern.)
So, for example, you can write an UnfairLock
class that avoids this problem:
final class UnfairLock: NSLocking {
private let unfairLock: UnsafeMutablePointer<os_unfair_lock> = {
let pointer = UnsafeMutablePointer<os_unfair_lock>.allocate(capacity: 1)
pointer.initialize(to: os_unfair_lock())
return pointer
}()
deinit {
unfairLock.deinitialize(count: 1)
unfairLock.deallocate()
}
func lock() {
os_unfair_lock_lock(unfairLock)
}
func tryLock() -> Bool {
os_unfair_lock_trylock(unfairLock)
}
func unlock() {
os_unfair_lock_unlock(unfairLock)
}
}
Then you can do things like:
let lock = UnfairLock()
And then use lock
and unlock
like you would with NSLock
, but using the more efficient os_unfair_lock
behind the scenes:
lock.lock()
// critical section here
lock.unlock()
And because this UnfairLock
conforms to NSLocking
, you can use extensions designed for that. E.g., there is a very useful Foundation extension withLock
for the NSLocking
protocol. This is a useful way of ensuring that locks and unlocks are properly scoped/balanced, thereby avoiding the brittleness of the above lock
/unlock
pattern. You can do things like:
lock.withLock {
// critical section here
}
That will lock
before the “critical section” runs, and unlock
when complete, ensuring that everything is properly balanced, even if you have some “early exits” inside the critical section.
But, going back to the original question, never use os_unfair_lock
from Swift without something like the above or as contemplated in that video, both of which provide a stable memory address for the lock.
You can use os_unfair_lock
as below,
var unfairLock = os_unfair_lock_s()
os_unfair_lock_lock(&unfairLock)
os_unfair_lock_unlock(&unfairLock)
os_unfair_lock_s
from Swift like this. –
Bal var unfairLock
is a file scope variable then it has a stable memory address, correct? –
Roee allocate
/initialize
is still the status quo). The idea of global/statics for locks doesn’t seem practical… –
Bal OSAllocatedUnfairLock
is clearly the new status quo. Lol. –
Bal © 2022 - 2025 — McMap. All rights reserved.