Most importantly though, previous Swift Atomics implementation (and to a broader scope, the lightweight locks such as os_unfair_lock on iOS), you have to choose from two evils:
Have a reference-counted object, thus, you have a stable memory address for either locking or atomic operations;
Use struct to avoid reference-counting. But unlike C++, you don’t have move / copy constructor, so you don’t know if the underlying address was moved / copied to a different location. You have to be careful.
The language itself spends a lot of time on balancing usability, memory safety and efficiency. That is why the whole language nowadays leaning very heavily on protocol + struct as the main programming paradigm (as demonstrated by SwiftUI) because it is safe (immutable by default, no object inheritance), easy to use (auto-synthesize Equatable, Hashable etc protocol conformance) and efficient (no reference counting).
The previous implementations such as glessard/swift-atomics choose to use struct as the base, with a giant warning of “there will be dragons!” because for atomics, you really don’t want to allocate reference-counted memory for each of them separately. At the same time, the memory address can change under you. It is a dilemma.
The new Swift Atomics library choose a different approach. To casually use it, you can use ManagedAtomic<> which will do the right thing, but you get a reference-counted memory for each and every of your “atomics”. This avoid the foot-gun situation for beginners.
To seriously use the new Swift Atomics library, it requires more dances. Basically, you need to first allocate the storage somewhere:
class MyOwnClassThatCanHoldTheAtomics {
var atomic: UnsafeAtomic<Int>.Storage(0)
}
and each time to use it, everything has to be explicit:
// Use withUnsafeMutablePointer to get a valid pointer until load call finishes.
let loadedValue = withUnsafeMutablePointer(to: &atomic) {
UnsafeAtomic(at: $0).load(ordering: .acquiring)
}
This is a lot of dances comparing to a ManagedAtomic:
let atomic: ManagedAtomic<Int>(0)
let loadedValue = atomic.load(ordering: .acquiring)
It will be interesting to see how people tend to use it in the wild, but kudos to the Apple team put so much thoughts into the use-case and try to make the incorrect use as much as impossible.
I’ve spent a little bit more time to try it out for https://github.com/liuliu/dflat. Previously, I use https://github.com/glessard/swift-atomics. It sort of works, but has its own gotchas and also “at your own risk” because no SE-0282 to guarantee a memory model.
Most importantly though, previous Swift Atomics implementation (and to a broader scope, the lightweight locks such as
os_unfair_lock
on iOS), you have to choose from two evils:struct
to avoid reference-counting. But unlike C++, you don’t have move / copy constructor, so you don’t know if the underlying address was moved / copied to a different location. You have to be careful.The language itself spends a lot of time on balancing usability, memory safety and efficiency. That is why the whole language nowadays leaning very heavily on protocol + struct as the main programming paradigm (as demonstrated by SwiftUI) because it is safe (immutable by default, no object inheritance), easy to use (auto-synthesize Equatable, Hashable etc protocol conformance) and efficient (no reference counting).
The previous implementations such as glessard/swift-atomics choose to use
struct
as the base, with a giant warning of “there will be dragons!” because for atomics, you really don’t want to allocate reference-counted memory for each of them separately. At the same time, the memory address can change under you. It is a dilemma.The new Swift Atomics library choose a different approach. To casually use it, you can use ManagedAtomic<> which will do the right thing, but you get a reference-counted memory for each and every of your “atomics”. This avoid the foot-gun situation for beginners.
To seriously use the new Swift Atomics library, it requires more dances. Basically, you need to first allocate the storage somewhere:
and each time to use it, everything has to be explicit:
This is a lot of dances comparing to a
ManagedAtomic
:It will be interesting to see how people tend to use it in the wild, but kudos to the Apple team put so much thoughts into the use-case and try to make the incorrect use as much as impossible.