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

net: File method of {TCP,UDP,IP,Unix}Conn and {TCP,Unix}Listener should leave the socket in nonblocking mode #24942

Closed
rothskeller opened this issue Apr 19, 2018 · 11 comments
Labels
FrozenDueToAge help wanted NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@rothskeller
Copy link

rothskeller commented Apr 19, 2018

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

1.10.1

Does this issue reproduce with the latest release?

Yes.

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

linux/amd64 and darwin/amd64

What did you do?

listener, _ := net.Listen("tcp", ":8000")
listenerFH, _ := listener.(*net.TCPListener).File()
server.Serve(listener)
// later, in a different goroutine
server.Shutdown(context.Background())

What did you expect to see?

Server graceful shutdown.

What did you see instead?

Shutdown() returned immediately but Serve() never returned.

Diagnosis

The call to File() on the listener puts the socket in blocking mode. As a result, when the listener is closed, the Accept() called by Serve() does not return. Note that, if the call to File() is moved so that it occurs after the call to Accept(), the problem does not occur.

Response from [email protected] on golang-nuts, April 18, 2018:

Before about Go 1.9 or so the os package could not handle a non-blocking descriptor, so File had to change the connection to blocking mode in order to make it usable. Now, however, the os package does use nonblocking descriptors where possible. We could probably change the File method to use a nonblocking *os.File. Please do open an issue for that.

@rothskeller
Copy link
Author

Probably irrelevant detail: the reason I am calling File() on the listener at all is so that I can add it to (exec.Command).ExtraFiles to pass it to a subprocess. Definitely no desire to change the socket behavior in so doing.

@ianlancetaylor ianlancetaylor changed the title (*net.TCPListener).File() should leave the socket in nonblocking mode net: (*net.TCPListener).File() should leave the socket in nonblocking mode Apr 19, 2018
@ianlancetaylor ianlancetaylor added help wanted NeedsFix The path to resolution is known, but the work has not been done. labels Apr 19, 2018
@ianlancetaylor ianlancetaylor added this to the Go1.11 milestone Apr 19, 2018
@fraenkel
Copy link
Contributor

@ianlancetaylor The change is trivial but do we care about the behavior change? Unlike what was done with os.File, we cannot easily enhance an existing method.

@ianlancetaylor
Copy link
Member

The package net methods return a *os.File, which will continue to work as expected if we make it nonblocking. I think it would be hard to write a program to detect this change. Let me know if you think otherwise.

@fraenkel
Copy link
Contributor

The only nuance I am aware is that Deadline calls now work.

@fraenkel
Copy link
Contributor

Shall this also apply to IPConn and TCPConn or just TCPListener? Seems weird to only have one behave differently, plus its easier to affect all 3.

@ianlancetaylor
Copy link
Member

I assume it should affect all three.

@gopherbot
Copy link
Contributor

Change https://golang.org/cl/108297 mentions this issue: net: calling File leaves the socket in nonblocking mode

@twmb
Copy link
Contributor

twmb commented Apr 19, 2018

I would guess that fd's were set to nonblocking to begin with (a144e3e) was for a roundabout way of deregistering from epoll (the logic is a bit to follow for the repo at that time). dup would've been problematic because a Close did not deregister from epoll, meaning a registered fd would never be unregistered.

netFD's now unconditionally fall into EPOLL_CTL_DEL on close, so this is no longer an issue. Removing SetNonblocking from netFD narrows #24481 down to the os package specifically. This might fix #24455 (but not the real underlying issue, if any).

@twmb
Copy link
Contributor

twmb commented Apr 19, 2018

Didn't realize this issue had closed (was looking into commits for too long with this page open) -- I'll move portions of that last comment to the relevant open issues.

@mikioh
Copy link
Contributor

mikioh commented Apr 20, 2018

Note that this might be a surprise to people who rely on the go1compt promise. What happens when people having two executables built by go1.10 or below and go1.11 or above, and the executables need to share a single underlying socket? The go1.10 executable assumes that the passed cloned descriptor points to the blocking mode socket but the go1.11 executable passes the cloned descriptor pointing to the non-blocking mode socket.

Of course, there's no problem when people use net.File{Conn,PacketConn,Listener} functions in the go1.10 executables instead of functions from the syscall and x/net/unix packages. Is it safe to bet/assume on that? (not sure, probably it's okay because functions and primitives provided by syscall and x/net/unix packages are just for people who understand what's the right thing and what they are doing)

@ianlancetaylor
Copy link
Member

@mikioh I think that the cases you are describing are fairly unlikely. I agree that there are cases that can break. Let's see what happens. If people find bugs, we can roll back.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge help wanted NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests

6 participants