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