github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/relation/peeker.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package relation 5 6 import ( 7 "github.com/juju/errors" 8 "launchpad.net/tomb" 9 10 "github.com/juju/juju/state/watcher" 11 "github.com/juju/juju/worker/uniter/hook" 12 ) 13 14 // Peeker maintains a HookSource, and allows an external client to inspect 15 // and consume, or reject, the head of the queue. 16 type Peeker interface { 17 // Peeks returns a channel on which Peeks are delivered. The receiver of a 18 // Peek must Consume or Reject the Peek before further peeks will be sent. 19 Peeks() <-chan Peek 20 // Stop stops the Peeker's HookSource, and prevents any further Peeks from 21 // being delivered. 22 Stop() error 23 } 24 25 // Peek exists to support Peeker and has no independent meaning or existence. 26 // Receiving a Peek from a Peeker's Peek channel implies acceptance of the 27 // responsibility to either Consume or Reject the Peek. 28 type Peek interface { 29 // HookInfo returns information about the hook at the head of the queue. 30 HookInfo() hook.Info 31 // Consume pops the hook from the head of the queue and makes new Peeks 32 // available. 33 Consume() 34 // Reject makes new Peeks available. 35 Reject() 36 } 37 38 // NewPeeker returns a new Peeker providing a view of the supplied source 39 // (of which it takes ownership). 40 func NewPeeker(source HookSource) Peeker { 41 p := &peeker{ 42 peeks: make(chan Peek), 43 } 44 go func() { 45 defer p.tomb.Done() 46 defer close(p.peeks) 47 defer watcher.Stop(source, &p.tomb) 48 p.tomb.Kill(p.loop(source)) 49 }() 50 return p 51 } 52 53 // peeker implements Peeker. 54 type peeker struct { 55 tomb tomb.Tomb 56 peeks chan Peek 57 } 58 59 // Peeks is part of the Peeker interface. 60 func (p *peeker) Peeks() <-chan Peek { 61 return p.peeks 62 } 63 64 // Stop is part of the Peeker interface. 65 func (p *peeker) Stop() error { 66 p.tomb.Kill(nil) 67 return p.tomb.Wait() 68 } 69 70 // loop delivers events from the source's Changes channel to its Update method, 71 // continually, unless a Peek is active. 72 func (p *peeker) loop(source HookSource) error { 73 for { 74 var next *peek 75 var peeks chan Peek 76 if !source.Empty() { 77 peeks = p.peeks 78 next = &peek{ 79 source: source, 80 done: make(chan struct{}), 81 } 82 } 83 select { 84 case <-p.tomb.Dying(): 85 return tomb.ErrDying 86 case peeks <- next: 87 select { 88 case <-p.tomb.Dying(): 89 case <-next.done: 90 } 91 case change, ok := <-source.Changes(): 92 if !ok { 93 return errors.New("hook source stopped providing updates") 94 } 95 if err := source.Update(change); err != nil { 96 return errors.Trace(err) 97 } 98 } 99 } 100 } 101 102 // peek implements Peek. 103 type peek struct { 104 source HookSource 105 done chan struct{} 106 } 107 108 // HookInfo is part of the Peek interface. 109 func (p *peek) HookInfo() hook.Info { 110 return p.source.Next() 111 } 112 113 // Consume is part of the Peek interface. 114 func (p *peek) Consume() { 115 p.source.Pop() 116 close(p.done) 117 } 118 119 // Reject is part of the Peek interface. 120 func (p *peek) Reject() { 121 close(p.done) 122 }