Understanding Go's atomic.Value Implementation
Interface Representation in Go
In Go, interface are represented as a combination of type and data pointers:
<code>package main
import (
"fmt"
"unsafe"
)
type CustomType struct {
num int64
}
type InterfaceRep struct {
typePtr unsafe.Pointer
dataPtr unsafe.Pointer
}
func inspectInterface(val interface{}) {
size := unsafe.Sizeof(val)
fmt.Println("Interface size:", size)
rep := (*InterfaceRep)(unsafe.Pointer(&val))
fmt.Println("Type pointer:", rep.typePtr)
fmt.Println("Data pointer:", rep.dataPtr)
value := (*int64)(rep.dataPtr)
fmt.Println("Stored value:", *value)
}
func main() {
ct := CustomType{num: 42}
inspectInterface(&ct)
}</code>
This demonstrates two key points:
- Interfaces use 128 bits (64 bits for type pointer + 64 bits for data pointer)
- We can access interface internals through unsafe pointer operations
atomic.Value Source Analysis
The core implementation uses pointer operations for atomic access:
<code>type Value struct {
val interface{}
}
type interfaceRep struct {
typePtr unsafe.Pointer
dataPtr unsafe.Pointer
}
func (v *Value) Load() interface{} {
rep := (*interfaceRep)(unsafe.Pointer(v))
typ := LoadPointer(&rep.typePtr)
if typ == nil || typ == unsafe.Pointer(&initialStoreFlag) {
return nil
}
data := LoadPointer(&rep.dataPtr)
result := new(interfaceRep)
result.typePtr = typ
result.dataPtr = data
return *(*interface{})(unsafe.Pointer(result))
}
var initialStoreFlag byte
func (v *Value) Store(newVal interface{}) {
if newVal == nil {
panic("atomic.Value: cannot store nil")
}
target := (*interfaceRep)(unsafe.Pointer(v))
source := (*interfaceRep)(unsafe.Pointer(&newVal))
for {
currentType := LoadPointer(&target.typePtr)
if currentType == nil {
runtime_procPin()
if !CompareAndSwapPointer(&target.typePtr, nil, unsafe.Pointer(&initialStoreFlag)) {
runtime_procUnpin()
continue
}
StorePointer(&target.dataPtr, source.dataPtr)
StorePointer(&target.typePtr, source.typePtr)
runtime_procUnpin()
return
}
if currentType == unsafe.Pointer(&initialStoreFlag) {
continue
}
if currentType != source.typePtr {
panic("atomic.Value: type mismatch during store")
}
StorePointer(&target.dataPtr, source.dataPtr)
return
}
}</code>
Implementation Explanation
The key aspects of the implemantation:
interfaceRepmirrors Go's internal interface representationLoadperforms atomic pointer reads and reconstructs the interfaceStoreuses CAS for initialization and enforces type consistency- The
initialStoreFlagmarks ongoing first-write operations - Runtime pinning prevents preemption during critical setcions