trpc.group/trpc-go/trpc-go@v1.0.3/internal/ring/ring.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 // Package ring provides a concurrent-safe circular queue, supports multiple read/write. 15 package ring 16 17 import ( 18 "errors" 19 "fmt" 20 "runtime" 21 "sync/atomic" 22 "unsafe" 23 24 "golang.org/x/sys/cpu" 25 ) 26 27 const ( 28 // cacheLinePadSize is the size of CPU cache line. 29 cacheLinePadSize = unsafe.Sizeof(cpu.CacheLinePad{}) 30 ) 31 32 var ( 33 // ErrQueueFull happens when the queue is full. 34 ErrQueueFull = errors.New("queue is full") 35 ) 36 37 type ringItem[T any] struct { 38 putSeq uint32 // sequence number expected to put. 39 getSeq uint32 // sequence number expected to get. 40 value T 41 _ [cacheLinePadSize - 8 - 16]byte 42 } 43 44 // Ring a concurrent-safe circular queue, based on the idea of Disruptor(simplified). 45 // https://lmax-exchange.github.io/disruptor/disruptor.html 46 type Ring[T any] struct { 47 capacity uint32 // the capacity of circular queue, including Empty element, must be the power of 2. 48 mask uint32 // capacity mask of the circular queue. 49 _ [cacheLinePadSize - 8]byte 50 head uint32 // the latest sequence number having been read. 51 _ [cacheLinePadSize - 4]byte 52 tail uint32 // the latest sequence number having been written. 53 _ [cacheLinePadSize - 4]byte 54 data []ringItem[T] // elements in the queue. 55 _ [cacheLinePadSize - unsafe.Sizeof([]ringItem[T]{})]byte 56 } 57 58 // New creates a circular queue. 59 func New[T any](capacity uint32) *Ring[T] { 60 capacity = roundUpToPower2(capacity) 61 if capacity < 2 { 62 capacity = 2 63 } 64 65 r := &Ring[T]{ 66 capacity: capacity, 67 mask: capacity - 1, 68 data: make([]ringItem[T], capacity), 69 } 70 // initialize every slot with read/write sequence number. 71 for i := range r.data { 72 r.data[i].getSeq = uint32(i) 73 r.data[i].putSeq = uint32(i) 74 } 75 // starts from Index=1 to fill the package. 76 r.data[0].getSeq = capacity 77 r.data[0].putSeq = capacity 78 return r 79 } 80 81 // Put puts element into the circular queue. 82 // Directly return if the queue is full. 83 func (r *Ring[T]) Put(val T) error { 84 // acquire put sequence. 85 seq, err := r.acquirePutSequence() 86 if err != nil { 87 return err 88 } 89 // write element. 90 r.commit(seq, val) 91 return nil 92 } 93 94 // Get gets an element from the circular queue, return element value and 95 // the remaining number of elements. 96 func (r *Ring[T]) Get() (T, uint32) { 97 // acquire get sequence. 98 head, size, left := r.acquireGetSequence(1) 99 if size == 0 { 100 var zero T 101 return zero, 0 102 } 103 // read element. 104 return r.consume(head), left 105 } 106 107 // Gets acquires elements from the circular queue and appends then into v, 108 // return the number of elements acquired and remained. 109 func (r *Ring[T]) Gets(val *[]T) (uint32, uint32) { 110 // batch acquire get sequence. 111 head, size, left := r.acquireGetSequence(uint32(cap(*val) - len(*val))) 112 if size == 0 { 113 return 0, 0 114 } 115 // batch read the elements. 116 for seq, i := head, uint32(0); i < size; seq, i = seq+1, i+1 { 117 *val = append(*val, r.consume(seq)) 118 } 119 return size, left 120 } 121 122 // Cap retrieves the number of elements the circular queue can hold. 123 func (r *Ring[T]) Cap() uint32 { 124 // the capacity is represented by mask. 125 return r.mask 126 } 127 128 // Size retrieves the number of elements in the circular queue. 129 func (r *Ring[T]) Size() uint32 { 130 head := atomic.LoadUint32(&r.head) 131 tail := atomic.LoadUint32(&r.tail) 132 return r.quantity(head, tail) 133 } 134 135 // IsEmpty checks whether the queue is empty. 136 func (r *Ring[T]) IsEmpty() bool { 137 head := atomic.LoadUint32(&r.head) 138 tail := atomic.LoadUint32(&r.tail) 139 return head == tail 140 } 141 142 // IsFull checks whether the queue is full. 143 func (r *Ring[T]) IsFull() bool { 144 head := atomic.LoadUint32(&r.head) 145 next := atomic.LoadUint32(&r.tail) + 1 146 return next-head > r.mask 147 } 148 149 // String prints the structure of Ring. 150 func (r *Ring[T]) String() string { 151 head := atomic.LoadUint32(&r.head) 152 tail := atomic.LoadUint32(&r.tail) 153 return fmt.Sprintf("Ring: Cap=%v, Head=%v, Tail=%v, Size=%v\n", 154 r.Cap(), head, tail, r.Size()) 155 } 156 157 func (r *Ring[T]) quantity(head, tail uint32) uint32 { 158 return tail - head 159 } 160 161 func (r *Ring[T]) acquirePutSequence() (uint32, error) { 162 var tail, head, next uint32 163 mask := r.mask 164 for { 165 head = atomic.LoadUint32(&r.head) 166 tail = atomic.LoadUint32(&r.tail) 167 next = tail + 1 168 left := r.quantity(head, next) 169 // the queue is full, return. 170 if left > mask { 171 return 0, ErrQueueFull 172 } 173 // got the sequence number, return. 174 if atomic.CompareAndSwapUint32(&r.tail, tail, next) { 175 return next, nil 176 } 177 // fails to get the sequence number, yields the CPU 178 // to reduce busy loop of CPU. 179 runtime.Gosched() 180 } 181 } 182 183 func (r *Ring[T]) acquireGetSequence(ask uint32) (uint32, uint32, uint32) { 184 var tail, head, size uint32 185 for { 186 head = atomic.LoadUint32(&r.head) 187 tail = atomic.LoadUint32(&r.tail) 188 left := r.quantity(head, tail) 189 // the queue is empty, return. 190 if left < 1 { 191 return head, 0, 0 192 } 193 size = left 194 if ask < left { 195 size = ask 196 } 197 // got the sequence number, return. 198 if atomic.CompareAndSwapUint32(&r.head, head, head+size) { 199 return head + 1, size, left - size 200 } 201 // fails to get the sequence number, yields the CPU 202 // to reduce busy loop of CPU. 203 runtime.Gosched() 204 } 205 } 206 207 func (r *Ring[T]) commit(seq uint32, val T) { 208 item := &r.data[seq&r.mask] 209 for { 210 getSeq := atomic.LoadUint32(&item.getSeq) 211 putSeq := atomic.LoadUint32(&item.putSeq) 212 // Waiting for data to be ready for writing. Due to the separation of 213 // obtaining the right to use the sequence number and reading and writing 214 // data operations, there is a short period of time that the old data has 215 // not been read, wait for the read operation to complete and set getSeq. 216 if seq == putSeq && getSeq == putSeq { 217 break 218 } 219 runtime.Gosched() 220 } 221 // Complete the write operation and set putSeq to the next expected write sequence number. 222 item.value = val 223 atomic.AddUint32(&item.putSeq, r.capacity) 224 } 225 226 func (r *Ring[T]) consume(seq uint32) T { 227 item := &r.data[seq&r.mask] 228 for { 229 getSeq := atomic.LoadUint32(&item.getSeq) 230 putSeq := atomic.LoadUint32(&item.putSeq) 231 // Waiting for data to be ready to read. Due to the separation of 232 // obtaining the right to use the sequence number and reading and writing 233 // data operations, there is a short period of time that the writing data has 234 // not been written yet, wait for the writing operation to complete and set putSeq. 235 if seq == getSeq && getSeq == (putSeq-r.capacity) { 236 break 237 } 238 runtime.Gosched() 239 } 240 // Complete the read operation and set getSeq to the next expected read sequence number. 241 val := item.value 242 var zero T 243 item.value = zero 244 atomic.AddUint32(&item.getSeq, r.capacity) 245 return val 246 } 247 248 // roundUpToPower2 rounds the integer up to the Nth power of 2. 249 func roundUpToPower2(v uint32) uint32 { 250 v-- 251 v |= v >> 1 252 v |= v >> 2 253 v |= v >> 4 254 v |= v >> 8 255 v |= v >> 16 256 v++ 257 return v 258 }