launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/multiwatcher/multiwatcher.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package multiwatcher 5 6 import ( 7 "container/list" 8 "launchpad.net/errgo/errors" 9 "reflect" 10 11 "launchpad.net/tomb" 12 13 "launchpad.net/juju-core/state/api/params" 14 "launchpad.net/juju-core/state/watcher" 15 ) 16 17 var mask = errors.Mask 18 19 // Watcher watches any changes to the state. 20 type Watcher struct { 21 all *StoreManager 22 23 // The following fields are maintained by the StoreManager 24 // goroutine. 25 revno int64 26 stopped bool 27 } 28 29 // NewWatcher creates a new watcher that can observe 30 // changes to an underlying store manager. 31 func NewWatcher(all *StoreManager) *Watcher { 32 return &Watcher{ 33 all: all, 34 } 35 } 36 37 // Stop stops the watcher. 38 func (w *Watcher) Stop() error { 39 select { 40 case w.all.request <- &request{w: w}: 41 return nil 42 case <-w.all.tomb.Dead(): 43 } 44 return w.all.tomb.Err() 45 } 46 47 var ErrWatcherStopped = errors.New("watcher was stopped") 48 49 // Next retrieves all changes that have happened since the last 50 // time it was called, blocking until there are some changes available. 51 func (w *Watcher) Next() ([]params.Delta, error) { 52 req := &request{ 53 w: w, 54 reply: make(chan bool), 55 } 56 select { 57 case w.all.request <- req: 58 case <-w.all.tomb.Dead(): 59 err := w.all.tomb.Err() 60 if err == nil { 61 err = errors.New("shared state watcher was stopped") 62 } 63 return nil, err 64 } 65 if ok := <-req.reply; !ok { 66 return nil, ErrWatcherStopped 67 } 68 return req.changes, nil 69 } 70 71 // StoreManager holds a shared record of current state and replies to 72 // requests from Watchers to tell them when it changes. 73 type StoreManager struct { 74 tomb tomb.Tomb 75 76 // backing knows how to fetch information from 77 // the underlying state. 78 backing Backing 79 80 // request receives requests from Watcher clients. 81 request chan *request 82 83 // all holds information on everything the StoreManager cares about. 84 all *Store 85 86 // Each entry in the waiting map holds a linked list of Next requests 87 // outstanding for the associated Watcher. 88 waiting map[*Watcher]*request 89 } 90 91 // InfoId holds an identifier for an Info item held in a Store. 92 type InfoId interface{} 93 94 // Backing is the interface required by the StoreManager to access the 95 // underlying state. 96 type Backing interface { 97 98 // GetAll retrieves information about all information 99 // known to the Backing and stashes it in the Store. 100 GetAll(all *Store) error 101 102 // Changed informs the backing about a change received 103 // from a watcher channel. The backing is responsible for 104 // updating the Store to reflect the change. 105 Changed(all *Store, change watcher.Change) error 106 107 // Watch watches for any changes and sends them 108 // on the given channel. 109 Watch(in chan<- watcher.Change) 110 111 // Unwatch stops watching for changes on the 112 // given channel. 113 Unwatch(in chan<- watcher.Change) 114 } 115 116 // request holds a message from the Watcher to the 117 // StoreManager for some changes. The request will be 118 // replied to when some changes are available. 119 type request struct { 120 // w holds the Watcher that has originated the request. 121 w *Watcher 122 123 // reply receives a message when deltas are ready. If reply is 124 // nil, the Watcher will be stopped. If the reply is true, 125 // the request has been processed; if false, the Watcher 126 // has been stopped, 127 reply chan bool 128 129 // On reply, changes will hold changes that have occurred since 130 // the last replied-to Next request. 131 changes []params.Delta 132 133 // next points to the next request in the list of outstanding 134 // requests on a given watcher. It is used only by the central 135 // StoreManager goroutine. 136 next *request 137 } 138 139 // newStoreManagerNoRun creates the store manager 140 // but does not start its run loop. 141 func newStoreManagerNoRun(backing Backing) *StoreManager { 142 return &StoreManager{ 143 backing: backing, 144 request: make(chan *request), 145 all: NewStore(), 146 waiting: make(map[*Watcher]*request), 147 } 148 } 149 150 // NewStoreManager returns a new StoreManager that retrieves information 151 // using the given backing. 152 func NewStoreManager(backing Backing) *StoreManager { 153 sm := newStoreManagerNoRun(backing) 154 go func() { 155 defer sm.tomb.Done() 156 // TODO(rog) distinguish between temporary and permanent errors: 157 // if we get an error in loop, this logic kill the state's StoreManager 158 // forever. This currently fits the way we go about things, 159 // because we reconnect to the state on any error, but 160 // perhaps there are errors we could recover from. 161 sm.tomb.Kill(sm.loop()) 162 }() 163 return sm 164 } 165 166 func (sm *StoreManager) loop() error { 167 in := make(chan watcher.Change) 168 sm.backing.Watch(in) 169 defer sm.backing.Unwatch(in) 170 // We have no idea what changes the watcher might be trying to 171 // send us while getAll proceeds, but we don't mind, because 172 // StoreManager.changed is idempotent with respect to both updates 173 // and removals. 174 // TODO(rog) Perhaps find a way to avoid blocking all other 175 // watchers while GetAll is running. 176 if err := sm.backing.GetAll(sm.all); err != nil { 177 return mask(err) 178 } 179 for { 180 select { 181 case <-sm.tomb.Dying(): 182 return tomb.ErrDying 183 case change := <-in: 184 if err := sm.backing.Changed(sm.all, change); err != nil { 185 return mask(err) 186 } 187 case req := <-sm.request: 188 sm.handle(req) 189 } 190 sm.respond() 191 } 192 } 193 194 // Stop stops the StoreManager. 195 func (sm *StoreManager) Stop() error { 196 sm.tomb.Kill(nil) 197 return sm.tomb.Wait() 198 } 199 200 // handle processes a request from a Watcher to the StoreManager. 201 func (sm *StoreManager) handle(req *request) { 202 if req.w.stopped { 203 // The watcher has previously been stopped. 204 if req.reply != nil { 205 req.reply <- false 206 } 207 return 208 } 209 if req.reply == nil { 210 // This is a request to stop the watcher. 211 for req := sm.waiting[req.w]; req != nil; req = req.next { 212 req.reply <- false 213 } 214 delete(sm.waiting, req.w) 215 req.w.stopped = true 216 sm.leave(req.w) 217 return 218 } 219 // Add request to head of list. 220 req.next = sm.waiting[req.w] 221 sm.waiting[req.w] = req 222 } 223 224 // respond responds to all outstanding requests that are satisfiable. 225 func (sm *StoreManager) respond() { 226 for w, req := range sm.waiting { 227 revno := w.revno 228 changes := sm.all.ChangesSince(revno) 229 if len(changes) == 0 { 230 continue 231 } 232 req.changes = changes 233 w.revno = sm.all.latestRevno 234 req.reply <- true 235 if req := req.next; req == nil { 236 // Last request for this watcher. 237 delete(sm.waiting, w) 238 } else { 239 sm.waiting[w] = req 240 } 241 sm.seen(revno) 242 } 243 } 244 245 // seen states that a Watcher has just been given information about 246 // all entities newer than the given revno. We assume it has already 247 // seen all the older entities. 248 func (sm *StoreManager) seen(revno int64) { 249 for e := sm.all.list.Front(); e != nil; { 250 next := e.Next() 251 entry := e.Value.(*entityEntry) 252 if entry.revno <= revno { 253 break 254 } 255 if entry.creationRevno > revno { 256 if !entry.removed { 257 // This is a new entity that hasn't been seen yet, 258 // so increment the entry's refCount. 259 entry.refCount++ 260 } 261 } else if entry.removed { 262 // This is an entity that we previously saw, but 263 // has now been removed, so decrement its refCount, removing 264 // the entity if nothing else is waiting to be notified that it's 265 // gone. 266 sm.all.decRef(entry) 267 } 268 e = next 269 } 270 } 271 272 // leave is called when the given watcher leaves. It decrements the reference 273 // counts of any entities that have been seen by the watcher. 274 func (sm *StoreManager) leave(w *Watcher) { 275 for e := sm.all.list.Front(); e != nil; { 276 next := e.Next() 277 entry := e.Value.(*entityEntry) 278 if entry.creationRevno <= w.revno { 279 // The watcher has seen this entry. 280 if entry.removed && entry.revno <= w.revno { 281 // The entity has been removed and the 282 // watcher has already been informed of that, 283 // so its refcount has already been decremented. 284 e = next 285 continue 286 } 287 sm.all.decRef(entry) 288 } 289 e = next 290 } 291 } 292 293 // entityEntry holds an entry in the linked list of all entities known 294 // to a Watcher. 295 type entityEntry struct { 296 // The revno holds the local idea of the latest change to the 297 // given entity. It is not the same as the transaction revno - 298 // this means we can unconditionally move a newly fetched entity 299 // to the front of the list without worrying if the revno has 300 // changed since the watcher reported it. 301 revno int64 302 303 // creationRevno holds the revision number when the 304 // entity was created. 305 creationRevno int64 306 307 // removed marks whether the entity has been removed. 308 removed bool 309 310 // refCount holds a count of the number of watchers that 311 // have seen this entity. When the entity is marked as removed, 312 // the ref count is decremented whenever a Watcher that 313 // has previously seen the entry now sees that it has been removed; 314 // the entry will be deleted when all such Watchers have 315 // been notified. 316 refCount int 317 318 // info holds the actual information on the entity. 319 info params.EntityInfo 320 } 321 322 // Store holds a list of all entities known 323 // to a Watcher. 324 type Store struct { 325 latestRevno int64 326 entities map[InfoId]*list.Element 327 list *list.List 328 } 329 330 // NewStore returns an Store instance holding information about the 331 // current state of all entities in the environment. 332 // It is only exposed here for testing purposes. 333 func NewStore() *Store { 334 all := &Store{ 335 entities: make(map[InfoId]*list.Element), 336 list: list.New(), 337 } 338 return all 339 } 340 341 // All returns all the entities stored in the Store, 342 // oldest first. It is only exposed for testing purposes. 343 func (a *Store) All() []params.EntityInfo { 344 entities := make([]params.EntityInfo, 0, a.list.Len()) 345 for e := a.list.Front(); e != nil; e = e.Next() { 346 entry := e.Value.(*entityEntry) 347 if entry.removed { 348 continue 349 } 350 entities = append(entities, entry.info) 351 } 352 return entities 353 } 354 355 // add adds a new entity with the given id and associated 356 // information to the list. 357 func (a *Store) add(id InfoId, info params.EntityInfo) { 358 if a.entities[id] != nil { 359 panic("adding new entry with duplicate id") 360 } 361 a.latestRevno++ 362 entry := &entityEntry{ 363 info: info, 364 revno: a.latestRevno, 365 creationRevno: a.latestRevno, 366 } 367 a.entities[id] = a.list.PushFront(entry) 368 } 369 370 // decRef decrements the reference count of an entry within the list, 371 // deleting it if it becomes zero and the entry is removed. 372 func (a *Store) decRef(entry *entityEntry) { 373 if entry.refCount--; entry.refCount > 0 { 374 return 375 } 376 if entry.refCount < 0 { 377 panic("negative reference count") 378 } 379 if !entry.removed { 380 return 381 } 382 id := entry.info.EntityId() 383 elem := a.entities[id] 384 if elem == nil { 385 panic("delete of non-existent entry") 386 } 387 delete(a.entities, id) 388 a.list.Remove(elem) 389 } 390 391 // delete deletes the entry with the given info id. 392 func (a *Store) delete(id params.EntityId) { 393 elem := a.entities[id] 394 if elem == nil { 395 return 396 } 397 delete(a.entities, id) 398 a.list.Remove(elem) 399 } 400 401 // Remove marks that the entity with the given id has 402 // been removed from the backing. If nothing has seen the 403 // entity, then we delete it immediately. 404 func (a *Store) Remove(id params.EntityId) { 405 if elem := a.entities[id]; elem != nil { 406 entry := elem.Value.(*entityEntry) 407 if entry.removed { 408 return 409 } 410 a.latestRevno++ 411 if entry.refCount == 0 { 412 a.delete(id) 413 return 414 } 415 entry.revno = a.latestRevno 416 entry.removed = true 417 a.list.MoveToFront(elem) 418 } 419 } 420 421 // Update updates the information for the given entity. 422 func (a *Store) Update(info params.EntityInfo) { 423 id := info.EntityId() 424 elem := a.entities[id] 425 if elem == nil { 426 a.add(id, info) 427 return 428 } 429 entry := elem.Value.(*entityEntry) 430 // Nothing has changed, so change nothing. 431 // TODO(rog) do the comparison more efficiently. 432 if reflect.DeepEqual(info, entry.info) { 433 return 434 } 435 // We already know about the entity; update its doc. 436 a.latestRevno++ 437 entry.revno = a.latestRevno 438 entry.info = info 439 a.list.MoveToFront(elem) 440 } 441 442 // Get returns the stored entity with the given 443 // id, or nil if none was found. The contents of the returned entity 444 // should not be changed. 445 func (a *Store) Get(id params.EntityId) params.EntityInfo { 446 if e := a.entities[id]; e != nil { 447 return e.Value.(*entityEntry).info 448 } 449 return nil 450 } 451 452 // ChangesSince returns any changes that have occurred since 453 // the given revno, oldest first. 454 func (a *Store) ChangesSince(revno int64) []params.Delta { 455 e := a.list.Front() 456 n := 0 457 for ; e != nil; e = e.Next() { 458 entry := e.Value.(*entityEntry) 459 if entry.revno <= revno { 460 break 461 } 462 n++ 463 } 464 if e != nil { 465 // We've found an element that we've already seen. 466 e = e.Prev() 467 } else { 468 // We haven't seen any elements, so we want all of them. 469 e = a.list.Back() 470 n++ 471 } 472 changes := make([]params.Delta, 0, n) 473 for ; e != nil; e = e.Prev() { 474 entry := e.Value.(*entityEntry) 475 if entry.removed && entry.creationRevno > revno { 476 // Don't include entries that have been created 477 // and removed since the revno. 478 continue 479 } 480 changes = append(changes, params.Delta{ 481 Removed: entry.removed, 482 Entity: entry.info, 483 }) 484 } 485 return changes 486 }