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