github.com/prysmaticlabs/prysm@v1.4.4/shared/queue/priority_queue.go (about) 1 // Copyright 2016 The Hashicorp vault Authors. 2 // This file is part of the Hashicorp vault library. 3 4 // Package queue provides Vault plugins with a Priority Queue. It can be used 5 // as an in-memory list of queue.Item sorted by their priority, and offers 6 // methods to find or remove items by their key. Internally it uses 7 // container/heap; see Example Priority Queue: 8 // https://golang.org/pkg/container/heap/#example__priorityQueue 9 package queue 10 11 import ( 12 "container/heap" 13 "errors" 14 "sync" 15 ) 16 17 // ErrEmpty is returned for queues with no items 18 var ErrEmpty = errors.New("queue is empty") 19 20 // ErrDuplicateItem is returned when the queue attmepts to push an item to a key that 21 // already exists. The queue does not attempt to update, instead returns this 22 // error. If an Item needs to be updated or replaced, pop the item first. 23 var ErrDuplicateItem = errors.New("duplicate item") 24 25 // New initializes the internal data structures and returns a new 26 // PriorityQueue 27 func New() *PriorityQueue { 28 pq := PriorityQueue{ 29 data: make(queue, 0), 30 dataMap: make(map[string]*Item), 31 } 32 heap.Init(&pq.data) 33 return &pq 34 } 35 36 // PriorityQueue facilitates queue of Items, providing Push, Pop, and 37 // PopByKey convenience methods. The ordering (priority) is an int64 value 38 // with the smallest value is the highest priority. PriorityQueue maintains both 39 // an internal slice for the queue as well as a map of the same items with their 40 // keys as the index. This enables users to find specific items by key. The map 41 // must be kept in sync with the data slice. 42 // See https://golang.org/pkg/container/heap/#example__priorityQueue 43 type PriorityQueue struct { 44 // data is the internal structure that holds the queue, and is operated on by 45 // heap functions 46 data queue 47 48 // dataMap represents all the items in the queue, with unique indexes, used 49 // for finding specific items. dataMap is kept in sync with the data slice 50 dataMap map[string]*Item 51 52 // lock is a read/write mutex, and used to facilitate read/write locks on the 53 // data and dataMap fields 54 lock sync.RWMutex 55 } 56 57 // queue is the internal data structure used to satisfy heap.Interface. This 58 // prevents users from calling Pop and Push heap methods directly 59 type queue []*Item 60 61 // Item is something managed in the priority queue 62 type Item struct { 63 // Key is a unique string used to identify items in the internal data map 64 Key string 65 // Value is an unspecified type that implementations can use to store 66 // information 67 Value interface{} 68 69 // Priority determines ordering in the queue, with the lowest value being the 70 // highest priority 71 Priority int64 72 73 // index is an internal value used by the heap package, and should not be 74 // modified by any consumer of the priority queue 75 index int 76 } 77 78 // Len returns the count of items in the Priority Queue 79 func (pq *PriorityQueue) Len() int { 80 pq.lock.RLock() 81 defer pq.lock.RUnlock() 82 return pq.data.Len() 83 } 84 85 // Pop pops the highest priority item from the queue. This is a 86 // wrapper/convenience method that calls heap.Pop, so consumers do not need to 87 // invoke heap functions directly 88 func (pq *PriorityQueue) Pop() (*Item, error) { 89 if pq.Len() == 0 { 90 return nil, ErrEmpty 91 } 92 93 pq.lock.Lock() 94 defer pq.lock.Unlock() 95 96 item, ok := heap.Pop(&pq.data).(*Item) 97 if !ok { 98 return nil, errors.New("unknown type") 99 } 100 delete(pq.dataMap, item.Key) 101 return item, nil 102 } 103 104 // Push pushes an item on to the queue. This is a wrapper/convenience 105 // method that calls heap.Push, so consumers do not need to invoke heap 106 // functions directly. Items must have unique Keys, and Items in the queue 107 // cannot be updated. To modify an Item, users must first remove it and re-push 108 // it after modifications. Item does not get copied before pushing on to the queue, 109 // it's up to the caller to copy the item. 110 func (pq *PriorityQueue) Push(i *Item) error { 111 if i == nil || i.Key == "" { 112 return errors.New("error adding item: Item Key is required") 113 } 114 115 pq.lock.Lock() 116 defer pq.lock.Unlock() 117 118 if _, ok := pq.dataMap[i.Key]; ok { 119 return ErrDuplicateItem 120 } 121 122 pq.dataMap[i.Key] = i 123 heap.Push(&pq.data, i) 124 return nil 125 } 126 127 // PopByKey searches the queue for an item with the given key and removes it 128 // from the queue if found. Returns nil if not found. This method must fix the 129 // queue after removing any key. 130 func (pq *PriorityQueue) PopByKey(key string) (*Item, error) { 131 pq.lock.Lock() 132 defer pq.lock.Unlock() 133 134 item, ok := pq.dataMap[key] 135 if !ok { 136 return nil, nil 137 } 138 139 // Remove the item the heap and delete it from the dataMap 140 itemRaw := heap.Remove(&pq.data, item.index) 141 delete(pq.dataMap, key) 142 143 if itemRaw != nil { 144 if i, ok := itemRaw.(*Item); ok { 145 return i, nil 146 } 147 } 148 149 return nil, nil 150 } 151 152 // Len returns the number of items in the queue data structure. Do not use this 153 // method directly on the queue, use PriorityQueue.Len() instead. 154 func (q queue) Len() int { return len(q) } 155 156 // Less returns whether the Item with index i should sort before the Item with 157 // index j in the queue. This method is used by the queue to determine priority 158 // internally; the Item with the lower value wins. (priority zero is higher 159 // priority than 1). The priority of Items with equal values is undetermined. 160 func (q queue) Less(i, j int) bool { 161 return q[i].Priority < q[j].Priority 162 } 163 164 // Swap swaps things in-place; part of sort.Interface 165 func (q queue) Swap(i, j int) { 166 q[i], q[j] = q[j], q[i] 167 q[i].index = i 168 q[j].index = j 169 } 170 171 // Push is used by heap.Interface to push items onto the heap. This method is 172 // invoked by container/heap, and should not be used directly. 173 // See: https://golang.org/pkg/container/heap/#Interface 174 func (q *queue) Push(x interface{}) { 175 n := len(*q) 176 item, ok := x.(*Item) 177 if !ok { 178 return 179 } 180 item.index = n 181 *q = append(*q, item) 182 } 183 184 // Pop is used by heap.Interface to pop items off of the heap. This method is 185 // invoked by container/heap, and should not be used directly. 186 // See: https://golang.org/pkg/container/heap/#Interface 187 func (q *queue) Pop() interface{} { 188 old := *q 189 n := len(old) 190 item := old[n-1] 191 old[n-1] = nil // avoid memory leak 192 item.index = -1 // for safety 193 *q = old[0 : n-1] 194 return item 195 }