github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/notifyworker.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package worker 5 6 import ( 7 "launchpad.net/tomb" 8 9 apiWatcher "github.com/juju/juju/api/watcher" 10 "github.com/juju/juju/state/watcher" 11 ) 12 13 // ensureErr is defined as a variable to allow the test suite 14 // to override it. 15 var ensureErr = watcher.EnsureErr 16 17 // notifyWorker is the internal implementation of the Worker 18 // interface, using a NotifyWatcher for handling changes. 19 type notifyWorker struct { 20 tomb tomb.Tomb 21 22 // handler is what will handle when events are triggered 23 handler NotifyWatchHandler 24 } 25 26 // NotifyWatchHandler implements the business logic that is triggered 27 // as part of watching a NotifyWatcher. 28 type NotifyWatchHandler interface { 29 // SetUp starts the handler, this should create the watcher we 30 // will be waiting on for more events. SetUp can return a Watcher 31 // even if there is an error, and the notify Worker will make sure 32 // to stop the watcher. 33 SetUp() (apiWatcher.NotifyWatcher, error) 34 35 // TearDown should cleanup any resources that are left around 36 TearDown() error 37 38 // Handle is called when the Watcher has indicated there are 39 // changes, do whatever work is necessary to process it 40 Handle() error 41 } 42 43 // NewNotifyWorker starts a new worker running the business logic from 44 // the handler. The worker loop is started in another goroutine as a 45 // side effect of calling this. 46 func NewNotifyWorker(handler NotifyWatchHandler) Worker { 47 nw := ¬ifyWorker{ 48 handler: handler, 49 } 50 go func() { 51 defer nw.tomb.Done() 52 nw.tomb.Kill(nw.loop()) 53 }() 54 return nw 55 } 56 57 // Kill the loop with no-error 58 func (nw *notifyWorker) Kill() { 59 nw.tomb.Kill(nil) 60 } 61 62 // Wait for the looping to finish 63 func (nw *notifyWorker) Wait() error { 64 return nw.tomb.Wait() 65 } 66 67 type tearDowner interface { 68 TearDown() error 69 } 70 71 // propagateTearDown tears down the handler, but ensures any error is 72 // propagated through the tomb's Kill method. 73 func propagateTearDown(handler tearDowner, t *tomb.Tomb) { 74 if err := handler.TearDown(); err != nil { 75 t.Kill(err) 76 } 77 } 78 79 func (nw *notifyWorker) loop() error { 80 w, err := nw.handler.SetUp() 81 if err != nil { 82 if w != nil { 83 // We don't bother to propagate an error, because we 84 // already have an error 85 w.Stop() 86 } 87 return err 88 } 89 defer propagateTearDown(nw.handler, &nw.tomb) 90 defer watcher.Stop(w, &nw.tomb) 91 for { 92 select { 93 case <-nw.tomb.Dying(): 94 return tomb.ErrDying 95 case _, ok := <-w.Changes(): 96 if !ok { 97 return ensureErr(w) 98 } 99 if err := nw.handler.Handle(); err != nil { 100 return err 101 } 102 } 103 } 104 }