How do I atomically increment a variable in Swift?

SwiftConcurrency

Swift Problem Overview


I want to be able to increment a counter atomically and I can't find any reference on how to do it.

Adding more information based on comments:

  • Are you using GCD? No. I am not using GCD. Having to use a queue system to increment a number seems overkill.
  • Do You understand basic thread safety? Yes I do otherwise I would not be asking about atomic increments.
  • Is this variable local? No.
  • Is it instance level? Yes it should be part of a single instance.

I want to do something like this:

 class Counter {
      private var mux: Mutex
      private (set) value: Int
      func increment() {
          mux.lock()
          value += 1
          mux.unlock()
      }
 }

Swift Solutions


Solution 1 - Swift

From Low-Level Concurrency APIs:

> There’s a long list of OSAtomicIncrement and OSAtomicDecrement > functions that allow you to increment and decrement an integer value > in an atomic way – thread safe without having to take a lock (or use > queues). These can be useful if you need to increment global counters > from multiple threads for statistics. If all you do is increment a > global counter, the barrier-free OSAtomicIncrement versions are fine, > and when there’s no contention, they’re cheap to call.

These functions work with fixed-size integers, you can choose the 32-bit or 64-bit variant depending on your needs:

class Counter {
    private (set) var value : Int32 = 0
    func increment () {
        OSAtomicIncrement32(&value)
    }
}

(Note: As Erik Aigner correctly noticed, OSAtomicIncrement32 and friends are deprecated as of macOS 10.12/iOS 10.10. Xcode 8 suggests to use functions from <stdatomic.h> instead. However that seems to be difficult, compare https://stackoverflow.com/questions/39356873/swift-3-atomic-compare-exchange-strong and https://openradar.appspot.com/27161329. Therefore the following GCD-based approach seems to be the best solution now.)

Alternatively, one can use a GCD queue for synchronization. From Dispatch Queues in the "Concurrency Programming Guide":

> ... With dispatch queues, you could add both tasks to a serial > dispatch queue to ensure that only one task modified the resource at > any given time. This type of queue-based synchronization is more > efficient than locks because locks always require an expensive kernel > trap in both the contested and uncontested cases, whereas a dispatch > queue works primarily in your application’s process space and only > calls down to the kernel when absolutely necessary.

In your case that would be

// Swift 2:
class Counter {
    private var queue = dispatch_queue_create("your.queue.identifier", DISPATCH_QUEUE_SERIAL)
    private (set) var value: Int = 0
    
    func increment() {
        dispatch_sync(queue) {
            value += 1
        }
    }
}

// Swift 3:
class Counter {
    private var queue = DispatchQueue(label: "your.queue.identifier") 
    private (set) var value: Int = 0
    
    func increment() {
        queue.sync {
            value += 1
        }
    }
}

See https://stackoverflow.com/questions/28784507/adding-items-to-swift-array-across-multiple-threads-causing-issues-because-arra or https://stackoverflow.com/questions/27839595/gcd-with-static-functions-of-a-struct for more sophisticated examples. This thread https://stackoverflow.com/questions/17599401/what-advantages-does-dispatch-sync-have-over-synchronized is also very interesting.

Solution 2 - Swift

Queues are an overkill in this case. You can use a DispatchSemaphore introduced in Swift 3 for this purpose like so:

import Foundation

public class AtomicInteger {
    
    private let lock = DispatchSemaphore(value: 1)
    private var value = 0
    
    // You need to lock on the value when reading it too since
    // there are no volatile variables in Swift as of today.
    public func get() -> Int {
        
        lock.wait()
        defer { lock.signal() }
        return value
    }
    
    public func set(_ newValue: Int) {
        
        lock.wait()
        defer { lock.signal() }
        value = newValue
    }
    
    public func incrementAndGet() -> Int {
        
        lock.wait()
        defer { lock.signal() }
        value += 1
        return value
    }
}

The latest version of the class is available over here.

Solution 3 - Swift

I know this question is already a little bit older, but I just recently stumbled upon the same problem. After researching a little and reading posts like http://www.cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html I came up with this solution for an atomic counter. Maybe it will also help others.

import Foundation

class AtomicCounter {

  private var mutex = pthread_mutex_t()
  private var counter: UInt = 0

  init() {
    pthread_mutex_init(&mutex, nil)
  }

  deinit {
    pthread_mutex_destroy(&mutex)
  }

  func incrementAndGet() -> UInt {
    pthread_mutex_lock(&mutex)
    defer {
      pthread_mutex_unlock(&mutex)
    }
    counter += 1
    return counter
  }
}

Solution 4 - Swift

Details

  • Xcode 10.1 (10B61)
  • Swift 4.2

Solution

import Foundation

struct AtomicInteger<Type>: BinaryInteger where Type: BinaryInteger {
    
    typealias Magnitude = Type.Magnitude
    typealias IntegerLiteralType = Type.IntegerLiteralType
    typealias Words = Type.Words
    fileprivate var value: Type
    
    private var semaphore = DispatchSemaphore(value: 1)
    fileprivate func _wait() { semaphore.wait() }
    fileprivate func _signal() { semaphore.signal() }
    
    init() { value = Type() }
    
    init(integerLiteral value: AtomicInteger.IntegerLiteralType) {
        self.value = Type(integerLiteral: value)
    }
    
    init<T>(_ source: T) where T : BinaryInteger {
        value = Type(source)
    }
    
    init(_ source: Int) {
        value = Type(source)
    }
    
    init<T>(clamping source: T) where T : BinaryInteger {
        value = Type(clamping: source)
    }
    
    init?<T>(exactly source: T) where T : BinaryInteger {
        guard let value = Type(exactly: source) else { return nil }
        self.value = value
    }
    
    init<T>(truncatingIfNeeded source: T) where T : BinaryInteger {
        value = Type(truncatingIfNeeded: source)
    }
    
    init?<T>(exactly source: T) where T : BinaryFloatingPoint {
        guard let value = Type(exactly: source) else { return nil }
        self.value = value
    }
    
    init<T>(_ source: T) where T : BinaryFloatingPoint {
        value = Type(source)
    }
}

// Instance Properties

extension AtomicInteger {
    var words: Type.Words {
        _wait(); defer { _signal() }
        return value.words
    }
    var bitWidth: Int {
        _wait(); defer { _signal() }
        return value.bitWidth
    }
    var trailingZeroBitCount: Int {
        _wait(); defer { _signal() }
        return value.trailingZeroBitCount
    }
    var magnitude: Type.Magnitude {
        _wait(); defer { _signal() }
        return value.magnitude
    }
}

// Type Properties

extension AtomicInteger {
    static var isSigned: Bool { return Type.isSigned }
}

// Instance Methods

extension AtomicInteger {
    
    func quotientAndRemainder(dividingBy rhs: AtomicInteger<Type>) -> (quotient: AtomicInteger<Type>, remainder: AtomicInteger<Type>) {
        _wait(); defer { _signal() }
        rhs._wait(); defer { rhs._signal() }
        let result = value.quotientAndRemainder(dividingBy: rhs.value)
        return (AtomicInteger(result.quotient), AtomicInteger(result.remainder))
    }
    
    func signum() -> AtomicInteger<Type> {
        _wait(); defer { _signal() }
        return AtomicInteger(value.signum())
    }
}


extension AtomicInteger {
    
    fileprivate static func atomicAction<Result, Other>(lhs: AtomicInteger<Type>,
                                                        rhs: Other, closure: (Type, Type) -> (Result)) -> Result where Other : BinaryInteger {
        lhs._wait(); defer { lhs._signal() }
        var rhsValue = Type(rhs)
        if let rhs = rhs as? AtomicInteger {
            rhs._wait(); defer { rhs._signal() }
            rhsValue = rhs.value
        }
        let result = closure(lhs.value, rhsValue)
        return result
    }
    
    fileprivate static func atomicActionAndResultSaving<Other>(lhs: inout AtomicInteger<Type>,
                                                               rhs: Other, closure: (Type, Type) -> (Type)) where Other : BinaryInteger {
        lhs._wait(); defer { lhs._signal() }
        var rhsValue = Type(rhs)
        if let rhs = rhs as? AtomicInteger {
            rhs._wait(); defer { rhs._signal() }
            rhsValue = rhs.value
        }
        let result = closure(lhs.value, rhsValue)
        lhs.value = result
    }
}

// Math Operator Functions

extension AtomicInteger {
    
    static func != <Other>(lhs: AtomicInteger, rhs: Other) -> Bool where Other : BinaryInteger {
        return atomicAction(lhs: lhs, rhs: rhs) { $0 != $1 }
    }
    
    static func != (lhs: AtomicInteger, rhs: AtomicInteger) -> Bool {
        return atomicAction(lhs: lhs, rhs: rhs) { $0 != $1 }
    }
    
    static func % (lhs: AtomicInteger, rhs: AtomicInteger) -> AtomicInteger {
        let value = atomicAction(lhs: lhs, rhs: rhs) { $0 % $1 }
        return self.init(value)
    }
    
    static func %= (lhs: inout AtomicInteger, rhs: AtomicInteger) {
        atomicActionAndResultSaving(lhs: &lhs, rhs: rhs) { $0 % $1 }
    }
    
    static func & (lhs: AtomicInteger, rhs: AtomicInteger) -> AtomicInteger {
        let value = atomicAction(lhs: lhs, rhs: rhs) { $0 & $1 }
        return self.init(value)
    }
    
    static func &= (lhs: inout AtomicInteger, rhs: AtomicInteger) {
        atomicActionAndResultSaving(lhs: &lhs, rhs: rhs) { $0 & $1 }
    }
    
    static func * (lhs: AtomicInteger, rhs: AtomicInteger) -> AtomicInteger {
        let value = atomicAction(lhs: lhs, rhs: rhs) { $0 * $1 }
        return self.init(value)
    }
    
    static func *= (lhs: inout AtomicInteger, rhs: AtomicInteger) {
        atomicActionAndResultSaving(lhs: &lhs, rhs: rhs) { $0 * $1 }
    }
    
    static func + (lhs: AtomicInteger, rhs: AtomicInteger) -> AtomicInteger {
        let value = atomicAction(lhs: lhs, rhs: rhs) { $0 + $1 }
        return self.init(value)
    }
    static func += (lhs: inout AtomicInteger, rhs: AtomicInteger) {
        atomicActionAndResultSaving(lhs: &lhs, rhs: rhs) { $0 + $1 }
    }
    
    static func - (lhs: AtomicInteger, rhs: AtomicInteger) -> AtomicInteger {
        let value = atomicAction(lhs: lhs, rhs: rhs) { $0 - $1 }
        return self.init(value)
    }
    
    static func -= (lhs: inout AtomicInteger, rhs: AtomicInteger) {
        atomicActionAndResultSaving(lhs: &lhs, rhs: rhs) { $0 - $1 }
    }
    
    static func / (lhs: AtomicInteger, rhs: AtomicInteger) -> AtomicInteger {
        let value = atomicAction(lhs: lhs, rhs: rhs) { $0 / $1 }
        return self.init(value)
    }
    
    static func /= (lhs: inout AtomicInteger, rhs: AtomicInteger) {
        atomicActionAndResultSaving(lhs: &lhs, rhs: rhs) { $0 / $1 }
    }
}


// Shifting Operator Functions

extension AtomicInteger {
    static func << <RHS>(lhs:  AtomicInteger<Type>, rhs: RHS) -> AtomicInteger where RHS : BinaryInteger {
        let value = atomicAction(lhs: lhs, rhs: rhs) { $0 << $1 }
        return self.init(value)
    }
    
    static func <<= <RHS>(lhs: inout AtomicInteger, rhs: RHS) where RHS : BinaryInteger {
        atomicActionAndResultSaving(lhs: &lhs, rhs: rhs) { $0 << $1 }
    }
    
    static func >> <RHS>(lhs: AtomicInteger, rhs: RHS) -> AtomicInteger where RHS : BinaryInteger {
        let value = atomicAction(lhs: lhs, rhs: rhs) { $0 >> $1 }
        return self.init(value)
    }
    
    static func >>= <RHS>(lhs: inout AtomicInteger, rhs: RHS) where RHS : BinaryInteger {
        atomicActionAndResultSaving(lhs: &lhs, rhs: rhs) { $0 >> $1 }
    }
}

// Comparing Operator Functions

extension AtomicInteger {
    
    static func < <Other>(lhs: AtomicInteger<Type>, rhs: Other) -> Bool where Other : BinaryInteger {
        return atomicAction(lhs: lhs, rhs: rhs) { $0 < $1 }
    }
    
    static func <= (lhs: AtomicInteger, rhs: AtomicInteger) -> Bool {
        return atomicAction(lhs: lhs, rhs: rhs) { $0 <= $1 }
    }
    
    static func == <Other>(lhs: AtomicInteger, rhs: Other) -> Bool where Other : BinaryInteger {
        return atomicAction(lhs: lhs, rhs: rhs) { $0 == $1 }
    }
    
    static func > <Other>(lhs: AtomicInteger, rhs: Other) -> Bool where Other : BinaryInteger {
        return atomicAction(lhs: lhs, rhs: rhs) { $0 > $1 }
    }
    
    static func > (lhs: AtomicInteger, rhs: AtomicInteger) -> Bool {
        return atomicAction(lhs: lhs, rhs: rhs) { $0 > $1 }
    }
    
    static func >= (lhs: AtomicInteger, rhs: AtomicInteger) -> Bool {
        return atomicAction(lhs: lhs, rhs: rhs) { $0 >= $1 }
    }
    
    static func >= <Other>(lhs: AtomicInteger, rhs: Other) -> Bool where Other : BinaryInteger {
        return atomicAction(lhs: lhs, rhs: rhs) { $0 >= $1 }
    }
}

// Binary Math Operator Functions

extension AtomicInteger {
    
    static func ^ (lhs: AtomicInteger, rhs: AtomicInteger) -> AtomicInteger {
        let value = atomicAction(lhs: lhs, rhs: rhs) { $0 ^ $1 }
        return self.init(value)
    }
    
    static func ^= (lhs: inout AtomicInteger, rhs: AtomicInteger) {
        atomicActionAndResultSaving(lhs: &lhs, rhs: rhs) { $0 ^ $1 }
    }
    
    static func | (lhs: AtomicInteger, rhs: AtomicInteger) -> AtomicInteger {
        let value = atomicAction(lhs: lhs, rhs: rhs) { $0 | $1 }
        return self.init(value)
    }
    
    static func |= (lhs: inout AtomicInteger, rhs: AtomicInteger) {
        atomicActionAndResultSaving(lhs: &lhs, rhs: rhs) { $0 | $1 }
    }
    
    static prefix func ~ (x: AtomicInteger) -> AtomicInteger {
        x._wait(); defer { x._signal() }
        return self.init(x.value)
    }
}

// Hashable

extension AtomicInteger {
    
    var hashValue: Int {
        _wait(); defer { _signal() }
        return value.hashValue
    }
    
    func hash(into hasher: inout Hasher) {
        _wait(); defer { _signal() }
        value.hash(into: &hasher)
    }
}

// Get/Set

extension AtomicInteger {

    // Single  actions

    func get() -> Type {
        _wait(); defer { _signal() }
        return value
    }

    mutating func set(value: Type) {
        _wait(); defer { _signal() }
        self.value = value
    }

    // Multi-actions
    
    func get(closure: (Type)->()) {
        _wait(); defer { _signal() }
        closure(value)
    }
    
    mutating func set(closure: (Type)->(Type)) {
        _wait(); defer { _signal() }
        self.value = closure(value)
    }
}

Usage

// Usage Samples
let numA = AtomicInteger<Int8>(0)
let numB = AtomicInteger<Int16>(0)
let numC = AtomicInteger<Int32>(0)
let numD = AtomicInteger<Int64>(0)

var num1 = AtomicInteger<Int>(0)
num1 += 1
num1 -= 1
num1 = 10
num1 = num1/2

var num2 = 0
num2 = num1.get()
num1.set(value: num2*5)

// lock num1 to do several actions
num1.get { value in
    //...
}

num1.set { value in
    //...
    return value
}

Full Sample

import Foundation

var x = AtomicInteger<Int>(0)
let dispatchGroup = DispatchGroup()
private func async(dispatch: DispatchQueue, closure: @escaping (DispatchQueue)->()) {
    for _ in 0 ..< 100 {
        dispatchGroup.enter()
        dispatch.async {
            print("Queue: \(dispatch.qos.qosClass)")
            closure(dispatch)
            dispatchGroup.leave()
        }
    }
}

func sample() {
    let closure1: (DispatchQueue)->() = { _ in x += 1 }
    let closure2: (DispatchQueue)->() = { _ in x -= 1 }
    async(dispatch: .global(qos: .userInitiated), closure: closure1) // result: x += 100
    async(dispatch: .global(qos: .utility), closure: closure1) // result: x += 100
    async(dispatch: .global(qos: .background), closure: closure2) // result: x -= 100
    async(dispatch: .global(qos: .default), closure: closure2) // result: x -= 100
}

sample()
dispatchGroup.wait()
print(x) // expected result x = 0

Solution 5 - Swift

I improved on the answer from @florian, by using some overloaded operators :

import Foundation

class AtomicInt {
    
    private var mutex = pthread_mutex_t()
    private var integer: Int = 0
    var value : Int {
        return integer
    }
    
    
    //MARK: - lifecycle
    
    
    init(_ value: Int = 0) {
        pthread_mutex_init(&mutex, nil)
        integer = value
    }
    
    deinit {
        pthread_mutex_destroy(&mutex)
    }

    
    //MARK: - Public API

    
    func increment() {
        pthread_mutex_lock(&mutex)
        defer {
            pthread_mutex_unlock(&mutex)
        }
        integer += 1
    }
    
    func incrementAndGet() -> Int {
        pthread_mutex_lock(&mutex)
        defer {
            pthread_mutex_unlock(&mutex)
        }
        integer += 1
        return integer
    }
    
    func decrement() {
        pthread_mutex_lock(&mutex)
        defer {
            pthread_mutex_unlock(&mutex)
        }
        integer -= 1
    }
    
    func decrementAndGet() -> Int {
        pthread_mutex_lock(&mutex)
        defer {
            pthread_mutex_unlock(&mutex)
        }
        integer -= 1
        return integer
    }
    
    
    //MARK: - overloaded operators
    
   static func > (lhs: AtomicInt, rhs: Int) -> Bool {
        return lhs.integer > rhs
    }
    
    static func < (lhs: AtomicInt, rhs: Int) -> Bool {
        return lhs.integer < rhs
    }
    
    static func == (lhs: AtomicInt, rhs: Int) -> Bool {
        return lhs.integer == rhs
    }
    
    static func > (lhs: Int, rhs: AtomicInt) -> Bool {
        return lhs > rhs.integer
    }
    
    static func < (lhs: Int, rhs: AtomicInt) -> Bool {
        return lhs < rhs.integer
    }

    static func == (lhs: Int, rhs: AtomicInt) -> Bool {
        return lhs == rhs.integer
    }
    
    func test() {
        let atomicInt = AtomicInt(0)
        atomicInt.increment()
        atomicInt.decrement()
        if atomicInt > 10  { print("bigger than 10") }
        if atomicInt < 10  { print("smaller than 10") }
        if atomicInt == 10 { print("its 10") }
        if 10 > atomicInt  { print("10 is bigger") }
        if 10 < atomicInt  { print("10 is smaller") }
        if 10 == atomicInt { print("its 10") }
    }

}

Solution 6 - Swift

You can use @propertyWrappers for that

import Foundation

@propertyWrapper
class Atomic<Value> where Value: BinaryInteger {

    private let lock: NSLock
    private var value: Value

    init(default: Value) {
        self.lock = NSLock()
        self.value = `default`
    }

    var wrappedValue: Value {
        get {
            lock.lock()
            defer { lock.unlock() }
            return value
        }
        set {
            lock.lock()
            value = newValue
            lock.unlock()
        }
    }

    var projectedValue: Atomic<Value> { self }

    func add(_ value: Value) {
        lock.lock()
        self.value += value
        lock.unlock()
    }
}

You can use it like this to increment atomically

class Foo {

    @Atomic(default: 1)
    var counter: Int
}

let foo = Foo()
foo.$counter.add(3)

Or use Swift Atomics if you want to include an external library.

Solution 7 - Swift

You can take a look at Swift Atomics library hosted by Apple which supports general types

[Concurrency theory]

Solution 8 - Swift

There are various approaches we can use to have atomically increment a variable in swift and has been discussed here.

Also there is a swift proposal SE-0283 to add atomic variables natively in swift.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionfabrizioMView Question on Stackoverflow
Solution 1 - SwiftMartin RView Answer on Stackoverflow
Solution 2 - SwiftAleks N.View Answer on Stackoverflow
Solution 3 - SwiftFlorian BauerView Answer on Stackoverflow
Solution 4 - SwiftVasily BodnarchukView Answer on Stackoverflow
Solution 5 - SwiftHixFieldView Answer on Stackoverflow
Solution 6 - SwiftEraView Answer on Stackoverflow
Solution 7 - SwiftyoAlex5View Answer on Stackoverflow
Solution 8 - SwiftKhurram ShehzadView Answer on Stackoverflow