53

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()
      }
 }
Palle
  • 11,511
  • 2
  • 40
  • 61
fabrizioM
  • 46,639
  • 15
  • 102
  • 119

8 Answers8

71

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.

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Using `OSAtomicIncrement32` with variables that aren't dumb fields (with get/set accessors or willSet/didSet observers) still compiles, but in case it wasn't obvious already, the compiler will insert additional instructions that make the operation as a whole dubiously atomic. Just a thing to keep an eye on. – zneak Jun 29 '16 at 04:58
  • 6
    Even the `OSAtomic...` functions are deprecated, it seems an incredibly expensive solution to queue a block for execution just to do such a simple task... – Joe Daniels Nov 01 '16 at 01:17
  • 1
    Is this truly threadsafe even though the getter for `value` is not also synchronized using the serial queue? It seems like a read from `value` could happen from any thread and get interleaved with the write to `value` in the serial queue. – Patrick Goley Dec 11 '16 at 19:03
  • 3
    @PatrickGoley: You are completely right, the answer applies only to the *"How do I atomically increment a variable?"* question. If the variable is also read from different threads then the read should be synchronized with the same queue. – Martin R Dec 11 '16 at 20:07
  • Queue is always a terrible solution for synchronization. Use `DispatchSemaphore` instead. –  Mar 08 '19 at 13:52
34

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.

Aleks N.
  • 6,051
  • 3
  • 42
  • 42
14

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
  }
}
Florian Bauer
  • 401
  • 5
  • 11
  • 3
    round of applause for the destroy in deinit – Dannie P Nov 01 '17 at 17:25
  • I am not sure. The benchmark you linked uses Objektiv-C, but this discussion is about Swift. The article that I linked says that the problem is that Swift needs to capture the closure when using dispatch_sync. And that it is this heap allocation, which makes it very slow. – Florian Bauer Sep 13 '19 at 08:32
  • 2
    Note that this is not proper in Swift: `var mutex = pthread_mutex_t()`. You need to allocate the memory for the `pthread_mutex_t` yourself, e.g. `let mutex = UnsafeMutablePointer.allocate(capacity: 1)`. – hnh Feb 12 '21 at 16:19
11

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
Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
2

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

}
HixField
  • 3,538
  • 1
  • 28
  • 54
2

Swift Atomic

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

[Concurrency theory]

yoAlex5
  • 29,217
  • 8
  • 193
  • 205
1

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.

  • This does not work: When mutating `bar`, Swift will first access the getter, which will acquire the lock, return the value and subsequently release the lock again. Then, the value is mutated, the lock is acquired again, the mutated value is written and the lock is released again. This can lead to the scenario, where two threads both first read the value (sequentially because of the lock), then both perform their edits and sequentially write back their results, such that the thread that happens to acquire the lock last determines the value, overwriting other concurrent writes. – Palle Apr 16 '22 at 21:06
  • Yes that's true, but that is another problem. Mutating a value is always different. In that case you can add a custom method to your property wrapper that performs the mutation and access it via its projected value. –  Apr 20 '22 at 10:08
  • No, that is not another problem. That is the problem asked about in the question. – Palle Apr 21 '22 at 11:02
0

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.

Khurram Shehzad
  • 261
  • 3
  • 12