github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/common/prque/lazyqueue.go (about)

     1  //  Copyright 2018 The go-ethereum Authors
     2  //  Copyright 2019 The go-aigar Authors
     3  //  This file is part of the go-aigar library.
     4  //
     5  //  The go-aigar library is free software: you can redistribute it and/or modify
     6  //  it under the terms of the GNU Lesser General Public License as published by
     7  //  the Free Software Foundation, either version 3 of the License, or
     8  //  (at your option) any later version.
     9  //
    10  //  The go-aigar library is distributed in the hope that it will be useful,
    11  //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  //  GNU Lesser General Public License for more details.
    14  //
    15  //  You should have received a copy of the GNU Lesser General Public License
    16  //  along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package prque
    19  
    20  import (
    21  	"container/heap"
    22  	"time"
    23  
    24  	"github.com/AigarNetwork/aigar/common/mclock"
    25  )
    26  
    27  // LazyQueue is a priority queue data structure where priorities can change over
    28  // time and are only evaluated on demand.
    29  // Two callbacks are required:
    30  // - priority evaluates the actual priority of an item
    31  // - maxPriority gives an upper estimate for the priority in any moment between
    32  //   now and the given absolute time
    33  // If the upper estimate is exceeded then Update should be called for that item.
    34  // A global Refresh function should also be called periodically.
    35  type LazyQueue struct {
    36  	clock mclock.Clock
    37  	// Items are stored in one of two internal queues ordered by estimated max
    38  	// priority until the next and the next-after-next refresh. Update and Refresh
    39  	// always places items in queue[1].
    40  	queue       [2]*sstack
    41  	popQueue    *sstack
    42  	period      time.Duration
    43  	maxUntil    mclock.AbsTime
    44  	indexOffset int
    45  	setIndex    SetIndexCallback
    46  	priority    PriorityCallback
    47  	maxPriority MaxPriorityCallback
    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  	q.Reset()
    65  	q.Refresh()
    66  	return q
    67  }
    68  
    69  // Reset clears the contents of the queue
    70  func (q *LazyQueue) Reset() {
    71  	q.queue[0] = newSstack(q.setIndex0)
    72  	q.queue[1] = newSstack(q.setIndex1)
    73  }
    74  
    75  // Refresh should be called at least with the frequency specified by the refreshPeriod parameter
    76  func (q *LazyQueue) Refresh() {
    77  	q.maxUntil = q.clock.Now() + mclock.AbsTime(q.period)
    78  	for q.queue[0].Len() != 0 {
    79  		q.Push(heap.Pop(q.queue[0]).(*item).value)
    80  	}
    81  	q.queue[0], q.queue[1] = q.queue[1], q.queue[0]
    82  	q.indexOffset = 1 - q.indexOffset
    83  	q.maxUntil += mclock.AbsTime(q.period)
    84  }
    85  
    86  // Push adds an item to the queue
    87  func (q *LazyQueue) Push(data interface{}) {
    88  	heap.Push(q.queue[1], &item{data, q.maxPriority(data, q.maxUntil)})
    89  }
    90  
    91  // Update updates the upper priority estimate for the item with the given queue index
    92  func (q *LazyQueue) Update(index int) {
    93  	q.Push(q.Remove(index))
    94  }
    95  
    96  // Pop removes and returns the item with the greatest actual priority
    97  func (q *LazyQueue) Pop() (interface{}, int64) {
    98  	var (
    99  		resData interface{}
   100  		resPri  int64
   101  	)
   102  	q.MultiPop(func(data interface{}, priority int64) bool {
   103  		resData = data
   104  		resPri = priority
   105  		return false
   106  	})
   107  	return resData, resPri
   108  }
   109  
   110  // peekIndex returns the index of the internal queue where the item with the
   111  // highest estimated priority is or -1 if both are empty
   112  func (q *LazyQueue) peekIndex() int {
   113  	if q.queue[0].Len() != 0 {
   114  		if q.queue[1].Len() != 0 && q.queue[1].blocks[0][0].priority > q.queue[0].blocks[0][0].priority {
   115  			return 1
   116  		}
   117  		return 0
   118  	}
   119  	if q.queue[1].Len() != 0 {
   120  		return 1
   121  	}
   122  	return -1
   123  }
   124  
   125  // MultiPop pops multiple items from the queue and is more efficient than calling
   126  // Pop multiple times. Popped items are passed to the callback. MultiPop returns
   127  // when the callback returns false or there are no more items to pop.
   128  func (q *LazyQueue) MultiPop(callback func(data interface{}, priority int64) bool) {
   129  	now := q.clock.Now()
   130  	nextIndex := q.peekIndex()
   131  	for nextIndex != -1 {
   132  		data := heap.Pop(q.queue[nextIndex]).(*item).value
   133  		heap.Push(q.popQueue, &item{data, q.priority(data, now)})
   134  		nextIndex = q.peekIndex()
   135  		for q.popQueue.Len() != 0 && (nextIndex == -1 || q.queue[nextIndex].blocks[0][0].priority < q.popQueue.blocks[0][0].priority) {
   136  			i := heap.Pop(q.popQueue).(*item)
   137  			if !callback(i.value, i.priority) {
   138  				for q.popQueue.Len() != 0 {
   139  					q.Push(heap.Pop(q.popQueue).(*item).value)
   140  				}
   141  				return
   142  			}
   143  		}
   144  	}
   145  }
   146  
   147  // PopItem pops the item from the queue only, dropping the associated priority value.
   148  func (q *LazyQueue) PopItem() interface{} {
   149  	i, _ := q.Pop()
   150  	return i
   151  }
   152  
   153  // Remove removes removes the item with the given index.
   154  func (q *LazyQueue) Remove(index int) interface{} {
   155  	if index < 0 {
   156  		return nil
   157  	}
   158  	return heap.Remove(q.queue[index&1^q.indexOffset], index>>1).(*item).value
   159  }
   160  
   161  // Empty checks whether the priority queue is empty.
   162  func (q *LazyQueue) Empty() bool {
   163  	return q.queue[0].Len() == 0 && q.queue[1].Len() == 0
   164  }
   165  
   166  // Size returns the number of items in the priority queue.
   167  func (q *LazyQueue) Size() int {
   168  	return q.queue[0].Len() + q.queue[1].Len()
   169  }
   170  
   171  // setIndex0 translates internal queue item index to the virtual index space of LazyQueue
   172  func (q *LazyQueue) setIndex0(data interface{}, index int) {
   173  	if index == -1 {
   174  		q.setIndex(data, -1)
   175  	} else {
   176  		q.setIndex(data, index+index)
   177  	}
   178  }
   179  
   180  // setIndex1 translates internal queue item index to the virtual index space of LazyQueue
   181  func (q *LazyQueue) setIndex1(data interface{}, index int) {
   182  	q.setIndex(data, index+index+1)
   183  }