github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/periodicworker.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package worker 5 6 import ( 7 "errors" 8 "time" 9 10 "launchpad.net/tomb" 11 ) 12 13 // ErrKilled can be returned by the PeriodicWorkerCall to signify that 14 // the function has returned as a result of a Stop() or Kill() signal 15 // and that the function was able to stop cleanly 16 var ErrKilled = errors.New("worker killed") 17 18 // periodicWorker implements the worker returned by NewPeriodicWorker. 19 type periodicWorker struct { 20 tomb tomb.Tomb 21 newTimer NewTimerFunc 22 } 23 24 // PeriodicWorkerCall represents the callable to be passed 25 // to the periodic worker to be run every elapsed period. 26 type PeriodicWorkerCall func(stop <-chan struct{}) error 27 28 // PeriodicTimer is an interface for the timer that periodicworker 29 // will use to handle the calls. 30 type PeriodicTimer interface { 31 // Reset changes the timer to expire after duration d. 32 // It returns true if the timer had been active, false 33 // if the timer had expired or been stopped. 34 Reset(time.Duration) bool 35 // CountDown returns the channel used to signal expiration of 36 // the timer duration. The channel is called C in the base 37 // implementation of timer but the name is confusing. 38 CountDown() <-chan time.Time 39 } 40 41 // NewTimerFunc is a constructor used to obtain the instance 42 // of PeriodicTimer periodicWorker uses on its loop. 43 // TODO(fwereade): 2016-03-17 lp:1558657 44 type NewTimerFunc func(time.Duration) PeriodicTimer 45 46 // Timer implements PeriodicTimer. 47 type Timer struct { 48 timer *time.Timer 49 } 50 51 // Reset implements PeriodicTimer. 52 func (t *Timer) Reset(d time.Duration) bool { 53 return t.timer.Reset(d) 54 } 55 56 // CountDown implements PeriodicTimer. 57 func (t *Timer) CountDown() <-chan time.Time { 58 return t.timer.C 59 } 60 61 // NewTimer is the default implementation of NewTimerFunc. 62 func NewTimer(d time.Duration) PeriodicTimer { 63 return &Timer{time.NewTimer(d)} 64 } 65 66 // NewPeriodicWorker returns a worker that runs the given function continually 67 // sleeping for sleepDuration in between each call, until Kill() is called 68 // The stopCh argument will be closed when the worker is killed. The error returned 69 // by the doWork function will be returned by the worker's Wait function. 70 func NewPeriodicWorker(call PeriodicWorkerCall, period time.Duration, timerFunc NewTimerFunc) Worker { 71 w := &periodicWorker{newTimer: timerFunc} 72 go func() { 73 defer w.tomb.Done() 74 w.tomb.Kill(w.run(call, period)) 75 }() 76 return w 77 } 78 79 func (w *periodicWorker) run(call PeriodicWorkerCall, period time.Duration) error { 80 timer := w.newTimer(0) 81 stop := w.tomb.Dying() 82 for { 83 select { 84 case <-stop: 85 return tomb.ErrDying 86 case <-timer.CountDown(): 87 if err := call(stop); err != nil { 88 if err == ErrKilled { 89 return tomb.ErrDying 90 } 91 return err 92 } 93 } 94 timer.Reset(period) 95 } 96 } 97 98 // Kill implements Worker.Kill() and will close the channel given to the doWork 99 // function. 100 func (w *periodicWorker) Kill() { 101 w.tomb.Kill(nil) 102 } 103 104 // Wait implements Worker.Wait(), and will return the error returned by 105 // the doWork function. 106 func (w *periodicWorker) Wait() error { 107 return w.tomb.Wait() 108 }