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.