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  }