github.com/theQRL/go-zond@v0.1.1/common/prque/lazyqueue.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package prque 18 19 import ( 20 "container/heap" 21 "time" 22 23 "github.com/theQRL/go-zond/common/mclock" 24 "golang.org/x/exp/constraints" 25 ) 26 27 // LazyQueue is a priority queue data structure where priorities can change over 28 // time and are only evaluated on demand. 29 // Two callbacks are required: 30 // - priority evaluates the actual priority of an item 31 // - maxPriority gives an upper estimate for the priority in any moment between 32 // now and the given absolute time 33 // 34 // If the upper estimate is exceeded then Update should be called for that item. 35 // A global Refresh function should also be called periodically. 36 type LazyQueue[P constraints.Ordered, V any] struct { 37 clock mclock.Clock 38 // Items are stored in one of two internal queues ordered by estimated max 39 // priority until the next and the next-after-next refresh. Update and Refresh 40 // always places items in queue[1]. 41 queue [2]*sstack[P, V] 42 popQueue *sstack[P, V] 43 period time.Duration 44 maxUntil mclock.AbsTime 45 indexOffset int 46 setIndex SetIndexCallback[V] 47 priority PriorityCallback[P, V] 48 maxPriority MaxPriorityCallback[P, V] 49 lastRefresh1, lastRefresh2 mclock.AbsTime 50 } 51 52 type ( 53 PriorityCallback[P constraints.Ordered, V any] func(data V) P // actual priority callback 54 MaxPriorityCallback[P constraints.Ordered, V any] func(data V, until mclock.AbsTime) P // estimated maximum priority callback 55 ) 56 57 // NewLazyQueue creates a new lazy queue 58 func NewLazyQueue[P constraints.Ordered, V any](setIndex SetIndexCallback[V], priority PriorityCallback[P, V], maxPriority MaxPriorityCallback[P, V], clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue[P, V] { 59 q := &LazyQueue[P, V]{ 60 popQueue: newSstack[P, V](nil), 61 setIndex: setIndex, 62 priority: priority, 63 maxPriority: maxPriority, 64 clock: clock, 65 period: refreshPeriod, 66 lastRefresh1: clock.Now(), 67 lastRefresh2: clock.Now(), 68 } 69 q.Reset() 70 q.refresh(clock.Now()) 71 return q 72 } 73 74 // Reset clears the contents of the queue 75 func (q *LazyQueue[P, V]) Reset() { 76 q.queue[0] = newSstack[P, V](q.setIndex0) 77 q.queue[1] = newSstack[P, V](q.setIndex1) 78 } 79 80 // Refresh performs queue re-evaluation if necessary 81 func (q *LazyQueue[P, V]) Refresh() { 82 now := q.clock.Now() 83 for time.Duration(now-q.lastRefresh2) >= q.period*2 { 84 q.refresh(now) 85 q.lastRefresh2 = q.lastRefresh1 86 q.lastRefresh1 = now 87 } 88 } 89 90 // refresh re-evaluates items in the older queue and swaps the two queues 91 func (q *LazyQueue[P, V]) refresh(now mclock.AbsTime) { 92 q.maxUntil = now.Add(q.period) 93 for q.queue[0].Len() != 0 { 94 q.Push(heap.Pop(q.queue[0]).(*item[P, V]).value) 95 } 96 q.queue[0], q.queue[1] = q.queue[1], q.queue[0] 97 q.indexOffset = 1 - q.indexOffset 98 q.maxUntil = q.maxUntil.Add(q.period) 99 } 100 101 // Push adds an item to the queue 102 func (q *LazyQueue[P, V]) Push(data V) { 103 heap.Push(q.queue[1], &item[P, V]{data, q.maxPriority(data, q.maxUntil)}) 104 } 105 106 // Update updates the upper priority estimate for the item with the given queue index 107 func (q *LazyQueue[P, V]) Update(index int) { 108 q.Push(q.Remove(index)) 109 } 110 111 // Pop removes and returns the item with the greatest actual priority 112 func (q *LazyQueue[P, V]) Pop() (V, P) { 113 var ( 114 resData V 115 resPri P 116 ) 117 q.MultiPop(func(data V, priority P) bool { 118 resData = data 119 resPri = priority 120 return false 121 }) 122 return resData, resPri 123 } 124 125 // peekIndex returns the index of the internal queue where the item with the 126 // highest estimated priority is or -1 if both are empty 127 func (q *LazyQueue[P, V]) peekIndex() int { 128 if q.queue[0].Len() != 0 { 129 if q.queue[1].Len() != 0 && q.queue[1].blocks[0][0].priority > q.queue[0].blocks[0][0].priority { 130 return 1 131 } 132 return 0 133 } 134 if q.queue[1].Len() != 0 { 135 return 1 136 } 137 return -1 138 } 139 140 // MultiPop pops multiple items from the queue and is more efficient than calling 141 // Pop multiple times. Popped items are passed to the callback. MultiPop returns 142 // when the callback returns false or there are no more items to pop. 143 func (q *LazyQueue[P, V]) MultiPop(callback func(data V, priority P) bool) { 144 nextIndex := q.peekIndex() 145 for nextIndex != -1 { 146 data := heap.Pop(q.queue[nextIndex]).(*item[P, V]).value 147 heap.Push(q.popQueue, &item[P, V]{data, q.priority(data)}) 148 nextIndex = q.peekIndex() 149 for q.popQueue.Len() != 0 && (nextIndex == -1 || q.queue[nextIndex].blocks[0][0].priority < q.popQueue.blocks[0][0].priority) { 150 i := heap.Pop(q.popQueue).(*item[P, V]) 151 if !callback(i.value, i.priority) { 152 for q.popQueue.Len() != 0 { 153 q.Push(heap.Pop(q.popQueue).(*item[P, V]).value) 154 } 155 return 156 } 157 nextIndex = q.peekIndex() // re-check because callback is allowed to push items back 158 } 159 } 160 } 161 162 // PopItem pops the item from the queue only, dropping the associated priority value. 163 func (q *LazyQueue[P, V]) PopItem() V { 164 i, _ := q.Pop() 165 return i 166 } 167 168 // Remove removes the item with the given index. 169 func (q *LazyQueue[P, V]) Remove(index int) V { 170 return heap.Remove(q.queue[index&1^q.indexOffset], index>>1).(*item[P, V]).value 171 } 172 173 // Empty checks whether the priority queue is empty. 174 func (q *LazyQueue[P, V]) Empty() bool { 175 return q.queue[0].Len() == 0 && q.queue[1].Len() == 0 176 } 177 178 // Size returns the number of items in the priority queue. 179 func (q *LazyQueue[P, V]) Size() int { 180 return q.queue[0].Len() + q.queue[1].Len() 181 } 182 183 // setIndex0 translates internal queue item index to the virtual index space of LazyQueue 184 func (q *LazyQueue[P, V]) setIndex0(data V, index int) { 185 if index == -1 { 186 q.setIndex(data, -1) 187 } else { 188 q.setIndex(data, index+index) 189 } 190 } 191 192 // setIndex1 translates internal queue item index to the virtual index space of LazyQueue 193 func (q *LazyQueue[P, V]) setIndex1(data V, index int) { 194 q.setIndex(data, index+index+1) 195 }