-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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: errClosing not exported #4373
Comments
Comment 1 by [email protected]: Another option would be to return a syscall.EINVAL just like reading from a closed connection. However, that would not be backwards compatible with anyone who is checking the Error() string and would imply a syscall had taken place and failed. If backwards compatibility was not an issue, I would probably want to make it so both reading from a closed connection and a closing connection returned an ErrClosed. |
Hello, Can you give a bit of background why you need to know this particular error state ? The reason I ask is errClosing is an artefact of net.TCPConn implementations of the net.Conn interface and is not guaranteed (or ever advertised, hence why it is not exposed). For example, the net.Conn your program may be working with may wrapped by another implementation, like the ssh package's ssh.ClientConn. Cheers Dave Status changed to WaitingForReply. |
Based on my experience, there seems to be quite a few patterns with goroutines in servers where you end up with 2 goroutines associated with the same Conn and where developers (correctly, or not) deem it necessary that both goroutines close that Conn in some error paths. In this case, some people want to distinguish between errClosing, which is an expected error and can be ignored, and any other error from Close (my favourite is EBADF), which should probably be fatal to the program (i.e. the program should panic when it happens). The general vibe seems to be that one should simply ignore the error from Close instead of trying to distinguish between expected and unexpected errors, but this still doesn't sit completely well with me... |
As the previous comment clearly states, sometimes a goroutine signals another goroutine that it should stop reading from that socket. As with channels, closing the resource, the socket in this case, seems like simple solution. Cosmetic improvement: if this global value is ever exposed, should it be rename to "ErrClosed"? |
Comment 5 by [email protected]: That would imply it is also sent when the net.Conn is closed. Currently it returns the error of the underlying syscall. If you are not trying to Read/Write at the time the channel is closed, you would not receive this "net.ErrClosed". |
A comment on a related issue https://golang.org/issue/4369?c=3 suggested that Accept() pre-empted by a Close should return io.EOF. Would that solution be acceptable ? |
For discussion http://golang.org/cl/6852070 |
From the small play I had last week, I think there are problems where errClosing might be wrapped by another error, from memory this is somewhere in the Accept path. I agree that ErrClosing is not io.EOF, but I am worried about making this an additional requirement of all implementations of net.Conn/net.Listener. |
I object to the 'rename errClosing to io.EOF' solution, but I don't care much what else we do. I suggest either: 1) Define that people should not care, that code that wants to check for errClosing is buggy to begin with. This is similar to getting rid of closed(c). 2) Export ErrClosing. Russ |
+bradfitz, because of his comment, https://golang.org/issue/4369?c=3 I'd prefer to do 1, relying on errClosing is buggy, for the following: * most times the errClosing is wrapped in an net.OpError, so a test like err == ErrClosing wouldn't work as expected. I'm sure there are situations where the error is wrapped more than once. I suspect the OP didn't consider that. * as an implementer of net.Conn implementations in things like the ssh package I wouldn't like the additional burdon of having to return net.ErrClosing according to an additional requirement of the net.Conn interface. I'm not even sure that I could detect concurrent closes on a ssh channel muxed over an unknown net.Conn. Even if the original ErrClosing was detected and retained, it would be very heavily wrapped, which speaks to my previous point * such a change to the net.Conn interface may make existing net.Conn implementations broken according to the Go 1.x contract. * Would this change bubble down to io.Reader/Writers ? It is easy to construct a struct like c := net.Dial(...) v := struct { io.Reader; io.Writer; io.Closer }{ c, c, c } Would there be an implied requirement to return a known ErrClosing when reading from an io.Reader whose direct implementation was closed concurrently? Fullung talked about not wanting to ignore errors from Close. I agree with this sentiment, but wonder if the requirement could be restated as, "I would like to be able to distinguish between expected and unexpected errors from Close". For the former they could be safely ignored, the latter might mean a trip through os.Exit(). At the moment errClosing, being unexported, makes it hard to tell if it is in the expected, or unexpected class. I believe this is the core issue, and possibly what bradfitz was suggesting when he suggested replacing errClosing with io.EOF. At the risk of appearing obstructionist, and considering the number of lines spilt in this CL vs the size of the original request, I'd like to hear from the OP about their specific requirements. |
I wanted to be able to distinguish a listener closing due to my own closing it versus a serious problem, but I don't care too much: If I closed it, I know I closed it, even if I have to pass that state around or make a net.Listener wrapper that does what I want. So don't make things complicated for me, if this is hard. |
Issue #5062 has been merged into this issue. |
Reopening for further thought. |
I'm not sure the reason why both net/url and net/http packages don't conform with net.Error interface, I'd prefer to make net.Error interface (both Timeout and Temporary methods on error) work with both packages. Thoughts? PS: #4856 is already filed for fixing the same issue of net package. |
I also have two use cases for detecting a closed connection when using goroutines:
In both cases, my goroutine is blocked on a Read() and errors out with an In both use cases, I want to be able to detect these errors as io.EOF (use case 1) or io.ErrClosed (use case 2) and handle them accordingly. With the current errClosing, I can't cleanly detect the error and if the error message were to change, my code wouldn't detect it. |
Can you please explain how you would handle these cases specifically if you On Fri, Mar 20, 2015 at 7:18 AM, smithwinston [email protected]
|
This should be exported (in some way). My code correctly closes the listener, and goroutine that accepts clients gets only ""use of closed network connection" as an error, when I would like to differentiate this 'not really an error but perfectly fine way to top this goroutine' from an actual error. Some may (and have already) ask why it is important to be able to check different errors. I thought it was obvious. The most basic example is not to log the information about normal close, only about true errors. Second is trying to restart the function. If listener truly breaks, then I will try to start it again. If it was closed normally (bacause of context.cancel for example) then I won't. I understand that go is fun language and doing hard things is easy with it. But the simple ones are usually the most challenging. Especially reliable error handling. Reluctance to exporting specific errors is something I do not understand. Especially when there are no error codes, nothing more then plain string error in the If this problem is not solved, and only strings will be THE error values, then parsing those string will become idiomatic.
|
I agree with @riobard in #4373 (comment): it's not necessarily the case that errClosing must be propagated to all other interfaces that wrap or work like this one. In any case, if a public function returns an object, that object's type must also be public — otherwise the caller has no chance to fully know and understand the interface of the function they're calling. The inevitable result is this string matching silliness (which I also found myself having to do), when type switching is the idiomatic and much more clearly correct approach. If we're willing to live with designs we don't love, and we have to be ;) , it seems straightforward that exporting the type is much less unlovable than the status quo (#4373 (comment)). |
There are currently over 3,000 cases of matching this error string on GitHub, including projects such as Kubernetes: https://github.com/search?l=Go&q=%22use+of+closed+network+connection%22&type=Code Whatever people think is the "correct" way to work around the issue, it is clearly out of touch with the reality on the ground. Let's please implement the pragmatic solution asap. |
Since it's been almost 3 months since the last serious comment here, I'm going to bump this. I have multiple variations of this across multiple code bases at the moment: // shortened example; full version has defer Close() and error handling on Listen and Close
server, _ := net.Listen("tcp4", ":9000")
go func() {
for {
client, err := server.Accept()
if err != nil {
// Accept() returns an error with substring "use of closed network connection" if
// the socket has been closed elsewhere (ie. during graceful stop, instead of EOF).
// Go is "supporting" this string comparison until an alternative is presented to
// improve the situation. See https://github.com/golang/go/issues/4373 for info.
if !strings.Contains(err.Error(), "use of closed network connection") {
log.Printf("[fatal] server: socket %q: accept: %v\n", server.Addr(), err)
cExitCode <- 1
}
return
}
go handleClient(client)
}
}() Go has quite a few "wtf" moments to adjust to, but this has to be one of the worst. Checking for a substring in an error message is just… disgusting. At least, unlike most code repositories, I've commented mine with the "wtf" thought process and a link to this issue so future developers who have to maintain my code might hopefully understand. So many other of the 3000 instances reported by @awfm9 give no such warning to the future. If it's really necessary to break compatibility, I think we're finally hitting the point where a 2.0 is required. A lot of the core packages are so out of date with "core values". For example, the At some point, the Go team needs to completely abandon all new features, and focus on a purely cleanup phase for a 2.0 release. These situations where Go doesn't respect its own modern conventions (eg. contexts and wrapped errors) is so jarring that it makes the language far too incomprehensible. |
It's tagged for 1.16, the next release. A lot of ppl wouldn't want Context to infect the rest of the stdlib. It's rather like "const poisoning" :-) |
This issue is currently labeled as early-in-cycle for Go 1.16. |
Change https://golang.org/cl/250357 mentions this issue: |
@ianlancetaylor A couple of years ago I proposed
also exporting |
@ainar-g Sure. Thanks. In particular, perhaps crypto/tls should replace |
8.5 years later and we can finally remove this hacky code 🤣 |
Change https://golang.org/cl/287592 mentions this issue: |
Emit the message as DEBUG, so logs avoid false positive errors. net.ErrClosed was added in Go 1.16, but this project's go.mod specifies Go 1.13. We alias Go's internal error type, to avoid the worse alternative of comparing the string of the error message. Go maintainers discuss this topic at length in golang/go#4373. Occurences of "use of closed network connection" are not an actual problem. It's advisory only, akin to io.EOF.
by [email protected]:
The text was updated successfully, but these errors were encountered: