Low-Level Atomic Operations

Leaving room for the non-copiable variant is not a concern here -- the "proper" atomic generic will be called Atomic<Value>.

UnsafeAtomicPointer<Value> is uncomfortably close to UnsafeMutablePointer<Pointee>, to the point of confusion. UnsafePointerToAtomic<Value> lends itself to a general naming scheme, so let's go with that one.

struct UnsafePointerToAtomic<Value>
struct UnsafePointerToAtomicLazyReference<Instance>
struct UnsafePointerToUnfairLock

Again, this is a non-issue; the eventual move-only variants won't have any of these naming warts.

moveonly struct Atomic<Value>
moveonly struct AtomicLazyReference<Instance>
moveonly struct UnfairLock

I'll try adding more examples. Optionals are explicitly supported.

Supporting implicitly unwrapped optionals with UnsafePointerToAtomic<Value> isn't feasible. We aren't modeling IUOs in the standard library any more, and UnsafePointerToAtomic<Foo!> is not supported as syntax.

Modeling IUOs through a separate type would be possible, of course:

struct UnsafePointerToAtomicImplicitlyUnwrappedOptional<Wrapped: NullableAtomic> {
  func isNil(ordering: AtomicLoadOrdering) -> Bool
  func load(ordering: AtomicLoadOrdering) -> Wrapped // traps on nil
  ...
}

However, I feel that low-level atomics are tricky enough to use correctly without having to deal with arbitrary implicit behavior layered on top of them. I don't think having to spell out the ! (or having to manually implement such a type) is going to be a hardship -- so I don't think such a construct belongs in the stdlib.

Agreed. But the language provides no (reliable) way to retrieve the memory location of a class instance variable -- the above code has undefined behavior. I previously explored this area in Exposing the Memory Locations of Class Instance Variables.

The implicit inout-to-pointer conversion is not appropriate for atomics, and I feel it's harmful to the language in general. The problem is that there is no way to disallow this conversion from occurring through a temporary variable. (I attempted to spell this out in Interaction with implicit pointer conversions.) The fact that it mostly works in practice is a huge problem, because we have no reasonable suggestion to replace it for people who absolutely do need to do such things. (Including the stdlib, sometimes.)

The only currently supported way to define "inline" storage for atomics is through ManagedBuffer.withUnsafeMutablePointerToHeader and MemoryLayout.offset(of:). This is decidedly not pretty, even in the lucky case where the Header is a single atomic value.

3 Likes