github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/api/watcher/watcher.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package watcher
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/juju/loggo"
    10  	"launchpad.net/tomb"
    11  
    12  	"github.com/juju/juju/state/api/base"
    13  	"github.com/juju/juju/state/api/params"
    14  )
    15  
    16  var logger = loggo.GetLogger("juju.state.api.watcher")
    17  
    18  // commonWatcher implements common watcher logic in one place to
    19  // reduce code duplication, but it's not in fact a complete watcher;
    20  // it's intended for embedding.
    21  type commonWatcher struct {
    22  	tomb tomb.Tomb
    23  	wg   sync.WaitGroup
    24  	in   chan interface{}
    25  
    26  	// These fields must be set by the embedding watcher, before
    27  	// calling init().
    28  
    29  	// newResult must return a pointer to a value of the type returned
    30  	// by the watcher's Next call.
    31  	newResult func() interface{}
    32  
    33  	// call should invoke the given API method, placing the call's
    34  	// returned value in result (if any).
    35  	call func(method string, result interface{}) error
    36  }
    37  
    38  // init must be called to initialize an embedded commonWatcher's
    39  // fields. Make sure newResult and call fields are set beforehand.
    40  func (w *commonWatcher) init() {
    41  	w.in = make(chan interface{})
    42  	if w.newResult == nil {
    43  		panic("newResult must me set")
    44  	}
    45  	if w.call == nil {
    46  		panic("call must be set")
    47  	}
    48  }
    49  
    50  // commonLoop implements the loop structure common to the client
    51  // watchers. It should be started in a separate goroutine by any
    52  // watcher that embeds commonWatcher. It kills the commonWatcher's
    53  // tomb when an error occurs.
    54  func (w *commonWatcher) commonLoop() {
    55  	defer close(w.in)
    56  	w.wg.Add(1)
    57  	go func() {
    58  		// When the watcher has been stopped, we send a Stop request
    59  		// to the server, which will remove the watcher and return a
    60  		// CodeStopped error to any currently outstanding call to
    61  		// Next. If a call to Next happens just after the watcher has
    62  		// been stopped, we'll get a CodeNotFound error; Either way
    63  		// we'll return, wait for the stop request to complete, and
    64  		// the watcher will die with all resources cleaned up.
    65  		defer w.wg.Done()
    66  		<-w.tomb.Dying()
    67  		if err := w.call("Stop", nil); err != nil {
    68  			logger.Errorf("error trying to stop watcher: %v", err)
    69  		}
    70  	}()
    71  	w.wg.Add(1)
    72  	go func() {
    73  		// Because Next blocks until there are changes, we need to
    74  		// call it in a separate goroutine, so the watcher can be
    75  		// stopped normally.
    76  		defer w.wg.Done()
    77  		for {
    78  			result := w.newResult()
    79  			err := w.call("Next", &result)
    80  			if err != nil {
    81  				if params.IsCodeStopped(err) || params.IsCodeNotFound(err) {
    82  					if w.tomb.Err() != tomb.ErrStillAlive {
    83  						// The watcher has been stopped at the client end, so we're
    84  						// expecting one of the above two kinds of error.
    85  						// We might see the same errors if the server itself
    86  						// has been shut down, in which case we leave them
    87  						// untouched.
    88  						err = tomb.ErrDying
    89  					}
    90  				}
    91  				// Something went wrong, just report the error and bail out.
    92  				w.tomb.Kill(err)
    93  				return
    94  			}
    95  			select {
    96  			case <-w.tomb.Dying():
    97  				return
    98  			case w.in <- result:
    99  				// Report back the result we just got.
   100  			}
   101  		}
   102  	}()
   103  	w.wg.Wait()
   104  }
   105  
   106  func (w *commonWatcher) Stop() error {
   107  	w.tomb.Kill(nil)
   108  	return w.tomb.Wait()
   109  }
   110  
   111  func (w *commonWatcher) Err() error {
   112  	return w.tomb.Err()
   113  }
   114  
   115  // notifyWatcher will send events when something changes.
   116  // It does not send content for those changes.
   117  type notifyWatcher struct {
   118  	commonWatcher
   119  	caller          base.Caller
   120  	notifyWatcherId string
   121  	out             chan struct{}
   122  }
   123  
   124  // If an API call returns a NotifyWatchResult, you can use this to turn it into
   125  // a local Watcher.
   126  func NewNotifyWatcher(caller base.Caller, result params.NotifyWatchResult) NotifyWatcher {
   127  	w := &notifyWatcher{
   128  		caller:          caller,
   129  		notifyWatcherId: result.NotifyWatcherId,
   130  		out:             make(chan struct{}),
   131  	}
   132  	go func() {
   133  		defer w.tomb.Done()
   134  		defer close(w.out)
   135  		defer w.wg.Wait() // Wait for watcher to be stopped.
   136  		w.tomb.Kill(w.loop())
   137  	}()
   138  	return w
   139  }
   140  
   141  func (w *notifyWatcher) loop() error {
   142  	// No results for this watcher type.
   143  	w.newResult = func() interface{} { return nil }
   144  	w.call = func(request string, result interface{}) error {
   145  		return w.caller.Call("NotifyWatcher", w.notifyWatcherId, request, nil, &result)
   146  	}
   147  	w.commonWatcher.init()
   148  	go w.commonLoop()
   149  
   150  	for {
   151  		select {
   152  		// Since for a notifyWatcher there are no changes to send, we
   153  		// just set the event (initial first, then after each change).
   154  		case w.out <- struct{}{}:
   155  		case <-w.tomb.Dying():
   156  			return nil
   157  		}
   158  		if _, ok := <-w.in; !ok {
   159  			// The tomb is already killed with the correct
   160  			// error at this point, so just return.
   161  			return nil
   162  		}
   163  	}
   164  	return nil
   165  }
   166  
   167  // Changes returns a channel that receives a value when a given entity
   168  // changes in some way.
   169  func (w *notifyWatcher) Changes() <-chan struct{} {
   170  	return w.out
   171  }
   172  
   173  // stringsWatcher will send events when something changes.
   174  // The content of the changes is a list of strings.
   175  type stringsWatcher struct {
   176  	commonWatcher
   177  	caller           base.Caller
   178  	stringsWatcherId string
   179  	out              chan []string
   180  }
   181  
   182  func NewStringsWatcher(caller base.Caller, result params.StringsWatchResult) StringsWatcher {
   183  	w := &stringsWatcher{
   184  		caller:           caller,
   185  		stringsWatcherId: result.StringsWatcherId,
   186  		out:              make(chan []string),
   187  	}
   188  	go func() {
   189  		defer w.tomb.Done()
   190  		defer close(w.out)
   191  		w.tomb.Kill(w.loop(result.Changes))
   192  	}()
   193  	return w
   194  }
   195  
   196  func (w *stringsWatcher) loop(initialChanges []string) error {
   197  	changes := initialChanges
   198  	w.newResult = func() interface{} { return new(params.StringsWatchResult) }
   199  	w.call = func(request string, result interface{}) error {
   200  		return w.caller.Call("StringsWatcher", w.stringsWatcherId, request, nil, &result)
   201  	}
   202  	w.commonWatcher.init()
   203  	go w.commonLoop()
   204  
   205  	for {
   206  		select {
   207  		// Send the initial event or subsequent change.
   208  		case w.out <- changes:
   209  		case <-w.tomb.Dying():
   210  			return nil
   211  		}
   212  		// Read the next change.
   213  		data, ok := <-w.in
   214  		if !ok {
   215  			// The tomb is already killed with the correct error
   216  			// at this point, so just return.
   217  			return nil
   218  		}
   219  		changes = data.(*params.StringsWatchResult).Changes
   220  	}
   221  	return nil
   222  }
   223  
   224  // Changes returns a channel that receives a list of strings of watched
   225  // entites with changes.
   226  func (w *stringsWatcher) Changes() <-chan []string {
   227  	return w.out
   228  }
   229  
   230  // relationUnitsWatcher will sends notifications of units entering and
   231  // leaving the scope of a RelationUnit, and changes to the settings of
   232  // those units known to have entered.
   233  type relationUnitsWatcher struct {
   234  	commonWatcher
   235  	caller                 base.Caller
   236  	relationUnitsWatcherId string
   237  	out                    chan params.RelationUnitsChange
   238  }
   239  
   240  func NewRelationUnitsWatcher(caller base.Caller, result params.RelationUnitsWatchResult) RelationUnitsWatcher {
   241  	w := &relationUnitsWatcher{
   242  		caller:                 caller,
   243  		relationUnitsWatcherId: result.RelationUnitsWatcherId,
   244  		out: make(chan params.RelationUnitsChange),
   245  	}
   246  	go func() {
   247  		defer w.tomb.Done()
   248  		defer close(w.out)
   249  		w.tomb.Kill(w.loop(result.Changes))
   250  	}()
   251  	return w
   252  }
   253  
   254  func (w *relationUnitsWatcher) loop(initialChanges params.RelationUnitsChange) error {
   255  	changes := initialChanges
   256  	w.newResult = func() interface{} { return new(params.RelationUnitsWatchResult) }
   257  	w.call = func(request string, result interface{}) error {
   258  		return w.caller.Call("RelationUnitsWatcher", w.relationUnitsWatcherId, request, nil, &result)
   259  	}
   260  	w.commonWatcher.init()
   261  	go w.commonLoop()
   262  
   263  	for {
   264  		select {
   265  		// Send the initial event or subsequent change.
   266  		case w.out <- changes:
   267  		case <-w.tomb.Dying():
   268  			return nil
   269  		}
   270  		// Read the next change.
   271  		data, ok := <-w.in
   272  		if !ok {
   273  			// The tomb is already killed with the correct error
   274  			// at this point, so just return.
   275  			return nil
   276  		}
   277  		changes = data.(*params.RelationUnitsWatchResult).Changes
   278  	}
   279  	return nil
   280  }
   281  
   282  // Changes returns a channel that will receive the changes to
   283  // counterpart units in a relation. The first event on the channel
   284  // holds the initial state of the relation in its Changed field.
   285  func (w *relationUnitsWatcher) Changes() <-chan params.RelationUnitsChange {
   286  	return w.out
   287  }