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