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