axiom-synchronization

Mutex & Synchronization — Thread-Safe Primitives

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "axiom-synchronization" with this command: npx skills add charleswiltgen/axiom/charleswiltgen-axiom-axiom-synchronization

Mutex & Synchronization — Thread-Safe Primitives

Low-level synchronization primitives for when actors are too slow or heavyweight.

When to Use Mutex vs Actor

Need Use Reason

Microsecond operations Mutex No async hop overhead

Protect single property Mutex Simpler, faster

Complex async workflows Actor Proper suspension handling

Suspension points needed Actor Mutex can't suspend

Shared across modules Mutex Sendable, no await needed

High-frequency counters Atomic Lock-free performance

API Reference

Mutex (iOS 18+ / Swift 6)

import Synchronization

let mutex = Mutex<Int>(0)

// Read let value = mutex.withLock { $0 }

// Write mutex.withLock { $0 += 1 }

// Non-blocking attempt if let value = mutex.withLockIfAvailable({ $0 }) { // Got the lock }

Properties:

  • Generic over protected value

  • Sendable — safe to share across concurrency boundaries

  • Closure-based access only (no lock/unlock methods)

OSAllocatedUnfairLock (iOS 16+)

import os

let lock = OSAllocatedUnfairLock(initialState: 0)

// Closure-based (recommended) lock.withLock { state in state += 1 }

// Traditional (same-thread only) lock.lock() defer { lock.unlock() } // access protected state

Properties:

  • Heap-allocated, stable memory address

  • Non-recursive (can't re-lock from same thread)

  • Sendable

Atomic Types (iOS 18+)

import Synchronization

let counter = Atomic<Int>(0)

// Atomic increment counter.wrappingAdd(1, ordering: .relaxed)

// Compare-and-swap let (exchanged, original) = counter.compareExchange( expected: 0, desired: 42, ordering: .acquiringAndReleasing )

Patterns

Pattern 1: Thread-Safe Counter

final class Counter: Sendable { private let mutex = Mutex<Int>(0)

var value: Int { mutex.withLock { $0 } }
func increment() { mutex.withLock { $0 += 1 } }

}

Pattern 2: Sendable Wrapper

final class ThreadSafeValue<T: Sendable>: @unchecked Sendable { private let mutex: Mutex<T>

init(_ value: T) { mutex = Mutex(value) }

var value: T {
    get { mutex.withLock { $0 } }
    set { mutex.withLock { $0 = newValue } }
}

}

Pattern 3: Fast Sync Access in Actor

actor ImageCache { // Mutex for fast sync reads without actor hop private let mutex = Mutex<[URL: Data]>([:])

nonisolated func cachedSync(_ url: URL) -> Data? {
    mutex.withLock { $0[url] }
}

func cacheAsync(_ url: URL, data: Data) {
    mutex.withLock { $0[url] = data }
}

}

Pattern 4: Lock-Free Counter with Atomic

final class FastCounter: Sendable { private let _value = Atomic<Int>(0)

var value: Int { _value.load(ordering: .relaxed) }

func increment() {
    _value.wrappingAdd(1, ordering: .relaxed)
}

}

Pattern 5: iOS 16 Fallback

#if compiler(>=6.0) import Synchronization typealias Lock<T> = Mutex<T> #else import os // Use OSAllocatedUnfairLock for iOS 16-17 #endif

Danger: Mixing with Swift Concurrency

Never Hold Locks Across Await

// ❌ DEADLOCK RISK mutex.withLock { await someAsyncWork() // Task suspends while holding lock! }

// ✅ SAFE: Release before await let value = mutex.withLock { $0 } let result = await process(value) mutex.withLock { $0 = result }

Why Semaphores/RWLocks Are Unsafe

Swift's cooperative thread pool has limited threads. Blocking primitives exhaust the pool:

// ❌ DANGEROUS: Blocks cooperative thread let semaphore = DispatchSemaphore(value: 0) Task { semaphore.wait() // Thread blocked, can't run other tasks! }

// ✅ Use async continuation instead await withCheckedContinuation { continuation in // Non-blocking callback callback { continuation.resume() } }

os_unfair_lock Danger

Never use os_unfair_lock directly in Swift — it can be moved in memory:

// ❌ UNDEFINED BEHAVIOR: Lock may move var lock = os_unfair_lock() os_unfair_lock_lock(&lock) // Address may be invalid

// ✅ Use OSAllocatedUnfairLock (heap-allocated, stable address) let lock = OSAllocatedUnfairLock()

Decision Tree

Need synchronization? ├─ Lock-free operation needed? │ └─ Simple counter/flag? → Atomic │ └─ Complex state? → Mutex ├─ iOS 18+ available? │ └─ Yes → Mutex │ └─ No, iOS 16+? → OSAllocatedUnfairLock ├─ Need suspension points? │ └─ Yes → Actor (not lock) ├─ Cross-await access? │ └─ Yes → Actor (not lock) └─ Performance-critical hot path? └─ Yes → Mutex/Atomic (not actor)

Common Mistakes

Mistake 1: Using Lock for Async Coordination

// ❌ Locks don't work with async let mutex = Mutex<Bool>(false) Task { await someWork() mutex.withLock { $0 = true } // Race condition still possible }

// ✅ Use actor or async state actor AsyncState { var isComplete = false func complete() { isComplete = true } }

Mistake 2: Recursive Locking Attempt

// ❌ Deadlock — OSAllocatedUnfairLock is non-recursive lock.withLock { doWork() // If doWork() also calls withLock → deadlock }

// ✅ Refactor to avoid nested locking let data = lock.withLock { $0.copy() } doWork(with: data)

Mistake 3: Mixing Lock Styles

// ❌ Don't mix lock/unlock with withLock lock.lock() lock.withLock { /* ... */ } // Deadlock! lock.unlock()

// ✅ Pick one style lock.withLock { /* all work here */ }

Memory Ordering Quick Reference

Ordering Read Write Use Case

.relaxed

Yes Yes Counters, no dependencies

.acquiring

Yes

Load before dependent ops

.releasing

Yes Store after dependent ops

.acquiringAndReleasing

Yes Yes Read-modify-write

.sequentiallyConsistent

Yes Yes Strongest guarantee

Default choice: .relaxed for counters, .acquiringAndReleasing for read-modify-write.

Resources

Docs: /synchronization, /synchronization/mutex, /os/osallocatedunfairlock

Swift Evolution: SE-0433

Skills: axiom-swift-concurrency, axiom-swift-performance

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

axiom-vision

No summary provided by upstream source.

Repository SourceNeeds Review
General

axiom-swiftdata

No summary provided by upstream source.

Repository SourceNeeds Review
General

axiom-swiftui-26-ref

No summary provided by upstream source.

Repository SourceNeeds Review
General

axiom-swiftui-architecture

No summary provided by upstream source.

Repository SourceNeeds Review