github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/relation/hooksender.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package relation 5 6 import ( 7 "launchpad.net/tomb" 8 9 "github.com/juju/errors" 10 11 "github.com/juju/juju/state/watcher" 12 "github.com/juju/juju/worker/uniter/hook" 13 ) 14 15 // HookSender maintains a HookSource and delivers its hooks via a channel. 16 type HookSender interface { 17 Stop() error 18 } 19 20 // NewHookSender starts sending hooks from source onto the out channel, and will 21 // continue to do so until Stop()ped (or the source is exhausted). NewHookSender 22 // takes ownership of the supplied source, and responsibility for cleaning it up; 23 // but it will not close the out channel. 24 func NewHookSender(out chan<- hook.Info, source HookSource) HookSender { 25 sender := &hookSender{ 26 out: out, 27 } 28 go func() { 29 defer sender.tomb.Done() 30 defer watcher.Stop(source, &sender.tomb) 31 sender.tomb.Kill(sender.loop(source)) 32 }() 33 return sender 34 } 35 36 // hookSender implements HookSender. 37 type hookSender struct { 38 tomb tomb.Tomb 39 out chan<- hook.Info 40 } 41 42 // Stop stops the HookSender and returns any errors encountered during 43 // operation or while shutting down. 44 func (sender *hookSender) Stop() error { 45 sender.tomb.Kill(nil) 46 return sender.tomb.Wait() 47 } 48 49 // loop synchronously delivers the source's change events to its update method, 50 // and, whenever the source is nonempty, repeatedly sends its first scheduled 51 // event on the out chan (and pops it from the source). 52 func (sender *hookSender) loop(source HookSource) error { 53 var next hook.Info 54 var out chan<- hook.Info 55 for { 56 if source.Empty() { 57 out = nil 58 } else { 59 out = sender.out 60 next = source.Next() 61 } 62 select { 63 case <-sender.tomb.Dying(): 64 return tomb.ErrDying 65 case out <- next: 66 source.Pop() 67 case change, ok := <-source.Changes(): 68 if !ok { 69 return errors.New("hook source stopped providing updates") 70 } 71 if err := source.Update(change); err != nil { 72 return errors.Trace(err) 73 } 74 } 75 } 76 }