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  }