github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/state/multiwatcher/multiwatcher.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package multiwatcher
     5  
     6  import (
     7  	"container/list"
     8  	"errors"
     9  	"reflect"
    10  
    11  	"launchpad.net/tomb"
    12  
    13  	"launchpad.net/juju-core/state/api/params"
    14  	"launchpad.net/juju-core/state/watcher"
    15  )
    16  
    17  // Watcher watches any changes to the state.
    18  type Watcher struct {
    19  	all *StoreManager
    20  
    21  	// The following fields are maintained by the StoreManager
    22  	// goroutine.
    23  	revno   int64
    24  	stopped bool
    25  }
    26  
    27  // NewWatcher creates a new watcher that can observe
    28  // changes to an underlying store manager.
    29  func NewWatcher(all *StoreManager) *Watcher {
    30  	return &Watcher{
    31  		all: all,
    32  	}
    33  }
    34  
    35  // Stop stops the watcher.
    36  func (w *Watcher) Stop() error {
    37  	select {
    38  	case w.all.request <- &request{w: w}:
    39  		return nil
    40  	case <-w.all.tomb.Dead():
    41  	}
    42  	return w.all.tomb.Err()
    43  }
    44  
    45  var ErrWatcherStopped = errors.New("watcher was stopped")
    46  
    47  // Next retrieves all changes that have happened since the last
    48  // time it was called, blocking until there are some changes available.
    49  func (w *Watcher) Next() ([]params.Delta, error) {
    50  	req := &request{
    51  		w:     w,
    52  		reply: make(chan bool),
    53  	}
    54  	select {
    55  	case w.all.request <- req:
    56  	case <-w.all.tomb.Dead():
    57  		err := w.all.tomb.Err()
    58  		if err == nil {
    59  			err = errors.New("shared state watcher was stopped")
    60  		}
    61  		return nil, err
    62  	}
    63  	if ok := <-req.reply; !ok {
    64  		return nil, ErrWatcherStopped
    65  	}
    66  	return req.changes, nil
    67  }
    68  
    69  // StoreManager holds a shared record of current state and replies to
    70  // requests from Watchers to tell them when it changes.
    71  type StoreManager struct {
    72  	tomb tomb.Tomb
    73  
    74  	// backing knows how to fetch information from
    75  	// the underlying state.
    76  	backing Backing
    77  
    78  	// request receives requests from Watcher clients.
    79  	request chan *request
    80  
    81  	// all holds information on everything the StoreManager cares about.
    82  	all *Store
    83  
    84  	// Each entry in the waiting map holds a linked list of Next requests
    85  	// outstanding for the associated Watcher.
    86  	waiting map[*Watcher]*request
    87  }
    88  
    89  // InfoId holds an identifier for an Info item held in a Store.
    90  type InfoId interface{}
    91  
    92  // Backing is the interface required by the StoreManager to access the
    93  // underlying state.
    94  type Backing interface {
    95  
    96  	// GetAll retrieves information about all information
    97  	// known to the Backing and stashes it in the Store.
    98  	GetAll(all *Store) error
    99  
   100  	// Changed informs the backing about a change received
   101  	// from a watcher channel.  The backing is responsible for
   102  	// updating the Store to reflect the change.
   103  	Changed(all *Store, change watcher.Change) error
   104  
   105  	// Watch watches for any changes and sends them
   106  	// on the given channel.
   107  	Watch(in chan<- watcher.Change)
   108  
   109  	// Unwatch stops watching for changes on the
   110  	// given channel.
   111  	Unwatch(in chan<- watcher.Change)
   112  }
   113  
   114  // request holds a message from the Watcher to the
   115  // StoreManager for some changes. The request will be
   116  // replied to when some changes are available.
   117  type request struct {
   118  	// w holds the Watcher that has originated the request.
   119  	w *Watcher
   120  
   121  	// reply receives a message when deltas are ready.  If reply is
   122  	// nil, the Watcher will be stopped.  If the reply is true,
   123  	// the request has been processed; if false, the Watcher
   124  	// has been stopped,
   125  	reply chan bool
   126  
   127  	// On reply, changes will hold changes that have occurred since
   128  	// the last replied-to Next request.
   129  	changes []params.Delta
   130  
   131  	// next points to the next request in the list of outstanding
   132  	// requests on a given watcher.  It is used only by the central
   133  	// StoreManager goroutine.
   134  	next *request
   135  }
   136  
   137  // newStoreManagerNoRun creates the store manager
   138  // but does not start its run loop.
   139  func newStoreManagerNoRun(backing Backing) *StoreManager {
   140  	return &StoreManager{
   141  		backing: backing,
   142  		request: make(chan *request),
   143  		all:     NewStore(),
   144  		waiting: make(map[*Watcher]*request),
   145  	}
   146  }
   147  
   148  // NewStoreManager returns a new StoreManager that retrieves information
   149  // using the given backing.
   150  func NewStoreManager(backing Backing) *StoreManager {
   151  	sm := newStoreManagerNoRun(backing)
   152  	go func() {
   153  		defer sm.tomb.Done()
   154  		// TODO(rog) distinguish between temporary and permanent errors:
   155  		// if we get an error in loop, this logic kill the state's StoreManager
   156  		// forever. This currently fits the way we go about things,
   157  		// because we reconnect to the state on any error, but
   158  		// perhaps there are errors we could recover from.
   159  		sm.tomb.Kill(sm.loop())
   160  	}()
   161  	return sm
   162  }
   163  
   164  func (sm *StoreManager) loop() error {
   165  	in := make(chan watcher.Change)
   166  	sm.backing.Watch(in)
   167  	defer sm.backing.Unwatch(in)
   168  	// We have no idea what changes the watcher might be trying to
   169  	// send us while getAll proceeds, but we don't mind, because
   170  	// StoreManager.changed is idempotent with respect to both updates
   171  	// and removals.
   172  	// TODO(rog) Perhaps find a way to avoid blocking all other
   173  	// watchers while GetAll is running.
   174  	if err := sm.backing.GetAll(sm.all); err != nil {
   175  		return err
   176  	}
   177  	for {
   178  		select {
   179  		case <-sm.tomb.Dying():
   180  			return tomb.ErrDying
   181  		case change := <-in:
   182  			if err := sm.backing.Changed(sm.all, change); err != nil {
   183  				return err
   184  			}
   185  		case req := <-sm.request:
   186  			sm.handle(req)
   187  		}
   188  		sm.respond()
   189  	}
   190  }
   191  
   192  // Stop stops the StoreManager.
   193  func (sm *StoreManager) Stop() error {
   194  	sm.tomb.Kill(nil)
   195  	return sm.tomb.Wait()
   196  }
   197  
   198  // handle processes a request from a Watcher to the StoreManager.
   199  func (sm *StoreManager) handle(req *request) {
   200  	if req.w.stopped {
   201  		// The watcher has previously been stopped.
   202  		if req.reply != nil {
   203  			req.reply <- false
   204  		}
   205  		return
   206  	}
   207  	if req.reply == nil {
   208  		// This is a request to stop the watcher.
   209  		for req := sm.waiting[req.w]; req != nil; req = req.next {
   210  			req.reply <- false
   211  		}
   212  		delete(sm.waiting, req.w)
   213  		req.w.stopped = true
   214  		sm.leave(req.w)
   215  		return
   216  	}
   217  	// Add request to head of list.
   218  	req.next = sm.waiting[req.w]
   219  	sm.waiting[req.w] = req
   220  }
   221  
   222  // respond responds to all outstanding requests that are satisfiable.
   223  func (sm *StoreManager) respond() {
   224  	for w, req := range sm.waiting {
   225  		revno := w.revno
   226  		changes := sm.all.ChangesSince(revno)
   227  		if len(changes) == 0 {
   228  			continue
   229  		}
   230  		req.changes = changes
   231  		w.revno = sm.all.latestRevno
   232  		req.reply <- true
   233  		if req := req.next; req == nil {
   234  			// Last request for this watcher.
   235  			delete(sm.waiting, w)
   236  		} else {
   237  			sm.waiting[w] = req
   238  		}
   239  		sm.seen(revno)
   240  	}
   241  }
   242  
   243  // seen states that a Watcher has just been given information about
   244  // all entities newer than the given revno.  We assume it has already
   245  // seen all the older entities.
   246  func (sm *StoreManager) seen(revno int64) {
   247  	for e := sm.all.list.Front(); e != nil; {
   248  		next := e.Next()
   249  		entry := e.Value.(*entityEntry)
   250  		if entry.revno <= revno {
   251  			break
   252  		}
   253  		if entry.creationRevno > revno {
   254  			if !entry.removed {
   255  				// This is a new entity that hasn't been seen yet,
   256  				// so increment the entry's refCount.
   257  				entry.refCount++
   258  			}
   259  		} else if entry.removed {
   260  			// This is an entity that we previously saw, but
   261  			// has now been removed, so decrement its refCount, removing
   262  			// the entity if nothing else is waiting to be notified that it's
   263  			// gone.
   264  			sm.all.decRef(entry)
   265  		}
   266  		e = next
   267  	}
   268  }
   269  
   270  // leave is called when the given watcher leaves.  It decrements the reference
   271  // counts of any entities that have been seen by the watcher.
   272  func (sm *StoreManager) leave(w *Watcher) {
   273  	for e := sm.all.list.Front(); e != nil; {
   274  		next := e.Next()
   275  		entry := e.Value.(*entityEntry)
   276  		if entry.creationRevno <= w.revno {
   277  			// The watcher has seen this entry.
   278  			if entry.removed && entry.revno <= w.revno {
   279  				// The entity has been removed and the
   280  				// watcher has already been informed of that,
   281  				// so its refcount has already been decremented.
   282  				e = next
   283  				continue
   284  			}
   285  			sm.all.decRef(entry)
   286  		}
   287  		e = next
   288  	}
   289  }
   290  
   291  // entityEntry holds an entry in the linked list of all entities known
   292  // to a Watcher.
   293  type entityEntry struct {
   294  	// The revno holds the local idea of the latest change to the
   295  	// given entity.  It is not the same as the transaction revno -
   296  	// this means we can unconditionally move a newly fetched entity
   297  	// to the front of the list without worrying if the revno has
   298  	// changed since the watcher reported it.
   299  	revno int64
   300  
   301  	// creationRevno holds the revision number when the
   302  	// entity was created.
   303  	creationRevno int64
   304  
   305  	// removed marks whether the entity has been removed.
   306  	removed bool
   307  
   308  	// refCount holds a count of the number of watchers that
   309  	// have seen this entity. When the entity is marked as removed,
   310  	// the ref count is decremented whenever a Watcher that
   311  	// has previously seen the entry now sees that it has been removed;
   312  	// the entry will be deleted when all such Watchers have
   313  	// been notified.
   314  	refCount int
   315  
   316  	// info holds the actual information on the entity.
   317  	info params.EntityInfo
   318  }
   319  
   320  // Store holds a list of all entities known
   321  // to a Watcher.
   322  type Store struct {
   323  	latestRevno int64
   324  	entities    map[InfoId]*list.Element
   325  	list        *list.List
   326  }
   327  
   328  // NewStore returns an Store instance holding information about the
   329  // current state of all entities in the environment.
   330  // It is only exposed here for testing purposes.
   331  func NewStore() *Store {
   332  	all := &Store{
   333  		entities: make(map[InfoId]*list.Element),
   334  		list:     list.New(),
   335  	}
   336  	return all
   337  }
   338  
   339  // All returns all the entities stored in the Store,
   340  // oldest first. It is only exposed for testing purposes.
   341  func (a *Store) All() []params.EntityInfo {
   342  	entities := make([]params.EntityInfo, 0, a.list.Len())
   343  	for e := a.list.Front(); e != nil; e = e.Next() {
   344  		entry := e.Value.(*entityEntry)
   345  		if entry.removed {
   346  			continue
   347  		}
   348  		entities = append(entities, entry.info)
   349  	}
   350  	return entities
   351  }
   352  
   353  // add adds a new entity with the given id and associated
   354  // information to the list.
   355  func (a *Store) add(id InfoId, info params.EntityInfo) {
   356  	if a.entities[id] != nil {
   357  		panic("adding new entry with duplicate id")
   358  	}
   359  	a.latestRevno++
   360  	entry := &entityEntry{
   361  		info:          info,
   362  		revno:         a.latestRevno,
   363  		creationRevno: a.latestRevno,
   364  	}
   365  	a.entities[id] = a.list.PushFront(entry)
   366  }
   367  
   368  // decRef decrements the reference count of an entry within the list,
   369  // deleting it if it becomes zero and the entry is removed.
   370  func (a *Store) decRef(entry *entityEntry) {
   371  	if entry.refCount--; entry.refCount > 0 {
   372  		return
   373  	}
   374  	if entry.refCount < 0 {
   375  		panic("negative reference count")
   376  	}
   377  	if !entry.removed {
   378  		return
   379  	}
   380  	id := entry.info.EntityId()
   381  	elem := a.entities[id]
   382  	if elem == nil {
   383  		panic("delete of non-existent entry")
   384  	}
   385  	delete(a.entities, id)
   386  	a.list.Remove(elem)
   387  }
   388  
   389  // delete deletes the entry with the given info id.
   390  func (a *Store) delete(id params.EntityId) {
   391  	elem := a.entities[id]
   392  	if elem == nil {
   393  		return
   394  	}
   395  	delete(a.entities, id)
   396  	a.list.Remove(elem)
   397  }
   398  
   399  // Remove marks that the entity with the given id has
   400  // been removed from the backing. If nothing has seen the
   401  // entity, then we delete it immediately.
   402  func (a *Store) Remove(id params.EntityId) {
   403  	if elem := a.entities[id]; elem != nil {
   404  		entry := elem.Value.(*entityEntry)
   405  		if entry.removed {
   406  			return
   407  		}
   408  		a.latestRevno++
   409  		if entry.refCount == 0 {
   410  			a.delete(id)
   411  			return
   412  		}
   413  		entry.revno = a.latestRevno
   414  		entry.removed = true
   415  		a.list.MoveToFront(elem)
   416  	}
   417  }
   418  
   419  // Update updates the information for the given entity.
   420  func (a *Store) Update(info params.EntityInfo) {
   421  	id := info.EntityId()
   422  	elem := a.entities[id]
   423  	if elem == nil {
   424  		a.add(id, info)
   425  		return
   426  	}
   427  	entry := elem.Value.(*entityEntry)
   428  	// Nothing has changed, so change nothing.
   429  	// TODO(rog) do the comparison more efficiently.
   430  	if reflect.DeepEqual(info, entry.info) {
   431  		return
   432  	}
   433  	// We already know about the entity; update its doc.
   434  	a.latestRevno++
   435  	entry.revno = a.latestRevno
   436  	entry.info = info
   437  	a.list.MoveToFront(elem)
   438  }
   439  
   440  // Get returns the stored entity with the given
   441  // id, or nil if none was found. The contents of the returned entity
   442  // should not be changed.
   443  func (a *Store) Get(id params.EntityId) params.EntityInfo {
   444  	if e := a.entities[id]; e != nil {
   445  		return e.Value.(*entityEntry).info
   446  	}
   447  	return nil
   448  }
   449  
   450  // ChangesSince returns any changes that have occurred since
   451  // the given revno, oldest first.
   452  func (a *Store) ChangesSince(revno int64) []params.Delta {
   453  	e := a.list.Front()
   454  	n := 0
   455  	for ; e != nil; e = e.Next() {
   456  		entry := e.Value.(*entityEntry)
   457  		if entry.revno <= revno {
   458  			break
   459  		}
   460  		n++
   461  	}
   462  	if e != nil {
   463  		// We've found an element that we've already seen.
   464  		e = e.Prev()
   465  	} else {
   466  		// We haven't seen any elements, so we want all of them.
   467  		e = a.list.Back()
   468  		n++
   469  	}
   470  	changes := make([]params.Delta, 0, n)
   471  	for ; e != nil; e = e.Prev() {
   472  		entry := e.Value.(*entityEntry)
   473  		if entry.removed && entry.creationRevno > revno {
   474  			// Don't include entries that have been created
   475  			// and removed since the revno.
   476  			continue
   477  		}
   478  		changes = append(changes, params.Delta{
   479  			Removed: entry.removed,
   480  			Entity:  entry.info,
   481  		})
   482  	}
   483  	return changes
   484  }