Using UnfairLock with Swift

UnfairLock seems to be causing a lot of confusion for Swift developers. Every once in a while I run across some incorrect implementation that reads like:

var lock = os_unfair_lock_s()
os_unfair_lock_lock(&lock)

// code here ...

os_unfair_lock_unlock(&lock)

The confusion seems to arise from the fact that & is used to take address of a variable in C while it is used for in-out operation in Swift, which seem similar but are not. To make os_unfair_lock_lock work the lock location in memory needs to not move, which isn’t guaranteed by Swift. So the above code might or might not work as expected. To understand better we need to take a look at equivalent C code:

os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&lock);

// code here ...

os_unfair_lock_unlock(&lock);

In C API os_unfair_lock is a struct defined as:

typedef struct os_unfair_lock_s { 
    // ...
} os_unfair_lock, *os_unfair_lock_t;

Since Swift does not provide a OS_UNFAIR_LOCK_INIT equivalent, we can not allocated os_unfair_lock in stack memory. Another alternative is to allocate in the heap memory

os_unfair_lock_t lock;
lock = malloc(sizeof(os_unfair_lock));
os_unfair_lock_lock(lock);

// code here ...

os_unfair_lock_unlock(lock);
free(lock);
lock = NULL;

The equivalent in Swift would be:

public typealias os_unfair_lock_t = UnsafeMutablePointer<os_unfair_lock_s>

var lock: os_unfair_lock_t // os_unfair_lock_t lock;
lock = UnsafeMutablePointer<os_unfair_lock_s>.allocate(capacity: 1) // lock = malloc(sizeof(os_unfair_lock));
lock.initialize(to: os_unfair_lock()) // memset(lock, 0, sizeof(os_unfair_lock));
os_unfair_lock_lock(lock)

// code here ...

os_unfair_lock_unlock(lock)
lock.deallocate() // free(lock);

Further Reading