type Uint128 struct { d [3]uint64 m sync.Mutex }然后类似标准库 atomic 中对各种整数的操作,它也提供了类似的方法:
func AddUint128(ptr *Uint128, incr [2]uint64) [2]uint64 func CompareAndSwapUint128(ptr *Uint128, old, new [2]uint64) bool func LoadUint128(ptr *Uint128) [2]uint64 func StoreUint128(ptr *Uint128, new [2]uint64) func SwapUint128(ptr *Uint128, new [2]uint64) [2]uint64 func OrUint128(ptr *Uint128, op [2]uint64) [2]uint64 func AndUint128(ptr *Uint128, op [2]uint64) [2]uint64 func XorUint128(ptr *Uint128, op [2]uint64) [2]uint64可以看到,除了正常的 Add、CAS、Load、Store、Swap 函数,还贴心的提供了 Or、And、Xor 三个位操作的函数。
n := &atomic128.Uint128{} v := atomic128.LoadUint128(n) // [2]uint64{0, 0} atomic128.StoreUint128(n, [2]uint64{1, ^uint64(0)}) v = atomic128.LoadUint128(n) // [2]uint64{1, ^uint64(0)} v = AddUint128(n, [2]uint64{2, 40}) v = atomic128.LoadUint128(n) // [2]uint64{3, 40} v = atomic128.SwapUint128(n, [2]uint64{4, 50}) v = atomic128.LoadUint128(n) // [2]uint64{4, 50} v = atomic128.CompareAndSwapUint128(n, [2]uint64{4, 50}, [2]uint64{5, 60}) v = atomic128.LoadUint128(n) // [2]uint64{5, 60} v = atomic128.OrUint128(n, [2]uint64{0, 0}) v = atomic128.LoadUint128(n) // [2]uint64{5, 60}atomic128 的实现
https://go101.org/article/memory-layout.html https://pkg.go.dev/sync/atomic#pkg-note-BUG通过包含三个 Uint64 元素的数组,我们总能通过下面的方法得到 128 位对齐的地址:
func addr(ptr *Uint128) *[2]uint64 { if (uintptr)((unsafe.Pointer)(&ptr.d[0]))%16 == 0 { // 指针已经128位对齐 return (*[2]uint64)((unsafe.Pointer)(&ptr.d[0])) } return (*[2]uint64)((unsafe.Pointer)(&ptr.d[1])) // 必然ptr.d[1]是128位对齐的 (AMD64架构) }通过变量useNativeAmd64判断 CPU 是否支持CMPXCHG16B指令:
func init() { useNativeAmd64 = cpuid.CPU.Supports(cpuid.CX16) }如果不支持,回退到使用 Mutex 实现一个低效的 atomic 128bit 原子操作:
func CompareAndSwapUint128(ptr *Uint128, old, new [2]uint64) bool { if runtime.GOARCH == "amd64" && useNativeAmd64 { return compareAndSwapUint128amd64(addr(ptr), old, new) } // 不支持CMPXCHG16B指令,使用Mutex ptr.m.Lock() v := load(ptr) if v != old { ptr.m.Unlock() return false } store(ptr, new) ptr.m.Unlock() return true }如果支持CMPXCHG16B指令,直接调用compareAndSwapUint128amd64函数:
TEXT ·compareAndSwapUint128amd64(SB),NOSPLIT,$0 MOVQ addr+0(FP), BP MOVQ old+8(FP), AX MOVQ old+16(FP), DX MOVQ new+24(FP), BX MOVQ new+32(FP), CX LOCK CMPXCHG16B (BP) SETEQ swapped+40(FP) RET主要依赖CMPXCHG16B实现。
根据比较结果,设置相应的标志位。