diff --git a/README.md b/README.md index 705a497..07dd77d 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,13 @@ This repository provides supplementary Go time packages. -## Download/Install - -The easiest way to install is to run `go get -u golang.org/x/time`. You can -also manually git clone the repository to `$GOPATH/src/golang.org/x/time`. - ## Report Issues / Send Patches This repository uses Gerrit for code changes. To learn how to submit changes to -this repository, see https://golang.org/doc/contribute.html. +this repository, see https://go.dev/doc/contribute. + +The git repository is https://go.googlesource.com/time. The main issue tracker for the time repository is located at -https://github.com/golang/go/issues. Prefix your issue with "x/time:" in the +https://go.dev/issues. Prefix your issue with "x/time:" in the subject line, so it is easy to find. diff --git a/rate/rate.go b/rate/rate.go index 93a798a..ec5f0cd 100644 --- a/rate/rate.go +++ b/rate/rate.go @@ -405,8 +405,15 @@ func (limit Limit) durationFromTokens(tokens float64) time.Duration { if limit <= 0 { return InfDuration } - seconds := tokens / float64(limit) - return time.Duration(float64(time.Second) * seconds) + + duration := (tokens / float64(limit)) * float64(time.Second) + + // Cap the duration to the maximum representable int64 value, to avoid overflow. + if duration > float64(math.MaxInt64) { + return InfDuration + } + + return time.Duration(duration) } // tokensFromDuration is a unit conversion function from a time duration to the number of tokens diff --git a/rate/rate_test.go b/rate/rate_test.go index c91fc8f..8b93903 100644 --- a/rate/rate_test.go +++ b/rate/rate_test.go @@ -192,7 +192,7 @@ func (tt *testTime) advance(dur time.Duration) { tt.advanceUnlocked(dur) } -// advanceUnlock advances the fake time, assuming it is already locked. +// advanceUnlocked advances the fake time, assuming it is already locked. func (tt *testTime) advanceUnlocked(dur time.Duration) { tt.cur = tt.cur.Add(dur) i := 0 @@ -638,3 +638,20 @@ func TestSetAfterZeroLimit(t *testing.T) { // We set the limit to 10/s so expect to get another token in 100ms runWait(t, tt, lim, wait{"wait-after-set-nonzero-after-zero", context.Background(), 1, 1, true}) } + +// TestTinyLimit tests that a limiter does not allow more than burst, when the rate is tiny. +// Prior to resolution of issue 71154, this test +// would fail on amd64 due to overflow in durationFromTokens. +func TestTinyLimit(t *testing.T) { + lim := NewLimiter(1e-10, 1) + + // The limiter starts with 1 burst token, so the first request should succeed + if !lim.Allow() { + t.Errorf("Limit(1e-10, 1) want true when first used") + } + + // The limiter should not have replenished the token bucket yet, so the second request should fail + if lim.Allow() { + t.Errorf("Limit(1e-10, 1) want false when already used") + } +}