Russ Cox mentioned changes to the atomic package in his series of articles last year and opened an issue for discussion, and now that the changes he committed have merged to the master branch, Go 1.19 will include those changes.

You can also use gotip to learn about these changes in advance.

This commit by Russ Cox simply adds some new types to atomic, which are a wrapper around primitive types (such as bool, int32, int64, uint32, uint64, uintptr, etc.) in order to provide atomic operations.

In fact, uber provided similar functionality a long time ago, and perhaps you used the uber-go/atomic library a long time ago.

Russ Cox implements it in a similar way. After all, this set of implementing wrappers for basic types is still relatively fixed, but the implementation is slightly different.

  • Russ Cox’s implementation in the standard library
    • embed _ noCopy , easy to go vet and other tools to do data race check
    • Code can be inlined to improve performance
  • Uber’s implementation
    • Embed _ nocmp , avoid non-atomic comparison
    • Provide JSON Marshal/UnMarshal function to facilitate serialization
    • Provide more types of wrappers: Duration, String, Time, Float64, Error

We will focus on the implementation of Russ Cox in this article, after all, it is a change in the Go standard library.

This change adds wrappers for bool, int32, int64, uint32, uint64, uintptr, Value, unsafe.Pointer types: Bool, Int32, Int64, Uint32, Uint64, Uintptr, Value , Pointer .

All of these types contain the following four methods :

  • CompareAndSwap(old, new *T) (swapped bool) : performs a CAS operation
  • Load() *T : Load the value corresponding to the atom
  • Store(val *T) : Store the value val atomically
  • Swap(new *T) (old* T) : Atomically store the new value and return the old one

There may be some additional methods for different types, such as :

  • Int32: func (x *Int32) Add(delta int32) (new int32)
  • Int64: func (x *Int64) Add(delta int64) (new int64)
  • Uint32: func (x *Int64) Add(delta int64) (new int64)
  • Uint64: func (x *Uint64) Add(delta uint64) (new uint64)
  • Uintptr: func (x *Uintptr) Add(delta uintptr) (new uintptr)

Similarly, for Uint32 and Uint64 types, there is only the Add method. If you want to subtract a positive value c, you have to use Add to add a negative value (-c), but the types of delta are all uxxx types, how can you do that? Use ^uint64(c-1) or ^uint32(c-1).

These wrappers and methods actually correspond to the corresponding functions of atomic, so it’s not difficult to use them:

1
2
3
4
5
6
var i Int64
i.Add(100)
i.Load()
i.Store(200)
i.Swap(300)
I.CompareAndSwap(300,400)

You can check the go doc for more detailed information: sync/atomic