-
Notifications
You must be signed in to change notification settings - Fork 18k
bufio: Writer.Flush panics if it holds a nil interface writer #9657
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
Comments
The example code is pretty much asking for panic: This is working as intended. |
@minux What? Are you kidding me? How does a caller check for this to avoid panic? |
The 2nd line set the underlying writer of wr to nil, and
|
@minux Please read into the history covered in moby/moby#10228 and the panic at https://gist.github.com/stevvooe/12e63e5f902b5ba7ec01. The reality is much more subtle but the behavior is broken. I conceded above that the use case is not valid but the panic is not expected. |
I fail to see why the panic is not expected. Why Reset(nil)? Adding the check to the Flush method is just hiding bugs in |
@minux "As using an invalid io.Writer is a programming Flushing on a closed Writer is a programming bug too, and yet Go returns an error instead if panicking in this case. Sounds inconsistent to me. |
Valid use cases of panic is an excellent question to ask on the We're not going to make every standard package function to |
@stevvooe bufio.Writer.Reset(w) is setting the underlying writer to w; if Reset is called with nil, that writer is nil. Arguably that is a programmer error, or shall I say "invalid use" of the library. In your case you see a panic in Flush, but any function that eventually writes to the underlying nil writer will panic (e.g.: http://play.golang.org/p/P_f8BbNvCa). I'd agree with @minux that it's not the job of the library to check for nil each time in this case. And even if one would check, that would lead to a difficult to use library because in the case of bufio, the panics (or errors, rather, if we check) would come at somewhat "arbitrary times". For instance in the example I gave above, all is fine if the buffer is large enough and never flushed, and the programming error is thus hidden as @minux @Aluded to. That said, I think an argument could be made that the bufio factory functions (NewReader, NewScanner, NewWriter, ... ) but also functions modifying the underlying data structure (Reset, etc.) should check their arguments for validity and report an error otherwise. However, this is not how this library was written. In case of the factory functions, one could argue that they should return a nil Reader/Writer/what-have-you if the incoming arguments are invalid. But that again puts the burden on each client, even clients that don't do anything wrong, because they cannot know if something else might cause a nil return. To make a long story short: this package is written with the assumption that the supplied readers and writers are valid: Calling bufio.NewWriter with a nil underlying writer simply makes no sense. Thus I must agree with @minux that this is working as intended. If one wanted to be very explicit, the documentation could say that the supplied arguments must be non-nil. At best this is a documentation issue. |
@griesemer Should the documentation be updated to indicate that once a nil argument is passed, the underlying |
Because in majority of cases passing nil interface is invalid, I don't Similarly, if a function takes a pointer, it's invalid to pass a nil |
I'd like to add that we can't check for nil in (*bufio.Writer).Reset, It's legal to use wr.Reset(nil) to mark (poison) a Writer as not be used |
@minux It would preferable that all calls to a nil writer panic, rather than the inconsistent behavior encountered now. Part of the complexity of tracking this down was the fact that the I'll note that Thank you for looking at this tonight. |
Unfortunately, in general we can't check if a method call will However, bufio.Writer is there to reduce the number of calls to As an extreme example, if I pass a typed nil interface (i.e. if T |
I have to agree that this is all working as intended. @minux and @griesemer mentioned all the most important points: Reset(nil) must remain as supported to allow people to free a Writer without freeing the buffer of a pooled bufio.Writer. It's the callers mistake to use that bufio.Writer before Resetting it back to something valid. |
I took a look at the stack trace you mentioned, I'm sure it's due to a nil writer. FYI, the analysis goes as follows:
b is not nil, as we can see from the arguments dump, b == 0xc208208900. (4) might cause problem but note that pc=0x78154a is not zero, so the
As the interface in question is Writer, which only contains one method, so the cause is itab == nil, this is only possible if the whole interface Hope this helps when you diagnose problems like this in the future. |
@minux @griesemer @bradfitz Thank you all for the careful responses. I do see your point on the basis that calling However, having the entire process panic when
(from https://gist.github.com/stevvooe/12e63e5f902b5ba7ec01) I'll drop this issue for now and see about a coming up with a reproduction that is simpler than the docker code base. For now, we have a fix that isolates the client's Close call from response's possibly closed writer. |
Calling
(*bufio.Writer).Flush
panics if(*bufio.Writer).Reset
is called with anil
io.Writer
, then(*bufio.Writer).Write
is called before the flush. This seems like an unlikely scenario but it has been seen on moby/moby#10228. Though the use case may be invalid, the call should not panic.The following code reproduces the issue:
Running the above results in the following panic:
This was reproduced with Go 1.4.1 on Mac OS X 10.9 but has been experienced in other versions.
The text was updated successfully, but these errors were encountered: