@@ -2,6 +2,7 @@ package retry
22
33import (
44 "context"
5+ "math"
56 "sync/atomic"
67 "time"
78)
@@ -11,17 +12,19 @@ type exponentialBackoff struct {
1112 attempt uint64
1213}
1314
14- // Exponential is a wrapper around Retry that uses an exponential backoff. It's
15- // very efficient, but does not check for overflow, so ensure you bound the
16- // retry. It panics if the given base is less than zero.
15+ // Exponential is a wrapper around Retry that uses an exponential backoff. See
16+ // NewExponential.
1717func Exponential (ctx context.Context , base time.Duration , f RetryFunc ) error {
1818 return Do (ctx , NewExponential (base ), f )
1919}
2020
2121// NewExponential creates a new exponential backoff using the starting value of
2222// base and doubling on each failure (1, 2, 4, 8, 16, 32, 64...), up to max.
23- // It's very efficient, but does not check for overflow, so ensure you bound the
24- // retry. It panics if the given base is less than 0.
23+ //
24+ // Once it overflows, the function constantly returns the maximum time.Duration
25+ // for a 64-bit integer.
26+ //
27+ // It panics if the given base is less than zero.
2528func NewExponential (base time.Duration ) Backoff {
2629 if base <= 0 {
2730 panic ("base must be greater than 0" )
@@ -34,5 +37,11 @@ func NewExponential(base time.Duration) Backoff {
3437
3538// Next implements Backoff. It is safe for concurrent use.
3639func (b * exponentialBackoff ) Next () (time.Duration , bool ) {
37- return b .base << (atomic .AddUint64 (& b .attempt , 1 ) - 1 ), false
40+ next := b .base << (atomic .AddUint64 (& b .attempt , 1 ) - 1 )
41+ if next <= 0 {
42+ atomic .AddUint64 (& b .attempt , ^ uint64 (0 ))
43+ next = math .MaxInt64
44+ }
45+
46+ return next , false
3847}
0 commit comments