atomic.Value

atomic.Value

在正式介绍atomic.Value​前, 先介绍一个基本知识, 任何Interface, 在Go中都会表示为Type +Data

package main

import (
	"fmt"
	"unsafe"
)

type A struct {
	v int64
}

type eStruct struct {
	typ  unsafe.Pointer
	data unsafe.Pointer
}

func getAnyAndPrint(v any) {
	size := unsafe.Sizeof(v)
	println("in getAnyAndPrint: ", size)

	realData := (*eStruct)(unsafe.Pointer(&v))
	println("this is Type Pointer", realData.typ)
	println("this is int64 Pointer", realData.data)
	i := (*int64)(realData.data)
	println("this is int64 Value", *i)
}

func main() {
	a := A{
		v: 10,
	}
	size := unsafe.Sizeof(a)
	fmt.Printf("Size of A: %d bytes\n", size)
	getAnyAndPrint(&a)
}

以上是我自己构建的一个case, 想要向你表达两件事:

  1. 当使用interface​时, 实际上是在用Type + Data, 这两者分别占用64 bit.
  2. 可以通过一些hack手段实现直接对typ/data的修改.

请注意eStruct​的使用.

源码注释

package atomic

import (
	"unsafe"
)

type Value struct {
	v any // interface 64bit
}

type efaceWords struct {
	typ  unsafe.Pointer // interface Type
	data unsafe.Pointer // interface Data
}

func (v *Value) Load() (val any) {
	vp := (*efaceWords)(unsafe.Pointer(v)) // 获取的数据: Type + Data
	typ := LoadPointer(&vp.typ) // 获取Type
	if typ == nil || typ == unsafe.Pointer(&firstStoreInProgress) {
		// 如果type 为nil, 表示此时刚刚初始化, 还没有调用Store
		// 如果type 为&firstStoreInProgress, 表示调用了第一次Store, 但是没有执行完成.
		// 这两者都会导致无法Load, 此时返回nil
		return nil
	}
	data := LoadPointer(&vp.data) // 获取实际的Data
	// 实际进行返回值的复制
	vlp := (*efaceWords)(unsafe.Pointer(&val)) 
	vlp.typ = typ
	vlp.data = data
	return
}

var firstStoreInProgress byte

func (v *Value) Store(val any) {
	// 如果要写入的val是nil, 直接抛出
	if val == nil {
		panic("sync/atomic: store of nil value into Value")
	}
	vp := (*efaceWords)(unsafe.Pointer(v)) // 解析现在的存储的值
	vlp := (*efaceWords)(unsafe.Pointer(&val)) // 解析要写入的数据
	for {
		typ := LoadPointer(&vp.typ) // 原子性的获取当前的指定值
		if typ == nil { // 如果为nil, 表示这是第一次赋值
			runtime_procPin()
			// 尝试占用赋值, 通过修改Type字段实现
			if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) {
				runtime_procUnpin()
				continue
			}
			// 写入Data
			StorePointer(&vp.data, vlp.data)
			// 写入Type: 真实的Type
			StorePointer(&vp.typ, vlp.typ)
			runtime_procUnpin()
			return
		}
		// 如果当前的类型不为nil, 但是是firstStoreInProgress, 表示此时有其他goroutine在处理, 本次循环忽略
		if typ == unsafe.Pointer(&firstStoreInProgress) {
			continue
		}
		// 第一次赋值完成, 检查类型是否变更了, 变更了直接抛错
		if typ != vlp.typ {
			panic("sync/atomic: store of inconsistently typed value into Value")
		}
		// 原子性的更新Data
		StorePointer(&vp.data, vlp.data)
		return
	}
}

解释

其实这不是一段难懂的代码

唯一需要注意的就是efaceWords​的使用.

其他的处理就很简单了, 确认Type​, 处理Type​的初始化, 通过调用StorePointer​实现更新Interface.Data