Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/time/rate: Wait's behaviour is inconsistent when the limit is 0 (tested on darwin/arm64 and linux/amd64) #47817

Closed
amwolff opened this issue Aug 19, 2021 · 7 comments
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@amwolff
Copy link
Contributor

amwolff commented Aug 19, 2021

What version of Go are you using (go version)?

$ go version
go version go1.17 darwin/arm64

and

$ gotip version
go version devel go1.18-91e2e3b Thu Aug 19 16:10:32 2021 +0000 darwin/arm64

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
[...]
GOARCH="arm64"
[...]
GOOS="darwin"
[...]

What did you do?

The following program will block forever on my machine: https://play.golang.org/p/STtqVhMulsy. On linux/amd64, it runs just fine. Similarly, this panics on my computer: https://play.golang.org/p/nPng8M23CEf.

What did you expect to see?

Consistent behaviour.

What did you see instead?

Inconsistent behaviour.

It might be closely related to #47221, but I'm not 100% sure.

@gopherbot gopherbot added this to the Unreleased milestone Aug 19, 2021
@mknyszek mknyszek added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Aug 20, 2021
@mknyszek
Copy link
Contributor

I'd think the right behavior here would be to block forever, as on your machine. From the docs:

A zero Limit allows no events.

And Wait blocks until 1 event is available.

Since we don't have any owners listed (https://dev.golang.org/owners) I'll pick some names from the blamelist: @Sajmani @josharian

@mknyszek
Copy link
Contributor

Actually, I think this is exactly #47221. The difference seen here can be explained by different divide-by-zero behavior on arm64 vs. amd64.

@mknyszek
Copy link
Contributor

Er, not divide by zero behavior, it might go deeper than that with Inf and NaN, but that could also explain the panic. What does the panic look like?

@amwolff
Copy link
Contributor Author

amwolff commented Aug 20, 2021

@mknyszek So yeah, that panic I mentioned is just a panic I instantiated in https://play.golang.org/p/nPng8M23CEf (line 19).

% go run -trimpath main.go
panic: Wait: rate: Wait(n=1) would exceed context deadline

goroutine 1 [running]:
main.main()
        ./main.go:19 +0x230
exit status 2

Sorry for the confusion. Anyway, the error comes from Wait (rate limit is zero, and with context with a timeout, we will exceed its deadline no matter what). This is similar to passing the background context, but there's no blocking, as expected. For both examples, I'm not sure if this is the correct behaviour either because we need to take burst into account.

@amwolff
Copy link
Contributor Author

amwolff commented Aug 21, 2021

I think I was able to pinpoint the problem, and now it's a matter of figuring out why

time.Duration(math.Inf(0)) or int64(math.Inf(0)) is -9223372036854775808 on amd64

and why

time.Duration(math.Inf(0)) or int64(math.Inf(0)) is 9223372036854775807 on arm64

When the limit is zero, there's this division by zero in durationFromTokens which produces +Inf converted to time.Duration later on. Since the return value of durationFromTokens depends on the arch (at the moment), there's a difference in behaviour. It looks like the conversion produces an off-by-one value, in one way or another, depending on the platform.

I suspect conversion because I looked up the bytes of +Inf float64s, and they are the same on both arches.

I tested on darwin/amd64 and darwin/arm64 with Go 1.17.

If I knew where to look, I'm happy to work on this problem.

@ianlancetaylor
Copy link
Contributor

In Go, when converting a floating-point value to an integer type, if the floating-point value can't be represented in the integer type, the result is implementation defined. Quoting https://golang.org/ref/spec#Conversions:

In all non-constant conversions involving floating-point or complex values, if the result type cannot represent the value the conversion succeeds but the result value is implementation-dependent.

I think that what you are seeing here is that the amd64 and arm64 implementations produce different results when converting a floating-point infinity to an integer type. If it's important to handle this case similarly, the code needs to check for that case explicitly. (I have not looked at the code.)

@mknyszek
Copy link
Contributor

@amwolff Thanks.

This is roughly what I expected (some implementation-defined FP behavior), and I think that means it is indeed a duplicate of #47221, since that divide-by-zero creates the Inf value in the first place, and should be fixed.

@golang golang locked and limited conversation to collaborators Aug 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

4 participants