github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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/errors"
    11  	"github.com/juju/utils/clock"
    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  		// TODO(fwereade): 2016-03-17 lp:1558657
    38  		return s.time.After(s.items[0].t.Sub(s.time.Now()))
    39  	}
    40  	return nil
    41  }
    42  
    43  // Ready returns the parameters for items that are scheduled at or before
    44  // "now", and removes them from the schedule. The resulting slices are in
    45  // order of time; items scheduled for the same time have no defined relative
    46  // order.
    47  func (s *Schedule) Ready(now time.Time) []interface{} {
    48  	var ready []interface{}
    49  	for len(s.items) > 0 && !s.items[0].t.After(now) {
    50  		item := heap.Pop(&s.items).(*scheduleItem)
    51  		delete(s.m, item.key)
    52  		ready = append(ready, item.value)
    53  	}
    54  	return ready
    55  }
    56  
    57  // Add adds an item with the specified value, with the corresponding key
    58  // and time to the schedule. Add will panic if there already exists an item
    59  // with the same key.
    60  func (s *Schedule) Add(key, value interface{}, t time.Time) {
    61  	if _, ok := s.m[key]; ok {
    62  		panic(errors.Errorf("duplicate key %v", key))
    63  	}
    64  	item := &scheduleItem{key: key, value: value, t: t}
    65  	s.m[key] = item
    66  	heap.Push(&s.items, item)
    67  }
    68  
    69  // Remove removes the item corresponding to the specified key from the
    70  // schedule. If no item with the specified key exists, this is a no-op.
    71  func (s *Schedule) Remove(key interface{}) {
    72  	if item, ok := s.m[key]; ok {
    73  		heap.Remove(&s.items, item.i)
    74  		delete(s.m, key)
    75  	}
    76  }
    77  
    78  type scheduleItems []*scheduleItem
    79  
    80  type scheduleItem struct {
    81  	i     int
    82  	key   interface{}
    83  	value interface{}
    84  	t     time.Time
    85  }
    86  
    87  func (s scheduleItems) Len() int {
    88  	return len(s)
    89  }
    90  
    91  func (s scheduleItems) Less(i, j int) bool {
    92  	return s[i].t.Before(s[j].t)
    93  }
    94  
    95  func (s scheduleItems) Swap(i, j int) {
    96  	s[i], s[j] = s[j], s[i]
    97  	s[i].i = i
    98  	s[j].i = j
    99  }
   100  
   101  func (s *scheduleItems) Push(x interface{}) {
   102  	item := x.(*scheduleItem)
   103  	item.i = len(*s)
   104  	*s = append(*s, item)
   105  }
   106  
   107  func (s *scheduleItems) Pop() interface{} {
   108  	n := len(*s) - 1
   109  	x := (*s)[n]
   110  	*s = (*s)[:n]
   111  	return x
   112  }