| // Copyright 2011, 2012, 2013 Canonical Ltd. |
| // Licensed under the LGPLv3, see LICENCE file for details. |
| |
| package utils |
| |
| import ( |
| "time" |
| ) |
| |
| // The Attempt and AttemptStrategy types are copied from those in launchpad.net/goamz/aws. |
| |
| // AttemptStrategy represents a strategy for waiting for an action |
| // to complete successfully. |
| type AttemptStrategy struct { |
| Total time.Duration // total duration of attempt. |
| Delay time.Duration // interval between each try in the burst. |
| Min int // minimum number of retries; overrides Total |
| } |
| |
| type Attempt struct { |
| strategy AttemptStrategy |
| last time.Time |
| end time.Time |
| force bool |
| count int |
| } |
| |
| // Start begins a new sequence of attempts for the given strategy. |
| func (s AttemptStrategy) Start() *Attempt { |
| now := time.Now() |
| return &Attempt{ |
| strategy: s, |
| last: now, |
| end: now.Add(s.Total), |
| force: true, |
| } |
| } |
| |
| // Next waits until it is time to perform the next attempt or returns |
| // false if it is time to stop trying. |
| // It always returns true the first time it is called - we are guaranteed to |
| // make at least one attempt. |
| func (a *Attempt) Next() bool { |
| now := time.Now() |
| sleep := a.nextSleep(now) |
| if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count { |
| return false |
| } |
| a.force = false |
| if sleep > 0 && a.count > 0 { |
| time.Sleep(sleep) |
| now = time.Now() |
| } |
| a.count++ |
| a.last = now |
| return true |
| } |
| |
| func (a *Attempt) nextSleep(now time.Time) time.Duration { |
| sleep := a.strategy.Delay - now.Sub(a.last) |
| if sleep < 0 { |
| return 0 |
| } |
| return sleep |
| } |
| |
| // HasNext returns whether another attempt will be made if the current |
| // one fails. If it returns true, the following call to Next is |
| // guaranteed to return true. |
| func (a *Attempt) HasNext() bool { |
| if a.force || a.strategy.Min > a.count { |
| return true |
| } |
| now := time.Now() |
| if now.Add(a.nextSleep(now)).Before(a.end) { |
| a.force = true |
| return true |
| } |
| return false |
| } |