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