Go

What are typed atomics (atomic.Int64, atomic.Pointer[T]) and why prefer them over atomic.AddInt64 / atomic.Value?

Question 230MediumGo 1.22 to 1.25

Go 1.19 added atomic.Int32, Int64, Uint32, Uint64, Uintptr, Bool and Pointer[T]. They are better than the function-based API for three reasons:

  • No accidental non-atomic access: the value can only be reached through methods. With a plain int64 it is easy to write x++ somewhere and create a race.
  • Alignment: atomic.Int64 and Uint64 are guaranteed 64-bit aligned even on 32-bit platforms (386, ARM). atomic.AddInt64 on a misaligned field panics there.
  • Type safety: atomic.Pointer[T] replaces atomic.Value. atomic.Value panics on Store(nil) and on storing inconsistent concrete types, and it needs a type assertion on every Load.
type Server struct {
	requests atomic.Int64
	cfg      atomic.Pointer[Config] // copy-on-write config
}

func (s *Server) Handle() {
	s.requests.Add(1)
	cfg := s.cfg.Load() // lock-free read of an immutable snapshot
	_ = cfg.Timeout
}

func (s *Server) Reload(c *Config) { s.cfg.Store(c) }

Go 1.23 added And and Or methods on the integer types.

What the interviewer is looking for: the snapshot behind an atomic.Pointer must be treated as immutable. Mutating *cfg after Store is a data race.

More on Concurrency Patterns & sync

All 38 Concurrency Patterns & sync questions