github.com/aswedchain/aswed@v1.0.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/aswedchain/aswed/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 lastRefresh1, lastRefresh2 mclock.AbsTime 48 } 49 50 type ( 51 PriorityCallback func(data interface{}, now mclock.AbsTime) int64 // actual priority callback 52 MaxPriorityCallback func(data interface{}, until mclock.AbsTime) int64 // estimated maximum priority callback 53 ) 54 55 // NewLazyQueue creates a new lazy queue 56 func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPriority MaxPriorityCallback, clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue { 57 q := &LazyQueue{ 58 popQueue: newSstack(nil), 59 setIndex: setIndex, 60 priority: priority, 61 maxPriority: maxPriority, 62 clock: clock, 63 period: refreshPeriod, 64 lastRefresh1: clock.Now(), 65 lastRefresh2: clock.Now(), 66 } 67 q.Reset() 68 q.refresh(clock.Now()) 69 return q 70 } 71 72 // Reset clears the contents of the queue 73 func (q *LazyQueue) Reset() { 74 q.queue[0] = newSstack(q.setIndex0) 75 q.queue[1] = newSstack(q.setIndex1) 76 } 77 78 // Refresh performs queue re-evaluation if necessary 79 func (q *LazyQueue) Refresh() { 80 now := q.clock.Now() 81 for time.Duration(now-q.lastRefresh2) >= q.period*2 { 82 q.refresh(now) 83 q.lastRefresh2 = q.lastRefresh1 84 q.lastRefresh1 = now 85 } 86 } 87 88 // refresh re-evaluates items in the older queue and swaps the two queues 89 func (q *LazyQueue) refresh(now mclock.AbsTime) { 90 q.maxUntil = now + mclock.AbsTime(q.period) 91 for q.queue[0].Len() != 0 { 92 q.Push(heap.Pop(q.queue[0]).(*item).value) 93 } 94 q.queue[0], q.queue[1] = q.queue[1], q.queue[0] 95 q.indexOffset = 1 - q.indexOffset 96 q.maxUntil += mclock.AbsTime(q.period) 97 } 98 99 // Push adds an item to the queue 100 func (q *LazyQueue) Push(data interface{}) { 101 heap.Push(q.queue[1], &item{data, q.maxPriority(data, q.maxUntil)}) 102 } 103 104 // Update updates the upper priority estimate for the item with the given queue index 105 func (q *LazyQueue) Update(index int) { 106 q.Push(q.Remove(index)) 107 } 108 109 // Pop removes and returns the item with the greatest actual priority 110 func (q *LazyQueue) Pop() (interface{}, int64) { 111 var ( 112 resData interface{} 113 resPri int64 114 ) 115 q.MultiPop(func(data interface{}, priority int64) bool { 116 resData = data 117 resPri = priority 118 return false 119 }) 120 return resData, resPri 121 } 122 123 // peekIndex returns the index of the internal queue where the item with the 124 // highest estimated priority is or -1 if both are empty 125 func (q *LazyQueue) peekIndex() int { 126 if q.queue[0].Len() != 0 { 127 if q.queue[1].Len() != 0 && q.queue[1].blocks[0][0].priority > q.queue[0].blocks[0][0].priority { 128 return 1 129 } 130 return 0 131 } 132 if q.queue[1].Len() != 0 { 133 return 1 134 } 135 return -1 136 } 137 138 // MultiPop pops multiple items from the queue and is more efficient than calling 139 // Pop multiple times. Popped items are passed to the callback. MultiPop returns 140 // when the callback returns false or there are no more items to pop. 141 func (q *LazyQueue) MultiPop(callback func(data interface{}, priority int64) bool) { 142 now := q.clock.Now() 143 nextIndex := q.peekIndex() 144 for nextIndex != -1 { 145 data := heap.Pop(q.queue[nextIndex]).(*item).value 146 heap.Push(q.popQueue, &item{data, q.priority(data, now)}) 147 nextIndex = q.peekIndex() 148 for q.popQueue.Len() != 0 && (nextIndex == -1 || q.queue[nextIndex].blocks[0][0].priority < q.popQueue.blocks[0][0].priority) { 149 i := heap.Pop(q.popQueue).(*item) 150 if !callback(i.value, i.priority) { 151 for q.popQueue.Len() != 0 { 152 q.Push(heap.Pop(q.popQueue).(*item).value) 153 } 154 return 155 } 156 nextIndex = q.peekIndex() // re-check because callback is allowed to push items back 157 } 158 } 159 } 160 161 // PopItem pops the item from the queue only, dropping the associated priority value. 162 func (q *LazyQueue) PopItem() interface{} { 163 i, _ := q.Pop() 164 return i 165 } 166 167 // Remove removes removes the item with the given index. 168 func (q *LazyQueue) Remove(index int) interface{} { 169 if index < 0 { 170 return nil 171 } 172 return heap.Remove(q.queue[index&1^q.indexOffset], index>>1).(*item).value 173 } 174 175 // Empty checks whether the priority queue is empty. 176 func (q *LazyQueue) Empty() bool { 177 return q.queue[0].Len() == 0 && q.queue[1].Len() == 0 178 } 179 180 // Size returns the number of items in the priority queue. 181 func (q *LazyQueue) Size() int { 182 return q.queue[0].Len() + q.queue[1].Len() 183 } 184 185 // setIndex0 translates internal queue item index to the virtual index space of LazyQueue 186 func (q *LazyQueue) setIndex0(data interface{}, index int) { 187 if index == -1 { 188 q.setIndex(data, -1) 189 } else { 190 q.setIndex(data, index+index) 191 } 192 } 193 194 // setIndex1 translates internal queue item index to the virtual index space of LazyQueue 195 func (q *LazyQueue) setIndex1(data interface{}, index int) { 196 q.setIndex(data, index+index+1) 197 }