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  }