NOTE
2.11 atomic
What atomicity is, what atomic.Value is, how to use it, and the implementation of Store and Load.
This is a historical learning note and may contain outdated or incomplete understanding.
1. What Is atomic
Atomicity.
- One or more operations either all execute or none of them execute.
- The smallest operation that cannot be parallelized means that at most one concurrent execution context can operate on the resource at the same time.
- In general, atomic operations are guaranteed through mutually exclusive access and are usually protected by special CPU instructions.
2. What Is atomic value
The atomic package wraps atomic operations provided by the underlying hardware as Go functions, but it only supports several basic data types.
atomic.Value can atomically Store and Load values of arbitrary types.
3. How to Use It
var (
count = 0
GlobalConfig atomic.Value
updateSignal chan int = make(chan int)
)
type config struct {
Url string
Name string
Password string
}
func init() {
GlobalConfig.Store(&config{
Url: strconv.Itoa(count),
Name: strconv.Itoa(count),
Password: "<REDACTED>",
})
}
func updateConfig(count int) {
GlobalConfig.Store(&config{
Url: strconv.Itoa(count),
Name: strconv.Itoa(count),
Password: "<REDACTED>",
})
updateSignal <- 1
}
func loadConfig() *config {
return GlobalConfig.Load().(*config)
}
func TestAtomic1(t *testing.T) {
// Background goroutine updates the configuration
go func() {
for {
time.Sleep(3 * time.Second)
count++
updateConfig(count)
}
}()
// Main goroutine pulls the latest configuration and processes it
for {
select {
case <-updateSignal:
fmt.Println(loadConfig())
//...
}
}
}
4. Source Code Analysis
4.1. Data Structure
type Value struct {
v interface{}// can store a value of any type
}
// Used to split interface{} into its two fields
type ifaceWords struct {
typ unsafe.Pointer
data unsafe.Pointer
}
4.2. Methods
4.2.1. Store
func (v *Value) Store(x interface{}) {
if x == nil {
panic("sync/atomic: store of nil value into Value")
}
vp := (*ifaceWords)(unsafe.Pointer(v)) // old value of vp
xp := (*ifaceWords)(unsafe.Pointer(&x)) // new value of xp
// Infinite loop
for {
typ := LoadPointer(&vp.typ)// get the dynamic type of the old value
if typ == nil {// the dynamic type is empty, so this is the first initialization
runtime_procPin()
// CAS sets the dynamic type of the old value to 111...111
if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) {
runtime_procUnpin()
continue// continue the for loop if the set failed
}
// Set the dynamic value and dynamic type
StorePointer(&vp.data, xp.data)
StorePointer(&vp.typ, xp.typ)
runtime_procUnpin()
return// return directly after the first set succeeds
}
// If the dynamic type is 11111...11111, another goroutine is performing the first set
if uintptr(typ) == ^uintptr(0) {
// Continue the for loop until the first set succeeds
continue
}
// Check whether the old and new values have the same dynamic type; panic if they differ
if typ != xp.typ {
panic("sync/atomic: store of inconsistently typed value into Value")
}
// Set the dynamic value
StorePointer(&vp.data, xp.data)
return
}
}
StorePointer and LoadPointer are atomic operations.
The key is that the first operation needs to set the dynamic type and set the dynamic value. These are two steps, so a lock is needed to guarantee atomicity. Here, CAS sets the dynamic type to unsafe.Pointer(^uintptr(0)) to act as the lock.
The second operation only needs to set the dynamic value, so it no longer needs to CAS the dynamic type to unsafe.Pointer(^uintptr(0)) as a lock.

4.2.2. Load
func (v *Value) Load() (x interface{}) {
vp := (*ifaceWords)(unsafe.Pointer(v))
// Get the dynamic type of the old value
typ := LoadPointer(&vp.typ)
// If the dynamic type is empty, the first set has not happened yet, so return nil
if typ == nil || uintptr(typ) == ^uintptr(0) {
return nil
}
// Get the dynamic value
data := LoadPointer(&vp.data)
// Assign the dynamic value and dynamic type to the new variable xp
xp := (*ifaceWords)(unsafe.Pointer(&x))
xp.typ = typ
xp.data = data
return
}
Discussion
Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub