github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/sync/poolqueue.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package sync 6 7 import ( 8 "sync/atomic" 9 "unsafe" 10 ) 11 12 // poolDequeue is a lock-free fixed-size single-producer, 13 // multi-consumer queue. The single producer can both push and pop 14 // from the head, and consumers can pop from the tail. 15 // 16 // It has the added feature that it nils out unused slots to avoid 17 // unnecessary retention of objects. This is important for sync.Pool, 18 // but not typically a property considered in the literature. 19 type poolDequeue struct { 20 // headTail packs together a 32-bit head index and a 32-bit 21 // tail index. Both are indexes into vals modulo len(vals)-1. 22 // 23 // tail = index of oldest data in queue 24 // head = index of next slot to fill 25 // 26 // Slots in the range [tail, head) are owned by consumers. 27 // A consumer continues to own a slot outside this range until 28 // it nils the slot, at which point ownership passes to the 29 // producer. 30 // 31 // The head index is stored in the most-significant bits so 32 // that we can atomically add to it and the overflow is 33 // harmless. 34 // headTail 高32位保存的是head index 35 // 低32位保存的是tail index 36 headTail uint64 37 38 // vals is a ring buffer of interface{} values stored in this 39 // dequeue. The size of this must be a power of 2. 40 // 41 // vals[i].typ is nil if the slot is empty and non-nil 42 // otherwise. A slot is still in use until *both* the tail 43 // index has moved beyond it and typ has been set to nil. This 44 // is set to nil atomically by the consumer and read 45 // atomically by the producer. 46 // vals 是一个ring buffer的双端队列, size 必须是2的倍数 47 vals []eface 48 } 49 50 type eface struct { 51 typ, val unsafe.Pointer 52 } 53 54 const dequeueBits = 32 55 56 // dequeueLimit is the maximum size of a poolDequeue. 57 // 58 // This must be at most (1<<dequeueBits)/2 because detecting fullness 59 // depends on wrapping around the ring buffer without wrapping around 60 // the index. We divide by 4 so this fits in an int on 32-bit. 61 const dequeueLimit = (1 << dequeueBits) / 4 62 63 // dequeueNil is used in poolDequeue to represent interface{}(nil). 64 // Since we use nil to represent empty slots, we need a sentinel value 65 // to represent nil. 66 // 用来表示 nil 67 type dequeueNil *struct{} 68 69 func (d *poolDequeue) unpack(ptrs uint64) (head, tail uint32) { 70 const mask = 1<<dequeueBits - 1 71 head = uint32((ptrs >> dequeueBits) & mask) 72 tail = uint32(ptrs & mask) 73 return 74 } 75 76 func (d *poolDequeue) pack(head, tail uint32) uint64 { 77 const mask = 1<<dequeueBits - 1 78 return (uint64(head) << dequeueBits) | 79 uint64(tail&mask) 80 } 81 82 // pushHead adds val at the head of the queue. It returns false if the 83 // queue is full. It must only be called by a single producer. 84 func (d *poolDequeue) pushHead(val interface{}) bool { 85 ptrs := atomic.LoadUint64(&d.headTail) 86 head, tail := d.unpack(ptrs) 87 if (tail+uint32(len(d.vals)))&(1<<dequeueBits-1) == head { 88 // Queue is full. 89 return false 90 } 91 slot := &d.vals[head&uint32(len(d.vals)-1)] 92 93 // Check if the head slot has been released by popTail. 94 typ := atomic.LoadPointer(&slot.typ) 95 if typ != nil { 96 // Another goroutine is still cleaning up the tail, so 97 // the queue is actually still full. 98 return false 99 } 100 101 // The head slot is free, so we own it. 102 if val == nil { 103 val = dequeueNil(nil) 104 } 105 *(*interface{})(unsafe.Pointer(slot)) = val 106 107 // Increment head. This passes ownership of slot to popTail 108 // and acts as a store barrier for writing the slot. 109 atomic.AddUint64(&d.headTail, 1<<dequeueBits) 110 return true 111 } 112 113 // popHead removes and returns the element at the head of the queue. 114 // It returns false if the queue is empty. It must only be called by a 115 // single producer. 116 func (d *poolDequeue) popHead() (interface{}, bool) { 117 var slot *eface 118 for { 119 ptrs := atomic.LoadUint64(&d.headTail) 120 head, tail := d.unpack(ptrs) 121 if tail == head { 122 // Queue is empty. 123 return nil, false 124 } 125 126 // Confirm tail and decrement head. We do this before 127 // reading the value to take back ownership of this 128 // slot. 129 head-- 130 ptrs2 := d.pack(head, tail) 131 // 如果多个 P 同时访问 ring buffer,在没有任何并发措施的情况下,两个 P 都可能会拿到对象,这肯定是不符合预期的。 132 // 在不引入 Mutex 锁的前提下,sync.Pool 是怎么实现的呢? 133 // sync.Pool 利用了 atomic 包中的 CAS 操作。 134 // 两个 P 都可能会拿到对象,但在最终设置 headTail 的时候, 135 // 只会有一个 P 调用 CAS 成功,另外一个 CAS 失败。 136 if atomic.CompareAndSwapUint64(&d.headTail, ptrs, ptrs2) { 137 // We successfully took back slot. 138 slot = &d.vals[head&uint32(len(d.vals)-1)] 139 break 140 } 141 } 142 143 val := *(*interface{})(unsafe.Pointer(slot)) 144 if val == dequeueNil(nil) { 145 val = nil 146 } 147 // Zero the slot. Unlike popTail, this isn't racing with 148 // pushHead, so we don't need to be careful here. 149 *slot = eface{} 150 return val, true 151 } 152 153 // popTail removes and returns the element at the tail of the queue. 154 // It returns false if the queue is empty. It may be called by any 155 // number of consumers. 156 func (d *poolDequeue) popTail() (interface{}, bool) { 157 var slot *eface 158 for { 159 ptrs := atomic.LoadUint64(&d.headTail) 160 head, tail := d.unpack(ptrs) 161 if tail == head { 162 // Queue is empty. 163 return nil, false 164 } 165 166 // Confirm head and tail (for our speculative check 167 // above) and increment tail. If this succeeds, then 168 // we own the slot at tail. 169 ptrs2 := d.pack(head, tail+1) 170 if atomic.CompareAndSwapUint64(&d.headTail, ptrs, ptrs2) { 171 // Success. 172 slot = &d.vals[tail&uint32(len(d.vals)-1)] 173 break 174 } 175 } 176 177 // We now own slot. 178 val := *(*interface{})(unsafe.Pointer(slot)) 179 if val == dequeueNil(nil) { 180 val = nil 181 } 182 183 // Tell pushHead that we're done with this slot. Zeroing the 184 // slot is also important so we don't leave behind references 185 // that could keep this object live longer than necessary. 186 // 187 // We write to val first and then publish that we're done with 188 // this slot by atomically writing to typ. 189 slot.val = nil 190 atomic.StorePointer(&slot.typ, nil) 191 // At this point pushHead owns the slot. 192 193 return val, true 194 } 195 196 // poolChain is a dynamically-sized version of poolDequeue. 197 // 198 // This is implemented as a doubly-linked list queue of poolDequeues 199 // where each dequeue is double the size of the previous one. Once a 200 // dequeue fills up, this allocates a new one and only ever pushes to 201 // the latest dequeue. Pops happen from the other end of the list and 202 // once a dequeue is exhausted, it gets removed from the list. 203 // poolChain 是一个动态大小的双端队列 204 type poolChain struct { 205 // head is the poolDequeue to push to. This is only accessed 206 // by the producer, so doesn't need to be synchronized. 207 head *poolChainElt 208 209 // tail is the poolDequeue to popTail from. This is accessed 210 // by consumers, so reads and writes must be atomic. 211 tail *poolChainElt 212 } 213 214 type poolChainElt struct { 215 poolDequeue 216 217 // next and prev link to the adjacent poolChainElts in this 218 // poolChain. 219 // 220 // next is written atomically by the producer and read 221 // atomically by the consumer. It only transitions from nil to 222 // non-nil. 223 // 224 // prev is written atomically by the consumer and read 225 // atomically by the producer. It only transitions from 226 // non-nil to nil. 227 next, prev *poolChainElt 228 } 229 230 func storePoolChainElt(pp **poolChainElt, v *poolChainElt) { 231 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(pp)), unsafe.Pointer(v)) 232 } 233 234 func loadPoolChainElt(pp **poolChainElt) *poolChainElt { 235 return (*poolChainElt)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(pp)))) 236 } 237 238 func (c *poolChain) pushHead(val interface{}) { 239 d := c.head 240 if d == nil { 241 // Initialize the chain. 242 const initSize = 8 // Must be a power of 2 243 d = new(poolChainElt) 244 d.vals = make([]eface, initSize) 245 c.head = d 246 storePoolChainElt(&c.tail, d) 247 } 248 249 if d.pushHead(val) { // 250 return 251 } 252 253 // The current dequeue is full. Allocate a new one of twice 254 // the size. 255 // 如果buffer 满了, 下面就会新建一个 2倍Head的poolDequeue 256 newSize := len(d.vals) * 2 257 if newSize >= dequeueLimit { 258 // Can't make it any bigger. 259 newSize = dequeueLimit 260 } 261 262 d2 := &poolChainElt{prev: d} 263 d2.vals = make([]eface, newSize) 264 c.head = d2 265 storePoolChainElt(&d.next, d2) 266 d2.pushHead(val) 267 } 268 269 func (c *poolChain) popHead() (interface{}, bool) { 270 d := c.head 271 for d != nil { 272 if val, ok := d.popHead(); ok { 273 return val, ok 274 } 275 // There may still be unconsumed elements in the 276 // previous dequeue, so try backing up. 277 d = loadPoolChainElt(&d.prev) 278 } 279 return nil, false 280 } 281 282 func (c *poolChain) popTail() (interface{}, bool) { 283 d := loadPoolChainElt(&c.tail) 284 if d == nil { 285 return nil, false 286 } 287 288 for { 289 // It's important that we load the next pointer 290 // *before* popping the tail. In general, d may be 291 // transiently empty, but if next is non-nil before 292 // the pop and the pop fails, then d is permanently 293 // empty, which is the only condition under which it's 294 // safe to drop d from the chain. 295 d2 := loadPoolChainElt(&d.next) 296 297 if val, ok := d.popTail(); ok { 298 return val, ok 299 } 300 301 if d2 == nil { 302 // This is the only dequeue. It's empty right 303 // now, but could be pushed to in the future. 304 return nil, false 305 } 306 307 // The tail of the chain has been drained, so move on 308 // to the next dequeue. Try to drop it from the chain 309 // so the next pop doesn't have to look at the empty 310 // dequeue again. 311 if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&c.tail)), unsafe.Pointer(d), unsafe.Pointer(d2)) { 312 // We won the race. Clear the prev pointer so 313 // the garbage collector can collect the empty 314 // dequeue and so popHead doesn't back up 315 // further than necessary. 316 storePoolChainElt(&d2.prev, nil) 317 } 318 d = d2 319 } 320 }