Avoid Locks
ID |
swift.avoid_locks |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Best Practice |
Language |
Swift |
Tags |
best-practice, concurrency, modernization |
Description
Reports use of legacy lock primitives — NSLock, NSRecursiveLock,
NSConditionLock, pthread_mutex_t, pthread_rwlock_t,
os_unfair_lock, OSSpinLock — and their C-style lock/unlock helper
functions.
let lock = NSLock() // FLAW
var m = pthread_mutex_t() // FLAW
os_unfair_lock_lock(&u) // FLAW
actor Counter { var value = 0 } // OK — actor isolates state
Rationale
Hand-rolled locking with NSLock, pthread_mutex_t or os_unfair_lock
is error-prone: forgetting to unlock on every exit path leaks the lock,
deadlocks survive process restarts, and priority inversion with
OSSpinLock is a known footgun (Apple has officially deprecated it).
Swift 5.5+ introduces structured concurrency: actor types serialize
access to mutable state by construction, and Swift 6’s Mutex value
type makes critical sections explicit while preventing reentrancy and
forgotten unlocks at compile time.
Remediation
Pick the highest-level primitive that fits the access pattern:
// Isolated state behind an actor
actor Counter {
private var value = 0
func increment() { value += 1 }
}
// Critical section behind a Mutex value (Swift 6+)
import Synchronization
let counter = Mutex(0)
counter.withLock { $0 += 1 }
For interop with C APIs that genuinely require a pthread_mutex_t,
wrap the lock in a small Swift type that owns lock/unlock symmetrically
(e.g. via defer) and confine the unsafe call site to that wrapper.