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/net/context/ctxhttp: remove racy go1.4 support #13293

Closed
pwaller opened this issue Nov 17, 2015 · 10 comments
Closed

x/net/context/ctxhttp: remove racy go1.4 support #13293

pwaller opened this issue Nov 17, 2015 · 10 comments

Comments

@pwaller
Copy link
Contributor

pwaller commented Nov 17, 2015

x/net/context/ctxhttp has code specifically to handle go1.4, and I believe this code is racy.

Details of this style of race: #11013

No synchronization here...
...Against the request going in flight.

Example of the sort of thing I understand that needs to be done with the 1.4 cancellation interface (I wrote this with Bradfitz' input, which is when I became aware of the issue).

The problem is the definition of the canceller API in Go1.4 - cancelling the request is only a valid thing to do once the request is in flight, otherwise the cancellation is ignored. Hence the need for the req.Body acrobatics in reverseproxy, and hence the improved API in 1.5.

golang-dev discussion.

@dsymonds
Copy link
Contributor

It's quite probable that ctxhttp is racy in Go 1.4. There isn't a way to do it in a non-racy way for Go 1.4, though (that's why the net/http API changed in Go 1.5).

@pwaller
Copy link
Contributor Author

pwaller commented Nov 17, 2015

@okdave
Copy link
Contributor

okdave commented Nov 18, 2015

@pwaller would that approach work if there was no request body?

The 1.4 support is racy, yes. I'm not too inclined to invest in fixing it though since a correct solution is going to increase complexity dramatically for little benefit.

@pwaller
Copy link
Contributor Author

pwaller commented Nov 18, 2015

@okdave, I understand that it works if there is no request body, yes. The body is written as part of Request.Write(conn), which reads the body, if it is present (which it is, in the reverseproxy example). So it is certain to be read from if it's not nil, even if there are zero bytes to read.

I'd like to see this fixed this because it means leaking connections and goroutines to docker in go-dockerclient, which a long lived process of mine uses. I want to implement cancellation in go-dockerclient in terms of ctxhttp but don't feel comfortable doing so while I know there is some chance that connections are leaked if someone is unlucky enough to build with go 1.4. The presence of this leak makes it harder to identify and diagnose other leaks.

The failure I'm concerned about is particularly pathological because the other side (docker) of the connection will hold the connection open indefinitely in some cases, so it could quickly run out of file descriptors and leak an unreasonable number of goroutines.

By avoiding the complexity in the library, that is pushing it onto the users! :)

@pwaller
Copy link
Contributor Author

pwaller commented Nov 18, 2015

By the way, I just ran the ctxhttp tests under go1.4 with this tweak:

func canceler(client *http.Client, req *http.Request) func() {
    rc, ok := client.Transport.(requestCanceler)
    if !ok {
        return func() {
            log.Printf("Not a canceler: %T", client.Transport)
        }
    }
    return func() {
        log.Printf("Using 1.4 canceler")
        rc.CancelRequest(req)
    }
}

The output is this:

=== RUN TestCancel
2015/11/18 08:57:32 Not a canceler: <nil>

This would appear to indicate that the tests aren't exercising the canceller on go1.4, and possibly the canceller is having no effect anyway?

(Also I hit this already fixed race in case anyone happens on it during investigation)

@okdave
Copy link
Contributor

okdave commented Nov 19, 2015

Interesting. ctxhttp.Do was not really designed with cancelling after it returned in mind. @broady, @bradfitz what are your thoughts on that?

@pwaller
Copy link
Contributor Author

pwaller commented Nov 19, 2015

I have opened #13325 for the above question of @okdave to avoid confusing this issue, which is about the go1.4 code being racy before the request is in flight.

(Aside: So now we have a problem where the request can't be cancelled before, and it can't be cancelled after, which means there is only a very "narrow" window — during the exchange of headers — where cancellation does anything. If that's ctxhttp working as intended, the documentation needs to be improved.)

@rsc rsc changed the title x/net/context/ctxhttp: Race in go1.4 cancellation x/net/context/ctxhttp: race in go1.4 cancellation Dec 28, 2015
@rsc rsc added this to the Unreleased milestone Dec 28, 2015
@pwaller
Copy link
Contributor Author

pwaller commented Jan 26, 2016

Note: I ended up not using this for my original use case, so I'm unlikely to follow this up. I don't suppose anyone else is either, since 1.4 is soon to be two major versions behind and few people are likely to hit this. I would be OK with closing this issue.

@ianlancetaylor
Copy link
Contributor

We may as well leave this open to actually drop the Go 1.4 support soon.

@ianlancetaylor ianlancetaylor changed the title x/net/context/ctxhttp: race in go1.4 cancellation x/net/context/ctxhttp: remove racy go1.4 support Jan 26, 2016
@broady
Copy link
Contributor

broady commented Apr 27, 2016

It's been removed.

@broady broady closed this as completed Apr 27, 2016
@golang golang locked and limited conversation to collaborators Apr 27, 2017
@rsc rsc unassigned Sajmani Jun 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants