'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 1:[1]
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.
Solution 2:[2]
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:[3]
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:[4]
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:[5]
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:[6]
You can take a look at Swift Atomics
library hosted by Apple which supports general types
Solution 7:[7]
You can use @propertyWrapper
s 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 8:[8]
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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Community |
Solution 2 | |
Solution 3 | Florian Bauer |
Solution 4 | |
Solution 5 | HixField |
Solution 6 | |
Solution 7 | |
Solution 8 | Khurram Shehzad |