I've recently rewatched the Swift concurrency: Behind the scenes - WWDC21 - Videos - Apple Developer video and came back across the section on which synchronization primitives are safe and unsafe to use in Swift Concurrency.
It states that (apart from async constructs like actors) os_unfair_lock
and NSLock
- and presumably by extension pthread_mutex_t
which underpins NSLock
- are safe to use in synchronous code but require caution.
On the other hand it specifically calls out semaphores, conditions (pthread_cond
, NSCondition
), and read/write locks (pthread_rwlock_t
) as unsafe primitives to use, but doesn't provide a satisfying explanations as to why these primitives are unsafe to use. Specifically the video states:
... primitives like semaphores and condition variables are unsafe to use with Swift concurrency. This is because they hide dependency information from the Swift runtime, but introduce a dependency in execution in your code. Since the runtime is unaware of this dependency, it cannot make the right scheduling decisions and resolve them.
Can someone provide additional context as to what exactly this means practically? Is code that uses these primitives as risk of hanging or deadlocking? Is it an issue of priority inversion like alluded to here? Could it lead to data corruption or crashes?
Mainly I'm just trying to understand the underlying (low-level) rationale as to why these primitives are unsafe and the consequences of their use in asynchronous code (assuming that locks aren't held across await boundaries) or synchronous code invoked by asynchronous code.