github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/queue/ring_buffer_cursor.go (about) 1 package queue 2 3 import ( 4 "sync/atomic" 5 "unsafe" 6 7 "golang.org/x/sys/cpu" 8 ) 9 10 // 2 ways for lock-free programming 11 // Ref https://hedzr.com/golang/nolock/two-nolock-skills-in-go/ 12 // 1. Structure entry mode: always copy the whole structure (independent element), for example, golang slog. 13 // 2. Bumper loop mode: force to synchronize the whole process. Only works for low concurrency. 14 // We have to avoid a slow handle process which will result in block next handle process, 15 // events heap up or event loss. 16 // For example, golang server/client conn. 17 // Bumper loop mode is appropriate for event distribution. Low concurrency means that 18 // the frequency is higher than 80ms per event. 19 20 var ( 21 _ RingBufferCursor = (*rbCursor)(nil) 22 ) 23 24 const cacheLinePadSize = unsafe.Sizeof(cpu.CacheLinePad{}) 25 26 // rbCursor is a cursor for xRingBuffer. 27 // Only increase, if it overflows, it will be reset to 0. 28 // Occupy a whole cache line (flag+tag+data), and a cache line data is 64 bytes. 29 // L1D cache: cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size 30 // L1I cache: cat /sys/devices/system/cpu/cpu0/cache/index1/coherency_line_size 31 // L2 cache: cat /sys/devices/system/cpu/cpu0/cache/index2/coherency_line_size 32 // L3 cache: cat /sys/devices/system/cpu/cpu0/cache/index3/coherency_line_size 33 // MESI (Modified-Exclusive-Shared-Invalid) 34 // RAM data -> L3 cache -> L2 cache -> L1 cache -> CPU register. 35 // CPU register (cache hit) -> L1 cache -> L2 cache -> L3 cache -> RAM data. 36 type rbCursor struct { 37 // sequence consistency data race free program 38 // avoid load into cpu cache will be broken by others data 39 // to compose a data race cache line 40 _ [cacheLinePadSize - unsafe.Sizeof(*new(uint64))]byte // padding for CPU cache line, avoid false sharing 41 val uint64 // space waste to exchange for performance 42 _ [cacheLinePadSize - unsafe.Sizeof(*new(uint64))]byte // padding for CPU cache line, avoid false sharing 43 } 44 45 func NewXRingBufferCursor() RingBufferCursor { 46 return &rbCursor{} 47 } 48 49 func (c *rbCursor) Next() uint64 { 50 // Golang atomic store with LOCK prefix, it means that 51 // it implements the Happens-Before relationship. 52 // But it is not clearly that atomic add satisfies the 53 // Happens-Before relationship. 54 // https://go.dev/ref/mem 55 return atomic.AddUint64(&c.val, 1) 56 } 57 58 func (c *rbCursor) NextN(n uint64) uint64 { 59 return atomic.AddUint64(&c.val, n) 60 } 61 62 func (c *rbCursor) Load() uint64 { 63 // Golang atomic load does not promise the Happens-Before 64 return atomic.LoadUint64(&c.val) 65 } 66 67 func (c *rbCursor) CompareAndSwap(old, new uint64) bool { 68 return atomic.CompareAndSwapUint64(&c.val, old, new) 69 }