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 := ¬ifyWatcher{ 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 }