Skip to content

sync/atomic: add typed atomic values #50860

@rsc

Description

@rsc

In June 2021 I posted a series of articles about memory models, ending with an article about changes I thought we should make to the Go memory model. See https://research.swtch.com/mm especially https://research.swtch.com/gomm.

Then I opened a GitHub Discussion to discuss these changes; see #47141.

Based on that discussion, I propose to add the following types to sync/atomic: Bool, Int32, Int64, Uint32, Uint64, Uintptr, Pointer[T any].

These have methods appropriate to the type. They all have Load, Store, Swap, and CompareAndSwap methods. The integers also have an Add method (Bool and Pointer[T] do not).

The exact details can be viewed in pending CL 381317 prepared for concreteness.

I have filed a separate proposal, #50859, for documentation fixes arising from the June discussion.


Generics? A natural question is why the types are not something like atomic.Val[bool], atomic.Val[int32], and so on. The main answer is that the APIs are different for different types, and generics provides no way to accomplish that.

Specifically, Bool and Pointer[T] should not have an Add method, while the integers should.

Another reason is that there is no way to write a single constraint that works for this set of types. The way to write a generic pointer constraint, for atomic.Val[*byte], is to say [T ~*E, E any], but there is no way to add on the other types. If we do

type Val[T interface { ~*E | ~bool | ~int32 | ... }, E any] struct { ... }

then any use that infers bool, int32, or so on for T will not have any idea what to infer for E, requiring the programmer to write

atomic.Val[bool, DOESNOTMATTER]

where DOESNOTMATTER is any type at all. All in all, trying to use generics is pretty awkward here.

Uses are awkward too: atomic.Val[int32] is not as nice as plain atomic.Int32.

We could potentially introduce a single generic for the ints, as in atomic.Int[int32], but that removes awkwardness in the implementation at the cost of introducing awkwardness (repetition) at all the call sites. That's usually the wrong tradeoff, including here.

(Finally there is the matter of what the implementation body would look like in the generic functions, but all the preceding concerns prevent us from even reaching that one.)

The non-generic API is simpler to explain and easier to use.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions