github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/storageprovisioner/internal/schedule/schedule.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package schedule
     5  
     6  import (
     7  	"container/heap"
     8  	"time"
     9  
    10  	"github.com/juju/clock"
    11  	"github.com/juju/errors"
    12  )
    13  
    14  // Schedule provides a schedule for storage operations, with the following
    15  // properties:
    16  //  - fast to add and remove items by key: O(log(n)); n is the total number of items
    17  //  - fast to identify/remove the next scheduled item: O(log(n))
    18  type Schedule struct {
    19  	time  clock.Clock
    20  	items scheduleItems
    21  	m     map[interface{}]*scheduleItem
    22  }
    23  
    24  // NewSchedule constructs a new schedule, using the given Clock for the Next
    25  // method.
    26  func NewSchedule(clock clock.Clock) *Schedule {
    27  	return &Schedule{
    28  		time: clock,
    29  		m:    make(map[interface{}]*scheduleItem),
    30  	}
    31  }
    32  
    33  // Next returns a channel which will send after the next scheduled item's time
    34  // has been reached. If there are no scheduled items, nil is returned.
    35  func (s *Schedule) Next() <-chan time.Time {
    36  	if len(s.items) > 0 {
    37  		return s.time.After(s.items[0].t.Sub(s.time.Now()))
    38  	}
    39  	return nil
    40  }
    41  
    42  // Ready returns the parameters for items that are scheduled at or before
    43  // "now", and removes them from the schedule. The resulting slices are in
    44  // order of time; items scheduled for the same time have no defined relative
    45  // order.
    46  func (s *Schedule) Ready(now time.Time) []interface{} {
    47  	var ready []interface{}
    48  	for len(s.items) > 0 && !s.items[0].t.After(now) {
    49  		item := heap.Pop(&s.items).(*scheduleItem)
    50  		delete(s.m, item.key)
    51  		ready = append(ready, item.value)
    52  	}
    53  	return ready
    54  }
    55  
    56  // Add adds an item with the specified value, with the corresponding key
    57  // and time to the schedule. Add will panic if there already exists an item
    58  // with the same key.
    59  func (s *Schedule) Add(key, value interface{}, t time.Time) {
    60  	if _, ok := s.m[key]; ok {
    61  		panic(errors.Errorf("duplicate key %v", key))
    62  	}
    63  	item := &scheduleItem{key: key, value: value, t: t}
    64  	s.m[key] = item
    65  	heap.Push(&s.items, item)
    66  }
    67  
    68  // Remove removes the item corresponding to the specified key from the
    69  // schedule. If no item with the specified key exists, this is a no-op.
    70  func (s *Schedule) Remove(key interface{}) {
    71  	if item, ok := s.m[key]; ok {
    72  		heap.Remove(&s.items, item.i)
    73  		delete(s.m, key)
    74  	}
    75  }
    76  
    77  type scheduleItems []*scheduleItem
    78  
    79  type scheduleItem struct {
    80  	i     int
    81  	key   interface{}
    82  	value interface{}
    83  	t     time.Time
    84  }
    85  
    86  func (s scheduleItems) Len() int {
    87  	return len(s)
    88  }
    89  
    90  func (s scheduleItems) Less(i, j int) bool {
    91  	return s[i].t.Before(s[j].t)
    92  }
    93  
    94  func (s scheduleItems) Swap(i, j int) {
    95  	s[i], s[j] = s[j], s[i]
    96  	s[i].i = i
    97  	s[j].i = j
    98  }
    99  
   100  func (s *scheduleItems) Push(x interface{}) {
   101  	item := x.(*scheduleItem)
   102  	item.i = len(*s)
   103  	*s = append(*s, item)
   104  }
   105  
   106  func (s *scheduleItems) Pop() interface{} {
   107  	n := len(*s) - 1
   108  	x := (*s)[n]
   109  	*s = (*s)[:n]
   110  	return x
   111  }