Golang的原子操作在sync.atomic里面, 源码位置在go/src/runtime/internal/atomic里面。
接口和介绍请参见: https://golang.org/pkg/sync/atomic
下面我们看源码怎么实现的:
以AMD64的 func Xadd64(ptr *uint64, delta int64) uint64
为例:
ASM实现如下:
TEXT runtime∕internal∕atomic·Xadd64(SB), NOSPLIT, $0-20
// no XADDQ so use CMPXCHG8B loop
MOVL ptr+0(FP), BP
TESTL $7, BP
JZ 2(PC)
MOVL 0, AX // crash when unaligned
// DI:SI = delta
MOVL delta_lo+4(FP), SI
MOVL delta_hi+8(FP), DI
// DX:AX = *addr
MOVL 0(BP), AX
MOVL 4(BP), DX
addloop:
// CX:BX = DX:AX (*addr) + DI:SI (delta)
MOVL AX, BX
MOVL DX, CX
ADDL SI, BX
ADCL DI, CX
// if *addr == DX:AX {
// *addr = CX:BX
// } else {
// DX:AX = *addr
// }
// all in one instruction
LOCK
CMPXCHG8B 0(BP)
JNZ addloop
// success
// return CX:BX
MOVL BX, ret_lo+12(FP)
MOVL CX, ret_hi+16(FP)
RET
怎么能保证这个操作的原子性呢? 单条mov语句是原子的,但是多条不是, LOCK能够保证原子,但是放在了后面,前面怎么保证是原子呢?