github.com/koko1123/flow-go-1@v0.29.6/engine/common/fifoqueue/fifoqueue.go (about) 1 package fifoqueue 2 3 import ( 4 "fmt" 5 mathbits "math/bits" 6 "sync" 7 8 "github.com/ef-ds/deque" 9 ) 10 11 // CapacityUnlimited specifies the largest possible capacity for a FifoQueue. 12 // maximum value for platform-specific int: https://yourbasic.org/golang/max-min-int-uint/ 13 const CapacityUnlimited = 1<<(mathbits.UintSize-1) - 1 14 15 // FifoQueue implements a FIFO queue with max capacity and length observer. 16 // Elements that exceeds the queue's max capacity are silently dropped. 17 // By default, the theoretical capacity equals to the largest `int` value 18 // (platform dependent). Capacity can be set at construction time via the 19 // option `WithCapacity`. 20 // Each time the queue's length changes, the QueueLengthObserver is called 21 // with the new length. By default, the QueueLengthObserver is a NoOp. 22 // A single QueueLengthObserver can be set at construction time via the 23 // option `WithLengthObserver`. 24 // 25 // Caution: 26 // * the QueueLengthObserver must be non-blocking 27 type FifoQueue struct { 28 mu sync.RWMutex 29 queue deque.Deque 30 maxCapacity int 31 lengthObserver QueueLengthObserver 32 } 33 34 // ConstructorOptions are optional arguments for the `NewFifoQueue` 35 // constructor to specify properties of the FifoQueue. 36 type ConstructorOption func(*FifoQueue) error 37 38 // QueueLengthObserver is a optional callback that can be provided to 39 // the `NewFifoQueue` constructor (via `WithLengthObserver` option). 40 type QueueLengthObserver func(int) 41 42 // WithLengthObserver is a constructor option for NewFifoQueue. Each time the 43 // queue's length changes, the queue calls the provided callback with the new 44 // length. By default, the QueueLengthObserver is a NoOp. 45 // CAUTION: 46 // - QueueLengthObserver implementations must be non-blocking 47 // - The values published to queue length observer might be in different order 48 // than the actual length values at the time of insertion. This is a 49 // performance optimization, which allows to reduce the duration during 50 // which the queue is internally locked when inserting elements. 51 func WithLengthObserver(callback QueueLengthObserver) ConstructorOption { 52 return func(queue *FifoQueue) error { 53 if callback == nil { 54 return fmt.Errorf("nil is not a valid QueueLengthObserver") 55 } 56 queue.lengthObserver = callback 57 return nil 58 } 59 } 60 61 // NewFifoQueue is the Constructor for FifoQueue 62 func NewFifoQueue(maxCapacity int, options ...ConstructorOption) (*FifoQueue, error) { 63 if maxCapacity < 1 { 64 return nil, fmt.Errorf("capacity for Fifo queue must be positive") 65 } 66 67 queue := &FifoQueue{ 68 maxCapacity: maxCapacity, 69 lengthObserver: func(int) { /* noop */ }, 70 } 71 for _, opt := range options { 72 err := opt(queue) 73 if err != nil { 74 return nil, fmt.Errorf("failed to apply constructor option to fifoqueue queue: %w", err) 75 } 76 } 77 return queue, nil 78 } 79 80 // Push appends the given value to the tail of the queue. 81 // If queue capacity is reached, the message is silently dropped. 82 func (q *FifoQueue) Push(element interface{}) bool { 83 length, pushed := q.push(element) 84 85 if pushed { 86 q.lengthObserver(length) 87 } 88 return pushed 89 } 90 91 func (q *FifoQueue) push(element interface{}) (int, bool) { 92 q.mu.Lock() 93 defer q.mu.Unlock() 94 95 length := q.queue.Len() 96 if length < q.maxCapacity { 97 q.queue.PushBack(element) 98 return q.queue.Len(), true 99 } 100 return length, false 101 } 102 103 // Front peeks message at the head of the queue (without removing the head). 104 func (q *FifoQueue) Head() (interface{}, bool) { 105 q.mu.RLock() 106 defer q.mu.RUnlock() 107 108 return q.queue.Front() 109 } 110 111 // Pop removes and returns the queue's head element. 112 // If the queue is empty, (nil, false) is returned. 113 func (q *FifoQueue) Pop() (interface{}, bool) { 114 event, length, ok := q.pop() 115 if !ok { 116 return nil, false 117 } 118 119 q.lengthObserver(length) 120 return event, true 121 } 122 123 func (q *FifoQueue) pop() (interface{}, int, bool) { 124 q.mu.Lock() 125 defer q.mu.Unlock() 126 127 event, ok := q.queue.PopFront() 128 return event, q.queue.Len(), ok 129 } 130 131 // Len returns the current length of the queue. 132 func (q *FifoQueue) Len() int { 133 q.mu.RLock() 134 defer q.mu.RUnlock() 135 136 return q.queue.Len() 137 }