launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/utils/attempt.go (about) 1 // Copyright 2011, 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package utils 5 6 import "time" 7 8 // The Attempt and AttemptStrategy types are copied from those in launchpad.net/goamz/aws. 9 10 // AttemptStrategy represents a strategy for waiting for an action 11 // to complete successfully. 12 type AttemptStrategy struct { 13 Total time.Duration // total duration of attempt. 14 Delay time.Duration // interval between each try in the burst. 15 Min int // minimum number of retries; overrides Total 16 } 17 18 type Attempt struct { 19 strategy AttemptStrategy 20 last time.Time 21 end time.Time 22 force bool 23 count int 24 } 25 26 // Start begins a new sequence of attempts for the given strategy. 27 func (s AttemptStrategy) Start() *Attempt { 28 now := time.Now() 29 return &Attempt{ 30 strategy: s, 31 last: now, 32 end: now.Add(s.Total), 33 force: true, 34 } 35 } 36 37 // Next waits until it is time to perform the next attempt or returns 38 // false if it is time to stop trying. 39 // It always returns true the first time it is called - we are guaranteed to 40 // make at least one attempt. 41 func (a *Attempt) Next() bool { 42 now := time.Now() 43 sleep := a.nextSleep(now) 44 if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count { 45 return false 46 } 47 a.force = false 48 if sleep > 0 && a.count > 0 { 49 time.Sleep(sleep) 50 now = time.Now() 51 } 52 a.count++ 53 a.last = now 54 return true 55 } 56 57 func (a *Attempt) nextSleep(now time.Time) time.Duration { 58 sleep := a.strategy.Delay - now.Sub(a.last) 59 if sleep < 0 { 60 return 0 61 } 62 return sleep 63 } 64 65 // HasNext returns whether another attempt will be made if the current 66 // one fails. If it returns true, the following call to Next is 67 // guaranteed to return true. 68 func (a *Attempt) HasNext() bool { 69 if a.force || a.strategy.Min > a.count { 70 return true 71 } 72 now := time.Now() 73 if now.Add(a.nextSleep(now)).Before(a.end) { 74 a.force = true 75 return true 76 } 77 return false 78 }