A little bit about atomic operations

The initial Sync /atomic support for Go was just a few of the basic data types, until the Go 1.4 release, when the Go language added the Value type, allowing atomic to read and store any type of data

Mentioned atomic operations will have to mention a thing – the lock, the history of this in my article reading notes 】 【 go source code – Sync. There are mentioned in the Mutex, interested readers can first move to read, so atomic operation and what is the difference between a lock, lock is first of the operating system level, and atomic operation is provided by the underlying hardware support, In high concurrency scenarios, atomic operations perform much better than locking because they can be lock free

The source code parsing

Before looking at the source code, we first need to understand the definition of structures in atomic

// A Value provides an atomic load and store of a consistently typed value.
// The zero value for a Value returns nil from Load.
// Once Store has been called, a Value must not be copied.
//
// A Value must not be copied after first use.
// Value is the structure that is stored and read
type Value struct {
   v interface{}}// ifaceWords is interface{} internal representation.
// Its function is to disassemble the v in the Value structure, and disassemble it into TYP and data
type ifaceWords struct {
   typ  unsafe.Pointer
   data unsafe.Pointer
}
Copy the code

Then we’ll look at the two operations that have to do with atomic, Load and Store

Storage (Store)

// Store sets the value of the Value to x.
// All calls to Store for a given Value must use values of the same concrete type.
// Store of an inconsistent type panics, as does Store(nil).
func (v *Value) Store(x interface{}) {
   // If the storage element is empty, panic is thrown
   if x == nil {
      panic("sync/atomic: store of nil value into Value")
   }
   vp := (*ifaceWords)(unsafe.Pointer(v))    // The old value is broken up by ifaceWords
   xp := (*ifaceWords)(unsafe.Pointer(&x))   // The new value is decomposed by ifaceWords
   for {
      typ := LoadPointer(&vp.typ)            // Get the type of element to store
      if typ == nil {                        // If the type is null, it is stored for the first time
         // Attempt to start first store.
         // Disable preemption so that other goroutines can use
         // active spin wait to wait for completion; and so that
         // GC does not see the fake type accidentally.
         // The current goroutine takes precedence over the CPU
         runtime_procPin()                   
         // There is a CAS operation, that is, multiple goroutines compete, if no successful assignment, the current goroutine
         // Remove control of the CPU and compete with other goroutines
         if! CompareAndSwapPointer(&vp.typ,nil, unsafe.Pointer(^uintptr(0))) {
            runtime_procUnpin()
            continue
         }
         // Complete first store.
         // Store the type and data for the first time
         StorePointer(&vp.data, xp.data)
         StorePointer(&vp.typ, xp.typ)
         // Remove the control of the CPU
         runtime_procUnpin()
         return
      }
      // If the current type is in the intermediate state, the assignment has not been completed, and the loop continues
      if uintptr(typ) == ^uintptr(0) {
         // First store in progress. Wait.
         // Since we disable preemption around the first store,
         // we can wait with active spinning.
         continue
      }
      // First store completed. Check type and overwrite data.
      // If the type does not match the new type to be stored, panic is thrown
      iftyp ! = xp.typ {panic("sync/atomic: store of inconsistently typed value into Value")}// If the type is the same, change the value of data directly
      StorePointer(&vp.data, xp.data)
      return}}Copy the code

Read (Load)

// Load returns the value set by the most recent Store.
// It returns nil if there has been no call to Store for this Value.
func (v *Value) Load(a) (x interface{}) {
   // Get the type of the element currently stored, and return null if the type is null or the type is in the intermediate state
   vp := (*ifaceWords)(unsafe.Pointer(v))
   typ := LoadPointer(&vp.typ)
   if typ == nil || uintptr(typ) == ^uintptr(0) {
      // First store not yet completed.
      return nil
   }
   // Get data and assign it to x
   data := LoadPointer(&vp.data)
   xp := (*ifaceWords)(unsafe.Pointer(&x))
   xp.typ = typ
   xp.data = data
   return
}
Copy the code