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  }