Skillshub axiom-synchronization
Use when needing thread-safe primitives for performance-critical code. Covers Mutex (iOS 18+), OSAllocatedUnfairLock (iOS 16+), Atomic types, when to use locks vs actors, deadlock prevention with Swift Concurrency.
install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/CharlesWiltgen/Axiom/axiom-synchronization" ~/.claude/skills/comeonoliver-skillshub-axiom-synchronization && rm -rf "$T"
manifest:
skills/CharlesWiltgen/Axiom/axiom-synchronization/SKILL.mdsource content
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
— safe to share across concurrency boundariesSendable- 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
directly in Swift — it can be moved in memory:os_unfair_lock
// ❌ 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 |
|---|---|---|---|
| Yes | Yes | Counters, no dependencies |
| Yes | - | Load before dependent ops |
| - | Yes | Store after dependent ops |
| Yes | Yes | Read-modify-write |
| 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