Search code examples
swiftconcurrency

How do I atomically increment a variable in Swift?


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()
      }
 }

Solution

  • 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 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 Adding items to Swift array across multiple threads causing issues (because arrays aren't thread safe) - how do I get around that? or GCD with static functions of a struct for more sophisticated examples. This thread What advantage(s) does dispatch_sync have over @synchronized? is also very interesting.