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