github.com/shyftnetwork/go-empyrean@v1.8.3-0.20191127201940-fbfca9338f04/swarm/network/priorityqueue/priorityqueue.go (about) 1 // Copyright 2018 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 priority_queue implement a channel based priority queue 18 // over arbitrary types. It provides an 19 // an autopop loop applying a function to the items always respecting 20 // their priority. The structure is only quasi consistent ie., if a lower 21 // priority item is autopopped, it is guaranteed that there was a point 22 // when no higher priority item was present, ie. it is not guaranteed 23 // that there was any point where the lower priority item was present 24 // but the higher was not 25 26 package priorityqueue 27 28 import ( 29 "context" 30 "errors" 31 32 "github.com/ShyftNetwork/go-empyrean/log" 33 ) 34 35 var ( 36 ErrContention = errors.New("contention") 37 38 errBadPriority = errors.New("bad priority") 39 40 wakey = struct{}{} 41 ) 42 43 // PriorityQueue is the basic structure 44 type PriorityQueue struct { 45 Queues []chan interface{} 46 wakeup chan struct{} 47 } 48 49 // New is the constructor for PriorityQueue 50 func New(n int, l int) *PriorityQueue { 51 var queues = make([]chan interface{}, n) 52 for i := range queues { 53 queues[i] = make(chan interface{}, l) 54 } 55 return &PriorityQueue{ 56 Queues: queues, 57 wakeup: make(chan struct{}, 1), 58 } 59 } 60 61 // Run is a forever loop popping items from the queues 62 func (pq *PriorityQueue) Run(ctx context.Context, f func(interface{})) { 63 top := len(pq.Queues) - 1 64 p := top 65 READ: 66 for { 67 q := pq.Queues[p] 68 select { 69 case <-ctx.Done(): 70 return 71 case x := <-q: 72 log.Trace("priority.queue f(x)", "p", p, "len(Queues[p])", len(pq.Queues[p])) 73 f(x) 74 p = top 75 default: 76 if p > 0 { 77 p-- 78 log.Trace("priority.queue p > 0", "p", p) 79 continue READ 80 } 81 p = top 82 select { 83 case <-ctx.Done(): 84 return 85 case <-pq.wakeup: 86 log.Trace("priority.queue wakeup", "p", p) 87 } 88 } 89 } 90 } 91 92 // Push pushes an item to the appropriate queue specified in the priority argument 93 // if context is given it waits until either the item is pushed or the Context aborts 94 func (pq *PriorityQueue) Push(x interface{}, p int) error { 95 if p < 0 || p >= len(pq.Queues) { 96 return errBadPriority 97 } 98 log.Trace("priority.queue push", "p", p, "len(Queues[p])", len(pq.Queues[p])) 99 select { 100 case pq.Queues[p] <- x: 101 default: 102 return ErrContention 103 } 104 select { 105 case pq.wakeup <- wakey: 106 default: 107 } 108 return nil 109 }