Should Go’s Memory Model Evolve? Proposals for Sync and Atomic API Guarantees
This article reviews the Go memory model first written in 2009, highlights missing documentation for sync and sync/atomic APIs, proposes concrete textual and typed‑atomic extensions, and explains why certain compiler optimizations must remain prohibited to preserve clear, race‑free semantics.
Background and Motivation
Go’s memory model was introduced in 2009 and has only received minor updates since. The author argues that the model should be extended to explicitly cover the race detector and to provide clear synchronization guarantees for the sync and sync/atomic packages.
Go’s Design Philosophy
Go aims to be a practical, efficient language for both small projects and large engineering teams. It encourages communication over shared memory, follows the maxim “don’t communicate by sharing memory; share memory by communicating,” and prefers clarity over cleverness. The language and its standard library are designed to avoid undefined behavior; bugs such as nil‑pointer dereference, integer overflow, or infinite loops have well‑defined semantics.
Current Memory Model Overview
The model is built around a happens‑before relation. Key guarantees include:
If package p imports package q, then q ’s init completes before any
p initstarts.
The main.main function starts after all init functions have finished.
A go statement happens‑before the started goroutine begins execution.
Send on a channel happens‑before the corresponding receive completes.
Close of a channel happens‑before a receive that returns the zero value.
Unbuffered channel receive happens‑before the matching send.
For a buffered channel of capacity C, the k ‑th receive happens‑before the k+C ‑th send.
Unlock of a sync.Mutex or sync.RWMutex happens‑before the next lock.
Each call to once.Do happens‑before any later call returns.
The list notably omits any explicit mention of the new APIs in sync and sync/atomic.
Proposed Documentation of Go’s Overall Approach
The author suggests adding a concise overview that clarifies Go’s stance between the “no‑data‑race = sequentially consistent” model of Java and the “data‑race = undefined” model of C/C++. The text would explain that data races are defined, that programs without races behave as if all goroutines ran on a single processor (DRF‑SC), and that certain multi‑word races can still cause memory corruption.
Documenting Happens‑Before for sync Primitives
sync.Cond.Broadcastor Signal happens‑before the return of any unblocked Wait call.
Operations on sync.Map are classified as reads ( Load, LoadAndDelete, LoadOrStore when it returns loaded==false) or writes ( Store, Delete, LoadOrStore when it returns loaded==true), with writes happening‑before subsequent reads.
For sync.Pool, a Put happens‑before a Get that returns the same value, and New behaves similarly.
For sync.WaitGroup, a Done happens‑before any Wait that returns.
These guarantees should be reflected in the package documentation to guide users.
Documenting Happens‑Before for sync/atomic
All atomic operations form a sequentially consistent total order: if atomic operation A is observed by atomic operation B, then A happens‑before B. This matches Java’s volatile and C++’s default atomic semantics.
The author argues that sequential consistency is more useful than acquire/release semantics, especially as modern hardware (e.g., ARMv8) provides native sequentially consistent instructions.
Typed Atomic API Proposal
Introduce a set of typed atomic types (Bool, Int, Uint, Int32, Uint32, Int64, Uint64, Uintptr) each exposing CompareAndSwap, Load, Store, Swap, and Add where appropriate. Example implementation for Int32:
type Int32 struct { v int32 }
func (i *Int32) Add(delta int32) int32 { return AddInt32(&i.v, delta) }
func (i *Int32) CompareAndSwap(old, new int32) (swapped bool) { return CompareAndSwapInt32(&i.v, old, new) }
func (i *Int32) Load() int32 { return LoadInt32(&i.v) }
func (i *Int32) Store(v int32) { StoreInt32(&i.v, v) }
func (i *Int32) Swap(new int32) (old int32) { return SwapInt32(&i.v, new) }Generic support could later enable a Pointer[T any] type with CompareAndSwap and other methods, avoiding the need for a separate unsafe import in most cases.
Possibility of Unsynchronized Atomics
The author explores adding “unsync” variants (e.g., UnsyncAdd, UnsyncLoad) that provide no ordering guarantees, similar to “relaxed” atomics in C/C++. The discussion weighs performance benefits on non‑x86 architectures against the risk of misuse and the difficulty of detecting such code with the race detector.
Documenting Disallowed Compiler Optimizations
Because the memory model is a contract between programmers and the compiler, certain optimizations must be prohibited:
Moving a read or write out of a conditional block that could introduce a data race.
Reordering loop‑invariant accesses that would create a happens‑before edge only in the original program.
Hoisting accesses across function calls without knowing whether the call contains synchronization.
Reloading a value from shared memory after it has been cached locally, which could change between reads.
Examples illustrate how a compiler must not transform code in ways that would turn a race‑free program into one with a data race.
Conclusion
Go’s conservative memory‑model approach has served the language well, but several overdue changes are needed: explicit documentation of the synchronization behavior of new sync and sync/atomic APIs, adoption of sequentially consistent atomic operations, and clearer statements about the limits of data‑race handling. These updates would align Go with other modern system languages while preserving its emphasis on debuggable, correct concurrent programs.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Cognitive Technology Team
Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
