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