github.com/sunvim/utils@v0.1.0/priorityqueue/pq.go (about) 1 package priorityqueue 2 3 import "sync" 4 5 // Item is an item that can be added to the priority queue. 6 type Item interface { 7 // Compare returns a bool that can be used to determine 8 // ordering in the priority queue. Assuming the queue 9 // is in ascending order, this should return > logic. 10 // Return 1 to indicate this object is greater than the 11 // the other logic, 0 to indicate equality, and -1 to indicate 12 // less than other. 13 Compare(other Item) int 14 } 15 16 type priorityItems []Item 17 18 func (items *priorityItems) swap(i, j int) { 19 (*items)[i], (*items)[j] = (*items)[j], (*items)[i] 20 } 21 22 func (items *priorityItems) pop() Item { 23 size := len(*items) 24 25 // Move last leaf to root, and 'pop' the last item. 26 items.swap(size-1, 0) 27 item := (*items)[size-1] // Item to return. 28 (*items)[size-1], *items = nil, (*items)[:size-1] 29 30 // 'Bubble down' to restore heap property. 31 index := 0 32 childL, childR := 2*index+1, 2*index+2 33 for len(*items) > childL { 34 child := childL 35 if len(*items) > childR && (*items)[childR].Compare((*items)[childL]) < 0 { 36 child = childR 37 } 38 39 if (*items)[child].Compare((*items)[index]) < 0 { 40 items.swap(index, child) 41 42 index = child 43 childL, childR = 2*index+1, 2*index+2 44 } else { 45 break 46 } 47 } 48 49 return item 50 } 51 52 func (items *priorityItems) get(number int) []Item { 53 returnItems := make([]Item, 0, number) 54 for i := 0; i < number; i++ { 55 if len(*items) == 0 { 56 break 57 } 58 59 returnItems = append(returnItems, items.pop()) 60 } 61 62 return returnItems 63 } 64 65 func (items *priorityItems) push(item Item) { 66 // Stick the item as the end of the last level. 67 *items = append(*items, item) 68 69 // 'Bubble up' to restore heap property. 70 index := len(*items) - 1 71 parent := int((index - 1) / 2) 72 for parent >= 0 && (*items)[parent].Compare(item) > 0 { 73 items.swap(index, parent) 74 75 index = parent 76 parent = int((index - 1) / 2) 77 } 78 } 79 80 // PriorityQueue is similar to queue except that it takes 81 // items that implement the Item interface and adds them 82 // to the queue in priority order. 83 type PriorityQueue struct { 84 waiters waiters 85 items priorityItems 86 itemMap map[Item]struct{} 87 lock sync.Mutex 88 disposeLock sync.Mutex 89 disposed bool 90 allowDuplicates bool 91 } 92 93 // Put adds items to the queue. 94 func (pq *PriorityQueue) Put(items ...Item) error { 95 if len(items) == 0 { 96 return nil 97 } 98 99 pq.lock.Lock() 100 defer pq.lock.Unlock() 101 102 if pq.disposed { 103 return ErrDisposed 104 } 105 106 for _, item := range items { 107 if pq.allowDuplicates { 108 pq.items.push(item) 109 } else if _, ok := pq.itemMap[item]; !ok { 110 pq.itemMap[item] = struct{}{} 111 pq.items.push(item) 112 } 113 } 114 115 for { 116 sema := pq.waiters.get() 117 if sema == nil { 118 break 119 } 120 121 sema.response.Add(1) 122 sema.ready <- true 123 sema.response.Wait() 124 if len(pq.items) == 0 { 125 break 126 } 127 } 128 129 return nil 130 } 131 132 // Get retrieves items from the queue. If the queue is empty, 133 // this call blocks until the next item is added to the queue. This 134 // will attempt to retrieve number of items. 135 func (pq *PriorityQueue) Get(number int) ([]Item, error) { 136 if number < 1 { 137 return nil, nil 138 } 139 140 pq.lock.Lock() 141 142 if pq.disposed { 143 pq.lock.Unlock() 144 return nil, ErrDisposed 145 } 146 147 var items []Item 148 149 // Remove references to popped items. 150 deleteItems := func(items []Item) { 151 for _, item := range items { 152 delete(pq.itemMap, item) 153 } 154 } 155 156 if len(pq.items) == 0 { 157 sema := newSema() 158 pq.waiters.put(sema) 159 pq.lock.Unlock() 160 161 <-sema.ready 162 163 if pq.Disposed() { 164 return nil, ErrDisposed 165 } 166 167 items = pq.items.get(number) 168 if !pq.allowDuplicates { 169 deleteItems(items) 170 } 171 sema.response.Done() 172 return items, nil 173 } 174 175 items = pq.items.get(number) 176 deleteItems(items) 177 pq.lock.Unlock() 178 return items, nil 179 } 180 181 // Peek will look at the next item without removing it from the queue. 182 func (pq *PriorityQueue) Peek() Item { 183 pq.lock.Lock() 184 defer pq.lock.Unlock() 185 if len(pq.items) > 0 { 186 return pq.items[0] 187 } 188 return nil 189 } 190 191 // Empty returns a bool indicating if there are any items left 192 // in the queue. 193 func (pq *PriorityQueue) Empty() bool { 194 pq.lock.Lock() 195 defer pq.lock.Unlock() 196 197 return len(pq.items) == 0 198 } 199 200 // Len returns a number indicating how many items are in the queue. 201 func (pq *PriorityQueue) Len() int { 202 pq.lock.Lock() 203 defer pq.lock.Unlock() 204 205 return len(pq.items) 206 } 207 208 // Disposed returns a bool indicating if this queue has been disposed. 209 func (pq *PriorityQueue) Disposed() bool { 210 pq.disposeLock.Lock() 211 defer pq.disposeLock.Unlock() 212 213 return pq.disposed 214 } 215 216 // Dispose will prevent any further reads/writes to this queue 217 // and frees available resources. 218 func (pq *PriorityQueue) Dispose() { 219 pq.lock.Lock() 220 defer pq.lock.Unlock() 221 222 pq.disposeLock.Lock() 223 defer pq.disposeLock.Unlock() 224 225 pq.disposed = true 226 for _, waiter := range pq.waiters { 227 waiter.response.Add(1) 228 waiter.ready <- true 229 } 230 231 pq.items = nil 232 pq.waiters = nil 233 } 234 235 // NewPriorityQueue is the constructor for a priority queue. 236 func NewPriorityQueue(hint int, allowDuplicates bool) *PriorityQueue { 237 return &PriorityQueue{ 238 items: make(priorityItems, 0, hint), 239 itemMap: make(map[Item]struct{}, hint), 240 allowDuplicates: allowDuplicates, 241 } 242 }