A decade or so ago, "what lock should I use?" was a fairly complicated question on macOS/iOS. We had:
- pthread_mutex_t: 64 bytes, FIFO12, weird API, pretty slow when uncontended
- os_spinlock: 4 bytes, unfair, super fast when uncontended, burns power and CPU when contended
- dispatch_queue_t + dispatch_sync: 80ish bytes, FIFO, a bit faster than pthread_mutex_t, bonus feature if you want to do async stuff
- @synchronized(…): slow, heavy, FIFO, always recursive (not actually a good thing), exception-safe (less useful than it sounds)
- NSLock: a pthread_mutex_t with extra overhead
- dispatch_semaphore_t misused as a lock: mid-sized, unfair, pretty fast when uncontended, actually surprisingly appealing
Then two important things happened. One was that we started relying WAY more on thread priorities, and all the locks that can't donate priority (spinlocks, semaphores) suddenly became essentially unusable. The other was we introduced a new kind of lock:
✨os_unfair_lock✨
4 bytes, unfair, super fast uncontended, efficient when contended, donates priority, enforces correct use via assertions. I've had so many conversations like this:
“hey what lock should I--”
“unfair lock”
“don’t you need more info about the context?”
“honestly not really”
(The one glaring downside is that due to language issues in Swift it's less efficient and requires more shenanigans to use than it does in C/ObjC/C++, but OSAllocatedUnfairLock mostly fixes this in recent systems if you can't use the built-in concurrency constructs)
UPDATE FROM THE FUTURE: The type introduced in This Swift Evolution proposal fixes3 the remaining issues here, getting performance parity with using os_unfair_lock from C/ObjC. It's also cross platform :)
-
FIFO sounds like an appealing property in a lock, "sure, the first thread to wait should be the first that gets to go", but in practice it's rarely actually useful, and the performance cost under contention can be huge due to requiring repeatedly switching threads and letting lower priority threads go before higher priority ones.
-
pthread mutexes on Darwin (and things that use them) were FIFO in the early 2010s when the dilemma being described was current, but are not in more recent OS versions
-
well, will fix, once it's accepted
