Swift Atomics, Playgrounds, Swift 5.9 and move-only types

I feel like I must be missing something everyone else is seeing.

Below is the standard simplest example of using an atomic as a counter. I just ran this in a playground using the tip of main on swift-atomics and verified it works as expected.

If the ManagedAtomic in this example becomes AtomicValue<T>: ~Copyable type (as clearly discussed in this thread), how can I access that Atomic<Int> from two different Tasks concurrently to increment the counter? If the answer is: "you can't" then when would I ever use a ~Copyable Atomic? And how would I modify this code using borrowing/consuming to achieve the concurrency this requires?

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

import Atomics

class IntRef { var i: Int = 0 }

let atomicValue: ManagedAtomic<Int> = .init(0)
let nonatomicValue: IntRef = .init()

Task {
    let t1 = Task<Void, Never> {
        for i in 0 ..< 100_000 {
            atomicValue.wrappingIncrementThenLoad(by: 1, ordering: .relaxed)
            nonatomicValue.i += 1
        }
    }

    let t2 = Task<Void, Never> {
        for i in 0 ..< 100_000 {
            atomicValue.wrappingIncrementThenLoad(by: 1, ordering: .relaxed)
            nonatomicValue.i += 1
        }
    }
    await t1.value
    await t2.value

    print("done. atomicValue = \(atomicValue.load(ordering: .relaxed)), nonatomicValue = \(nonatomicValue.i)")
    PlaygroundPage.current.finishExecution()
}
1 Like