Thread safety with Mutex in Swift 6

Noah Little on 2024-09-07

Thread safety with Mutex in Swift 6

With Xcode 16/Swift 6, Apple introduced the Synchronization framework, bringing to the table more ways to ensure thread safety in our software.

Today we will be exploring the Mutex struct, seeing how it can be used to prevent crashes and undefined behaviour due to simultaneous reads & writes to shared mutable data. Will also discuss how a similar type can be implemented to support older iOS versions.

Firstly, let’s look at some unsafe code which can crash the app:

Here we have a singleton that can be accessed by anywhere in the app to read & write to a cache. This code is unsafe though, since it doesn’t use any thread safety mechanisms (i.e. queues, locks, actors). We can get the program to crash reliably with DispatchQueue.concurrentPerform like this:

Prior to Swift 6, you’d probably use an actor instead of class which is an easy way to ensure thread safety. However, making the jump to Swift Concurrency in legacy codebases can be difficult, so you’d probably use a queue (and conform the class to @unchecked Sendable if you’re compiling under strict concurrency).

Now let’s see how we can use the new Mutex instead:

Now the class can be marked as just Sendable instead of @unchecked Sendable since all sendability checks are passing now (class must be final, must not contain mutable instance variables and immutable instance variables must be Sendable).

Mutex is a really nice way to ensure thread safety with a simple API, but there’s one problem, it only supports iOS 18, so let’s build something similar to Mutex which supports all iOS versions.

We can make a simple wrapper around the low level C type pthread_mutex_t to achieve the backwards compatible version that can be used in a similar way to Apple’sMutex:

Thanks for reading :)