SE-410 and AtomicReference

SE-410 says the following in the future directions on Atomic Strong References and The Problem of Memory Reclamation

There are a variety of approaches to tackle this problem, but the one we think would be the best fit is the implementation of AtomicReference in the swift-atomics package.

I started trying to rewrite some code that uses swift-atomics today that uses the AtomicReference type and realized that I don't have that type in the std lib. Anyone have recommendations on what to do instead?

Suppose I should be tagging @lorentey and @Alejandro on that question rather than asking just anyone :slight_smile:

1 Like

I agree that the Swift Standard Library should come with out-of-the-box support for atomic strong references. It missed SE-0410 because the implementation in swift-atomics needs to invoke the swift_retain_n/swift_release_n entry points in the Swift runtime, and we did not have capacity to figure out how to do that properly. (The package is most definitely not doing it right -- we want to do a better job.)

We did ship all other necessary primitives, though (the most prominent being atomic operations on the WordPair type), so as long as you solve the retain_n/release_n situation, it should be reasonable straightforward to directly port the algorithm in swift-atomics to struct Atomic.

We've also shipped struct Mutex since then, and a mutex protecting a class instance works as a quick-and-dirty substitute for an atomic strong reference. (The obvious drawback is that Mutex isn't a lock-free solution; whether that is a problem depends on your use case.)

I have an unfinished PR that attempts to make progress on allowing Swift code to invoke those problematic runtime entry points. My full plan was to subsequently expose new Swift builtins that we could wrap in new Unmanaged API (Unmanaged.retain(by:)/.release(by:). I do still believe that is what we need, but I did not have the energy to work through the pushback I got from compiler experts at the time.

Note: calling the preexisting Unmanaged.retain()/.release() in a loop is not a viable substitute; we really do need to do multiple increments/decrements of the refcount in a single operation, or the construct would typically perform far worse than Mutex.

Note 2: while it may seem tempting to work out your own implementation for atomic operations on strong references, beware that it is quite a challenging task. (Very fun though!) So unless you have a month or two to do it, I'd recommend simply using Mutex or porting the algorithm from swift-atomics.

1 Like

Hmmm... I was afraid of that. :) I need to go look at my previous code, but now that you mention it I remember some previous conversations (this one for example) where retain_n/release_n were a problem in Playgrounds and I solved it with the looping technique.

Curious as to why that is such a big deal to expose when the single versions seem to be fine.

Swift’s standard library doesn’t yet have AtomicReference. For now, wrap your class in Unmanaged and use ManagedAtomic<Unmanaged?> to handle atomic swaps safely. Be careful with manual retain and release to manage memory properly. Alternatively, use locks or actors.