Atomic Types in Swift

In today modern apps multithreading is a de-facto standard for achieving concurrency. Multithreading comes with its own set of complexities i.e data race, dead lock, live lock etc. Apple provides various technologies for achieving multithreading on its platforms(macOS, iOS, iPadOS, watchOS and tvOS). These technologies include classic pthread, Thread(NSThread), NSOperationQueue and DispatchQueue.

One of the important aspect of multi threading is atomic types. Atomic types are language primitives that provide access to value with guarantee that no data race conditions arise.

Popular programming languages like C++ and Java have well established support for atomic types. C++ has std::atomic as part of standard library and Java has Atomic Variables as part of java.util.concurrent.atomic package.

Unfortunately as of writing there is no language support for atomic types in Swift. To demonstrate how an atomic type can be implemented in Swift we will start with a simple use case

// A protocol to be used in our atomic type
protocol Operatable {
    func add(other: Self) -> Self
}

final class Atomic<T: Operatable> {
    
    private var t: T
    
    init(_ t: T) {
        self.t = t
    }
    
    func incrementAndGet(by other: T) -> T {
        // Critical section starts
        // The change to t should be atomic here
        let result = t.add(other: other)
        t = result
        // Critical section ends
        return result
    }
}

In the above code snippet the implementation of incrementAndGet method is not thread safe. Below is our test code

// We will only use Int for our demo but it can be made to work with any other type
extension Int: Operatable {
    func add(other: Int) -> Int {
        return self + other
    }
}

func test() {
    let atomicInt = Atomic<Int>(0)
    for i in 1...5 {
        let thread = Thread {
            print("\(Thread.current.name!):Value:\(atomicInt.incrementAndGet(by: 1))")
        }
        thread.name = "Thread: \(i)"
        thread.start()
    }
    Thread.sleep(forTimeInterval: 2)
}

Below is one of the possible output of above test code

Thread: 4: critical section started
Thread: 5: critical section started
Thread: 2: critical section started
Thread: 3: critical section started
Thread: 4: critical section ended
Thread: 5: critical section ended
Thread: 2: critical section ended
Thread: 1: critical section started
Thread: 3: critical section ended
Thread: 1: critical section ended

The is exactly the behaviour we do not want. What we want is at any given time there will at most one thread in critical section, thus it will make our type atomic. In below sections we will discuss various approaches to make the Atomic class truly atomic.

Using NSLock

We can achieve atomicity using NSLock from Foundation framework. Below is a simple implementation of Atomic class using NSLock.

final class NSAtomic<T: Operatable> {
    
    private var t: T
    private let lock = NSLock()
    init(_ t: T) {
        self.t = t
    }
    
    func incrementAndGet(by other: T) -> T {
        // Critical section starts
        // The change to t should be atomic here
        lock.lock()
        print("\(Thread.current.name!): critical section started")
        let result = t.add(other: other)
        t = result
        print("\(Thread.current.name!): critical section ended")
        lock.unlock()
        // Critical section ends
        return result
    }
}

func testNSAtomic() {
    print("test with NSLock")
    let atomicInt = NSAtomic<Int>(0)
    for i in 1...5 {
        let thread = Thread {
            _ = atomicInt.incrementAndGet(by: 1)
        }
        thread.name = "Thread: \(i)"
        thread.start()
    }
    Thread.sleep(forTimeInterval: 2)
}

We have just added a NSLock instance variable to our atomic type. Below is the output of using our NSAtomic class.

Thread: 1: critical section started
Thread: 1: critical section ended
Thread: 2: critical section started
Thread: 2: critical section ended
Thread: 3: critical section started
Thread: 3: critical section ended
Thread: 4: critical section started
Thread: 4: critical section ended
Thread: 5: critical section started
Thread: 5: critical section ended

The is the correct behaviour we want. Each thread wait outside critical section if there is another thread currently inside critical section. We have ensured that our value t is only changed by only one thread at any given time.

Using Grand Central Dispatch (GCD)

Using GCD is Apple recommended way of achieving concurrency. We will modify our Atomic class and implement it using a serial dispatch queue.

final class GCDAtomic<T: Operatable> {
    
    private let initial: T
    private var t: T
    private let dispatchQueue = DispatchQueue(label: "tech.swiftx.dispatchQueue")
    init(_ t: T) {
        self.t = t
        initial = t
    }
    
    func incrementAndGet(by other: T) -> T {
        
        var result = initial
        dispatchQueue.sync {
            // Critical section starts
            // The change to t should be atomic here
         
            print("\(Thread.current.name!): critical section started")
            result = t.add(other: other)
            t = result
            print("\(Thread.current.name!): critical section ended")
            
            // Critical section ends
        }
        return result
    }
}

Below is the output of using GCDAtomic class

Thread: 1: critical section started
Thread: 1: critical section ended
Thread: 5: critical section started
Thread: 5: critical section ended
Thread: 3: critical section started
Thread: 3: critical section ended
Thread: 4: critical section started
Thread: 4: critical section ended
Thread: 2: critical section started
Thread: 2: critical section ended

Please note that the order of thread execution is not relevant. The important factor is that at any give time only one of the thread is executing in critical section. This has also same behaviour as using NSLock.

All the above code snippets can be found here.

This a lot of boilerplate code. Luckily you dont have to implement atomic behaviour from scratch for your swift project. There is a popular open source library for working with atomics types in swift named swift-atomics.(I am not associated with swift-atomics library).

Towards Native Swift Atomics

As of writing there is a proposal SE-0282 in swift evolution that promises atomic types in swift as part of swift standard library. If the proposal is accepted we will see first class atomic types in swift in future swift versions.

Important Links

NSLock: https://developer.apple.com/documentation/foundation/nslock

GCD: https://developer.apple.com/documentation/DISPATCH

Swift-atomics: https://github.com/glessard/swift-atomics

SE-0283: https://github.com/apple/swift-evolution/blob/master/proposals/0282-atomics.md

Gist: https://bit.ly/3gD9QV6

std::atomic: https://en.cppreference.com/w/cpp/atomic/atomic

java.util.concurrent.atomic: https://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html

If you liked the article please consider sharing it with other people. Also please subscribe to my mailing list to get latest updates immediately.