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