github.com/YoungNK/go-ethereum@v1.9.7/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/ethereum/go-ethereum/common/mclock" 24 ) 25 26 // LazyQueue is a priority queue data structure where priorities can change over 27 // time and are only evaluated on demand. 28 // Two callbacks are required: 29 // - priority evaluates the actual priority of an item 30 // - maxPriority gives an upper estimate for the priority in any moment between 31 // now and the given absolute time 32 // If the upper estimate is exceeded then Update should be called for that item. 33 // A global Refresh function should also be called periodically. 34 type LazyQueue struct { 35 clock mclock.Clock 36 // Items are stored in one of two internal queues ordered by estimated max 37 // priority until the next and the next-after-next refresh. Update and Refresh 38 // always places items in queue[1]. 39 queue [2]*sstack 40 popQueue *sstack 41 period time.Duration 42 maxUntil mclock.AbsTime 43 indexOffset int 44 setIndex SetIndexCallback 45 priority PriorityCallback 46 maxPriority MaxPriorityCallback 47 } 48 49 type ( 50 PriorityCallback func(data interface{}, now mclock.AbsTime) int64 // actual priority callback 51 MaxPriorityCallback func(data interface{}, until mclock.AbsTime) int64 // estimated maximum priority callback 52 ) 53 54 // NewLazyQueue creates a new lazy queue 55 func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPriority MaxPriorityCallback, clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue { 56 q := &LazyQueue{ 57 popQueue: newSstack(nil), 58 setIndex: setIndex, 59 priority: priority, 60 maxPriority: maxPriority, 61 clock: clock, 62 period: refreshPeriod} 63 q.Reset() 64 q.Refresh() 65 return q 66 } 67 68 // Reset clears the contents of the queue 69 func (q *LazyQueue) Reset() { 70 q.queue[0] = newSstack(q.setIndex0) 71 q.queue[1] = newSstack(q.setIndex1) 72 } 73 74 // Refresh should be called at least with the frequency specified by the refreshPeriod parameter 75 func (q *LazyQueue) Refresh() { 76 q.maxUntil = q.clock.Now() + mclock.AbsTime(q.period) 77 for q.queue[0].Len() != 0 { 78 q.Push(heap.Pop(q.queue[0]).(*item).value) 79 } 80 q.queue[0], q.queue[1] = q.queue[1], q.queue[0] 81 q.indexOffset = 1 - q.indexOffset 82 q.maxUntil += mclock.AbsTime(q.period) 83 } 84 85 // Push adds an item to the queue 86 func (q *LazyQueue) Push(data interface{}) { 87 heap.Push(q.queue[1], &item{data, q.maxPriority(data, q.maxUntil)}) 88 } 89 90 // Update updates the upper priority estimate for the item with the given queue index 91 func (q *LazyQueue) Update(index int) { 92 q.Push(q.Remove(index)) 93 } 94 95 // Pop removes and returns the item with the greatest actual priority 96 func (q *LazyQueue) Pop() (interface{}, int64) { 97 var ( 98 resData interface{} 99 resPri int64 100 ) 101 q.MultiPop(func(data interface{}, priority int64) bool { 102 resData = data 103 resPri = priority 104 return false 105 }) 106 return resData, resPri 107 } 108 109 // peekIndex returns the index of the internal queue where the item with the 110 // highest estimated priority is or -1 if both are empty 111 func (q *LazyQueue) peekIndex() int { 112 if q.queue[0].Len() != 0 { 113 if q.queue[1].Len() != 0 && q.queue[1].blocks[0][0].priority > q.queue[0].blocks[0][0].priority { 114 return 1 115 } 116 return 0 117 } 118 if q.queue[1].Len() != 0 { 119 return 1 120 } 121 return -1 122 } 123 124 // MultiPop pops multiple items from the queue and is more efficient than calling 125 // Pop multiple times. Popped items are passed to the callback. MultiPop returns 126 // when the callback returns false or there are no more items to pop. 127 func (q *LazyQueue) MultiPop(callback func(data interface{}, priority int64) bool) { 128 now := q.clock.Now() 129 nextIndex := q.peekIndex() 130 for nextIndex != -1 { 131 data := heap.Pop(q.queue[nextIndex]).(*item).value 132 heap.Push(q.popQueue, &item{data, q.priority(data, now)}) 133 nextIndex = q.peekIndex() 134 for q.popQueue.Len() != 0 && (nextIndex == -1 || q.queue[nextIndex].blocks[0][0].priority < q.popQueue.blocks[0][0].priority) { 135 i := heap.Pop(q.popQueue).(*item) 136 if !callback(i.value, i.priority) { 137 for q.popQueue.Len() != 0 { 138 q.Push(heap.Pop(q.popQueue).(*item).value) 139 } 140 return 141 } 142 } 143 } 144 } 145 146 // PopItem pops the item from the queue only, dropping the associated priority value. 147 func (q *LazyQueue) PopItem() interface{} { 148 i, _ := q.Pop() 149 return i 150 } 151 152 // Remove removes removes the item with the given index. 153 func (q *LazyQueue) Remove(index int) interface{} { 154 if index < 0 { 155 return nil 156 } 157 return heap.Remove(q.queue[index&1^q.indexOffset], index>>1).(*item).value 158 } 159 160 // Empty checks whether the priority queue is empty. 161 func (q *LazyQueue) Empty() bool { 162 return q.queue[0].Len() == 0 && q.queue[1].Len() == 0 163 } 164 165 // Size returns the number of items in the priority queue. 166 func (q *LazyQueue) Size() int { 167 return q.queue[0].Len() + q.queue[1].Len() 168 } 169 170 // setIndex0 translates internal queue item index to the virtual index space of LazyQueue 171 func (q *LazyQueue) setIndex0(data interface{}, index int) { 172 if index == -1 { 173 q.setIndex(data, -1) 174 } else { 175 q.setIndex(data, index+index) 176 } 177 } 178 179 // setIndex1 translates internal queue item index to the virtual index space of LazyQueue 180 func (q *LazyQueue) setIndex1(data interface{}, index int) { 181 q.setIndex(data, index+index+1) 182 }