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

bufio: preemptive Flush() inside Write*() methods #20943

Closed
gobwas opened this issue Jul 7, 2017 · 2 comments
Closed

bufio: preemptive Flush() inside Write*() methods #20943

gobwas opened this issue Jul 7, 2017 · 2 comments
Labels
FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Milestone

Comments

@gobwas
Copy link

gobwas commented Jul 7, 2017

Hello!

While digging the sources of bufio.Writer I had a question:
Inside Writer.ReadFrom() method exists a "preemptive" Flush(). I think this is made to deal with src.Read() deadlines in subsequent calls of ReadFrom – to not spent time on buffer flush before read attempt.

Could it be useful to make the same "preemptive" flushes inside other Writer.Write*() methods?

I mean that in current implementation test like this will fail:

type snoozeWriter struct {
	delay time.Duration
}

func (w snoozeWriter) Write(p []byte) (int, error) {
	time.Sleep(w.delay)
	return len(p), nil
}

type deadlineReader struct {
	deadline time.Time
}

func (r deadlineReader) Read(p []byte) (int, error) {
	if time.Since(r.deadline) > 0 {
		return 0, fmt.Errorf("deadline exceeded")
	}
	return len(p), io.EOF
}

func TestWriterReadFromTimeout(t *testing.T) {
	const size = 10

	bw := bufio.NewWriterSize(snoozeWriter{time.Second}, 10)

	// Fill buffer, without Flush().
	bw.WriteString("0123456789")

	// Emulate some reader with read deadline set.
	r := deadlineReader{time.Now().Add(500 * time.Millisecond)}

	if n, err := bw.ReadFrom(r); n != size || err != nil {
		t.Fatalf("ReadFrom() = (%v, %v); want (10, nil)", n, err)
	}
}
@bradfitz bradfitz added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Jul 7, 2017
@bradfitz bradfitz added this to the Go1.10 milestone Jul 7, 2017
@rsc
Copy link
Contributor

rsc commented Jul 11, 2017

In general the rule for buffering I/O is to buffer as much as possible and therefore to flush as little as possible. Write implementations should check b.Available() > 0 before starting to write; if that's not true, they should call Flush then, before the write operation. The code you found was added to fix #5947, in which ReadFrom did not check for b.Available() > 0 before starting and therefore could be confused by trying to read 0 bytes. In the fix for that issue, Andrew did a kind of belt-and-suspenders fix and checked for a full buffer both before and after the operation. Only the one before is needed. The one after (the one you found) could potentially be removed for Go 1.10.

But I'm inclined to just leave it alone. It's not bothering anyone. At the same time, I don't want to introduce more preemptive flushes - they'll just hide bugs where Flush has been forgotten.

@rsc rsc closed this as completed Jul 11, 2017
@gobwas
Copy link
Author

gobwas commented Jul 11, 2017

@rsc Thanks for clarification. Yeah, removing the "one after" is looks fine too. By the way, after few days pass, I realise that preemptive flush after Write() with buf.Available() bytes will produce unexpected behaviour – caller probably may want to fill buffer, but not to flush it. So yeah, its ok, thanks.

@golang golang locked and limited conversation to collaborators Jul 11, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Projects
None yet
Development

No branches or pull requests

4 participants