mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-25 13:12:11 +02:00
multi: add buffer.Write and pool.WriteBuffer, make GCQueue generic
This commit is contained in:
@@ -8,21 +8,6 @@ import (
|
||||
"github.com/lightningnetwork/lnd/ticker"
|
||||
)
|
||||
|
||||
// Recycler is an interface that allows an object to be reclaimed without
|
||||
// needing to be returned to the runtime.
|
||||
type Recycler interface {
|
||||
// Recycle resets the object to its default state.
|
||||
Recycle()
|
||||
}
|
||||
|
||||
// gcQueueEntry is a tuple containing a Recycler and the time at which the item
|
||||
// was added to the queue. The recorded time is used to determine when the entry
|
||||
// becomes stale, and can be released if it has not already been taken.
|
||||
type gcQueueEntry struct {
|
||||
item Recycler
|
||||
time time.Time
|
||||
}
|
||||
|
||||
// GCQueue is garbage collecting queue, which dynamically grows and contracts
|
||||
// based on load. If the queue has items which have been returned, the queue
|
||||
// will check every gcInterval amount of time to see if any elements are
|
||||
@@ -36,15 +21,15 @@ type gcQueueEntry struct {
|
||||
type GCQueue struct {
|
||||
// takeBuffer coordinates the delivery of items taken from the queue
|
||||
// such that they are delivered to requesters.
|
||||
takeBuffer chan Recycler
|
||||
takeBuffer chan interface{}
|
||||
|
||||
// returnBuffer coordinates the return of items back into the queue,
|
||||
// where they will be kept until retaken or released.
|
||||
returnBuffer chan Recycler
|
||||
returnBuffer chan interface{}
|
||||
|
||||
// newItem is a constructor, used to generate new elements if none are
|
||||
// otherwise available for reuse.
|
||||
newItem func() Recycler
|
||||
newItem func() interface{}
|
||||
|
||||
// expiryInterval is the minimum amount of time an element will remain
|
||||
// in the queue before being released.
|
||||
@@ -75,12 +60,12 @@ type GCQueue struct {
|
||||
// the steady state. The returnQueueSize parameter is used to size the maximal
|
||||
// number of items that can be returned without being dropped during large
|
||||
// bursts in attempts to return items to the GCQUeue.
|
||||
func NewGCQueue(newItem func() Recycler, returnQueueSize int,
|
||||
func NewGCQueue(newItem func() interface{}, returnQueueSize int,
|
||||
gcInterval, expiryInterval time.Duration) *GCQueue {
|
||||
|
||||
q := &GCQueue{
|
||||
takeBuffer: make(chan Recycler),
|
||||
returnBuffer: make(chan Recycler, returnQueueSize),
|
||||
takeBuffer: make(chan interface{}),
|
||||
returnBuffer: make(chan interface{}, returnQueueSize),
|
||||
expiryInterval: expiryInterval,
|
||||
freeList: list.New(),
|
||||
recycleTicker: ticker.New(gcInterval),
|
||||
@@ -95,7 +80,7 @@ func NewGCQueue(newItem func() Recycler, returnQueueSize int,
|
||||
|
||||
// Take returns either a recycled element from the queue, or creates a new item
|
||||
// if none are available.
|
||||
func (q *GCQueue) Take() Recycler {
|
||||
func (q *GCQueue) Take() interface{} {
|
||||
select {
|
||||
case item := <-q.takeBuffer:
|
||||
return item
|
||||
@@ -107,20 +92,21 @@ func (q *GCQueue) Take() Recycler {
|
||||
// Return adds the returned item to freelist if the queue's returnBuffer has
|
||||
// available capacity. Under load, items may be dropped to ensure this method
|
||||
// does not block.
|
||||
func (q *GCQueue) Return(item Recycler) {
|
||||
// Recycle the item to ensure that a dirty instance is never offered
|
||||
// from Take. The call is done here so that the CPU cycles spent
|
||||
// clearing the buffer are owned by the caller, and not by the queue
|
||||
// itself. This makes the queue more likely to be available to deliver
|
||||
// items in the free list.
|
||||
item.Recycle()
|
||||
|
||||
func (q *GCQueue) Return(item interface{}) {
|
||||
select {
|
||||
case q.returnBuffer <- item:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// gcQueueEntry is a tuple containing an interface{} and the time at which the
|
||||
// item was added to the queue. The recorded time is used to determine when the
|
||||
// entry becomes stale, and can be released if it has not already been taken.
|
||||
type gcQueueEntry struct {
|
||||
item interface{}
|
||||
time time.Time
|
||||
}
|
||||
|
||||
// queueManager maintains the free list of elements by popping the head of the
|
||||
// queue when items are needed, and appending them to the end of the queue when
|
||||
// items are returned. The queueManager will periodically attempt to release any
|
||||
@@ -190,20 +176,20 @@ func (q *GCQueue) queueManager() {
|
||||
next = e.Next()
|
||||
entry := e.Value.(gcQueueEntry)
|
||||
|
||||
// Use now - insertTime > expiryInterval to
|
||||
// determine if this entry has expired.
|
||||
if time.Since(entry.time) > q.expiryInterval {
|
||||
// Remove the expired entry from the
|
||||
// linked-list.
|
||||
q.freeList.Remove(e)
|
||||
entry.item = nil
|
||||
e.Value = nil
|
||||
} else {
|
||||
// Use now - insertTime <= expiryInterval to
|
||||
// determine if this entry has not expired.
|
||||
if time.Since(entry.time) <= q.expiryInterval {
|
||||
// If this entry hasn't expired, then
|
||||
// all entries that follow will still be
|
||||
// valid.
|
||||
break
|
||||
}
|
||||
|
||||
// Otherwise, remove the expired entry from the
|
||||
// linked-list.
|
||||
q.freeList.Remove(e)
|
||||
entry.item = nil
|
||||
e.Value = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,10 +7,8 @@ import (
|
||||
"github.com/lightningnetwork/lnd/queue"
|
||||
)
|
||||
|
||||
// mockRecycler implements the queue.Recycler interface using a NOP.
|
||||
type mockRecycler bool
|
||||
|
||||
func (*mockRecycler) Recycle() {}
|
||||
// testItem is an item type we'll be using to test the GCQueue.
|
||||
type testItem uint32
|
||||
|
||||
// TestGCQueueGCCycle asserts that items that are kept in the GCQueue past their
|
||||
// expiration will be released by a subsequent gc cycle.
|
||||
@@ -23,7 +21,7 @@ func TestGCQueueGCCycle(t *testing.T) {
|
||||
numItems = 6
|
||||
)
|
||||
|
||||
newItem := func() queue.Recycler { return new(mockRecycler) }
|
||||
newItem := func() interface{} { return new(testItem) }
|
||||
|
||||
bp := queue.NewGCQueue(newItem, 100, gcInterval, expiryInterval)
|
||||
|
||||
@@ -61,7 +59,7 @@ func TestGCQueuePartialGCCycle(t *testing.T) {
|
||||
numItems = 6
|
||||
)
|
||||
|
||||
newItem := func() queue.Recycler { return new(mockRecycler) }
|
||||
newItem := func() interface{} { return new(testItem) }
|
||||
|
||||
bp := queue.NewGCQueue(newItem, 100, gcInterval, expiryInterval)
|
||||
|
||||
@@ -104,10 +102,10 @@ func TestGCQueuePartialGCCycle(t *testing.T) {
|
||||
|
||||
// takeN draws n items from the provided GCQueue. This method also asserts that
|
||||
// n unique items are drawn, and then returns the resulting set.
|
||||
func takeN(t *testing.T, q *queue.GCQueue, n int) map[queue.Recycler]struct{} {
|
||||
func takeN(t *testing.T, q *queue.GCQueue, n int) map[interface{}]struct{} {
|
||||
t.Helper()
|
||||
|
||||
items := make(map[queue.Recycler]struct{})
|
||||
items := make(map[interface{}]struct{})
|
||||
for i := 0; i < n; i++ {
|
||||
// Wait a small duration to ensure the tests behave reliable,
|
||||
// and don't activate the non-blocking case unintentionally.
|
||||
@@ -125,7 +123,7 @@ func takeN(t *testing.T, q *queue.GCQueue, n int) map[queue.Recycler]struct{} {
|
||||
}
|
||||
|
||||
// returnAll returns the items of the given set back to the GCQueue.
|
||||
func returnAll(q *queue.GCQueue, items map[queue.Recycler]struct{}) {
|
||||
func returnAll(q *queue.GCQueue, items map[interface{}]struct{}) {
|
||||
for item := range items {
|
||||
q.Return(item)
|
||||
|
||||
@@ -138,11 +136,11 @@ func returnAll(q *queue.GCQueue, items map[queue.Recycler]struct{}) {
|
||||
// returnN returns n items at random from the set of items back to the GCQueue.
|
||||
// This method fails if the set's cardinality is smaller than n.
|
||||
func returnN(t *testing.T, q *queue.GCQueue,
|
||||
items map[queue.Recycler]struct{}, n int) map[queue.Recycler]struct{} {
|
||||
items map[interface{}]struct{}, n int) map[interface{}]struct{} {
|
||||
|
||||
t.Helper()
|
||||
|
||||
var remainingItems = make(map[queue.Recycler]struct{})
|
||||
var remainingItems = make(map[interface{}]struct{})
|
||||
var numReturned int
|
||||
for item := range items {
|
||||
if numReturned < n {
|
||||
|
Reference in New Issue
Block a user