| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | // Copyright 2018 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // package priority_queue implement a channel based priority queue | 
					
						
							|  |  |  | // over arbitrary types. It provides an | 
					
						
							|  |  |  | // an autopop loop applying a function to the items always respecting | 
					
						
							|  |  |  | // their priority. The structure is only quasi consistent ie., if a lower | 
					
						
							|  |  |  | // priority item is autopopped, it is guaranteed that there was a point | 
					
						
							|  |  |  | // when no higher priority item was present, ie. it is not guaranteed | 
					
						
							|  |  |  | // that there was any point where the lower priority item was present | 
					
						
							|  |  |  | // but the higher was not | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package priorityqueue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/log" | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	ErrContention = errors.New("contention") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	errBadPriority = errors.New("bad priority") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wakey = struct{}{} | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PriorityQueue is the basic structure | 
					
						
							|  |  |  | type PriorityQueue struct { | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	Queues []chan interface{} | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	wakeup chan struct{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // New is the constructor for PriorityQueue | 
					
						
							|  |  |  | func New(n int, l int) *PriorityQueue { | 
					
						
							|  |  |  | 	var queues = make([]chan interface{}, n) | 
					
						
							|  |  |  | 	for i := range queues { | 
					
						
							|  |  |  | 		queues[i] = make(chan interface{}, l) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &PriorityQueue{ | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 		Queues: queues, | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		wakeup: make(chan struct{}, 1), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Run is a forever loop popping items from the queues | 
					
						
							|  |  |  | func (pq *PriorityQueue) Run(ctx context.Context, f func(interface{})) { | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	top := len(pq.Queues) - 1 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	p := top | 
					
						
							|  |  |  | READ: | 
					
						
							|  |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 		q := pq.Queues[p] | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		case x := <-q: | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 			log.Trace("priority.queue f(x)", "p", p, "len(Queues[p])", len(pq.Queues[p])) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 			f(x) | 
					
						
							|  |  |  | 			p = top | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			if p > 0 { | 
					
						
							|  |  |  | 				p-- | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 				log.Trace("priority.queue p > 0", "p", p) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 				continue READ | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			p = top | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ctx.Done(): | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			case <-pq.wakeup: | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 				log.Trace("priority.queue wakeup", "p", p) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Push pushes an item to the appropriate queue specified in the priority argument | 
					
						
							|  |  |  | // if context is given it waits until either the item is pushed or the Context aborts | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | func (pq *PriorityQueue) Push(x interface{}, p int) error { | 
					
						
							|  |  |  | 	if p < 0 || p >= len(pq.Queues) { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		return errBadPriority | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-13 11:42:19 +02:00
										 |  |  | 	log.Trace("priority.queue push", "p", p, "len(Queues[p])", len(pq.Queues[p])) | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case pq.Queues[p] <- x: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return ErrContention | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case pq.wakeup <- wakey: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |