From 384619452bf36722f210e36ad5dc26d510f54351 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 31 Oct 2024 15:47:44 -0700 Subject: [PATCH 1/3] README: don't recommend go get These days people will just import the packages and the go tool will do the right thing. We don't need to explain it. Add a pointer to the git repo, though. For golang/go#62645 Change-Id: I87fbf5d50e75120052f56ed6d4ac9977056cde9f Reviewed-on: https://go-review.googlesource.com/c/time/+/624235 LUCI-TryBot-Result: Go LUCI Reviewed-by: Ian Lance Taylor Commit-Queue: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov Auto-Submit: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) 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. From 1ce61fe87e0e5dd90752d2b6c5972f9b6918e77c Mon Sep 17 00:00:00 2001 From: cuishuang Date: Thu, 2 Jan 2025 20:01:01 +0800 Subject: [PATCH 2/3] rate: make function comment match function name Change-Id: I603ca855eac736e90d2ba2bb7ce61981a4a91918 Reviewed-on: https://go-review.googlesource.com/c/time/+/638776 LUCI-TryBot-Result: Go LUCI Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor Reviewed-by: Dmitri Shuralyov --- rate/rate_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rate/rate_test.go b/rate/rate_test.go index c91fc8f..c9bc239 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 From 2c6c5a229852db5dff1662b3a80d9a396939412f Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 8 Jan 2025 13:59:48 +0000 Subject: [PATCH 3/3] rate: prevent overflows when calculating durationFromTokens Currently, there is a conversion from float64 to int64 when returning the duration needed to accumulate the required number of tokens. When limiters are set with low limits, i.e. 1e-10, the duration needed is greater than math.MaxInt64. As per the language specifications, in these scenarios the outcome is implementation determined. This results in overflows on `amd64`, resulting in no wait, effectively jamming the limiter open. Here we add a check for this scenario, returning InfDuration if the desired duration is greater than math.MaxInt64. Fixes golang/go#71154 Change-Id: I775aab80fcc8563a59aa399844a64ef70b9eb76a Reviewed-on: https://go-review.googlesource.com/c/time/+/641336 Reviewed-by: Dmitri Shuralyov Auto-Submit: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Reviewed-by: Ian Lance Taylor --- rate/rate.go | 11 +++++++++-- rate/rate_test.go | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) 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 c9bc239..8b93903 100644 --- a/rate/rate_test.go +++ b/rate/rate_test.go @@ -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") + } +}