mirror of
https://github.com/golang/go
synced 2024-11-15 05:30:32 -07:00
internal/poll: fix the inaccurate comment and add a edge test case
*BSD can also returns (>0, EAGAIN) with non-blocking socket, it's therefore not macOS-specific. Change-Id: I57a393da31fab7788ad5779a038396be8236b0f9 Reviewed-on: https://go-review.googlesource.com/c/go/+/623056 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Damien Neil <dneil@google.com> Auto-Submit: Ian Lance Taylor <iant@google.com>
This commit is contained in:
parent
6df37f70de
commit
970dfe0ff0
@ -96,10 +96,10 @@ func sendFile(dstFD *FD, src int, offset *int64, size int64) (written int64, err
|
|||||||
return written, nil, true
|
return written, nil, true
|
||||||
}
|
}
|
||||||
case syscall.EAGAIN:
|
case syscall.EAGAIN:
|
||||||
// Darwin can return EAGAIN with n > 0,
|
// *BSD and Darwin can return EAGAIN with n > 0,
|
||||||
// so check to see if the write has completed.
|
// so check to see if the write has completed.
|
||||||
// So far as we know all other platforms only return EAGAIN when n == 0,
|
// So far as we know all other platforms only
|
||||||
// but checking is harmless.
|
// return EAGAIN when n == 0, but checking is harmless.
|
||||||
if size > 0 && written >= size {
|
if size > 0 && written >= size {
|
||||||
return written, nil, true
|
return written, nil, true
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"internal/poll"
|
"internal/poll"
|
||||||
"io"
|
"io"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -46,6 +47,7 @@ func expectSendfile(t *testing.T, wantConn Conn, f func()) {
|
|||||||
called bool
|
called bool
|
||||||
gotHandled bool
|
gotHandled bool
|
||||||
gotFD *poll.FD
|
gotFD *poll.FD
|
||||||
|
gotErr error
|
||||||
)
|
)
|
||||||
poll.TestHookDidSendFile = func(dstFD *poll.FD, src int, written int64, err error, handled bool) {
|
poll.TestHookDidSendFile = func(dstFD *poll.FD, src int, written int64, err error, handled bool) {
|
||||||
if called {
|
if called {
|
||||||
@ -54,6 +56,7 @@ func expectSendfile(t *testing.T, wantConn Conn, f func()) {
|
|||||||
called = true
|
called = true
|
||||||
gotHandled = handled
|
gotHandled = handled
|
||||||
gotFD = dstFD
|
gotFD = dstFD
|
||||||
|
gotErr = err
|
||||||
}
|
}
|
||||||
f()
|
f()
|
||||||
if !called {
|
if !called {
|
||||||
@ -61,7 +64,7 @@ func expectSendfile(t *testing.T, wantConn Conn, f func()) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !gotHandled {
|
if !gotHandled {
|
||||||
t.Error("internal/poll.SendFile did not handle the write, want it to")
|
t.Error("internal/poll.SendFile did not handle the write, want it to, error:", gotErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if &wantConn.(*TCPConn).fd.pfd != gotFD {
|
if &wantConn.(*TCPConn).fd.pfd != gotFD {
|
||||||
@ -69,10 +72,33 @@ func expectSendfile(t *testing.T, wantConn Conn, f func()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendfile(t *testing.T) { testSendfile(t, 0) }
|
func TestSendfile(t *testing.T) { testSendfile(t, newton, newtonSHA256, newtonLen, 0) }
|
||||||
func TestSendfileWithExactLimit(t *testing.T) { testSendfile(t, newtonLen) }
|
func TestSendfileWithExactLimit(t *testing.T) {
|
||||||
func TestSendfileWithLimitLargerThanFile(t *testing.T) { testSendfile(t, newtonLen*2) }
|
testSendfile(t, newton, newtonSHA256, newtonLen, newtonLen)
|
||||||
func testSendfile(t *testing.T, limit int64) {
|
}
|
||||||
|
func TestSendfileWithLimitLargerThanFile(t *testing.T) {
|
||||||
|
testSendfile(t, newton, newtonSHA256, newtonLen, newtonLen*2)
|
||||||
|
}
|
||||||
|
func TestSendfileWithLargeFile(t *testing.T) {
|
||||||
|
// Some platforms are not capable of handling large files with sendfile
|
||||||
|
// due to limited system resource, so we only run this test on amd64 and
|
||||||
|
// arm64 for the moment.
|
||||||
|
if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {
|
||||||
|
t.Skip("skipping on non-amd64 and non-arm64 platforms")
|
||||||
|
}
|
||||||
|
// Also skip it during short testing.
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skip it during short testing")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're using 1<<31 - 1 as the chunk size for sendfile currently,
|
||||||
|
// make an edge case file that is 1 byte bigger than that.
|
||||||
|
f := createTempFile(t, 1<<31)
|
||||||
|
// For big file like this, only verify the transmission of the file,
|
||||||
|
// skip the content check.
|
||||||
|
testSendfile(t, f.Name(), "", 1<<31, 0)
|
||||||
|
}
|
||||||
|
func testSendfile(t *testing.T, filePath, fileHash string, size, limit int64) {
|
||||||
ln := newLocalListener(t, "tcp")
|
ln := newLocalListener(t, "tcp")
|
||||||
defer ln.Close()
|
defer ln.Close()
|
||||||
|
|
||||||
@ -90,7 +116,7 @@ func testSendfile(t *testing.T, limit int64) {
|
|||||||
defer close(errc)
|
defer close(errc)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
f, err := os.Open(newton)
|
f, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errc <- err
|
errc <- err
|
||||||
return
|
return
|
||||||
@ -109,7 +135,7 @@ func testSendfile(t *testing.T, limit int64) {
|
|||||||
expectSendfile(t, conn, func() {
|
expectSendfile(t, conn, func() {
|
||||||
if limit > 0 {
|
if limit > 0 {
|
||||||
sbytes, err = io.CopyN(conn, f, limit)
|
sbytes, err = io.CopyN(conn, f, limit)
|
||||||
if err == io.EOF && limit > newtonLen {
|
if err == io.EOF && limit > size {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -122,8 +148,8 @@ func testSendfile(t *testing.T, limit int64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if sbytes != newtonLen {
|
if sbytes != size {
|
||||||
errc <- fmt.Errorf("sent %d bytes; expected %d", sbytes, newtonLen)
|
errc <- fmt.Errorf("sent %d bytes; expected %d", sbytes, size)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -143,11 +169,11 @@ func testSendfile(t *testing.T, limit int64) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rbytes != newtonLen {
|
if rbytes != size {
|
||||||
t.Errorf("received %d bytes; expected %d", rbytes, newtonLen)
|
t.Errorf("received %d bytes; expected %d", rbytes, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
if res := hex.EncodeToString(h.Sum(nil)); res != newtonSHA256 {
|
if len(fileHash) > 0 && hex.EncodeToString(h.Sum(nil)) != newtonSHA256 {
|
||||||
t.Error("retrieved data hash did not match")
|
t.Error("retrieved data hash did not match")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,7 +578,7 @@ type sendFileBench struct {
|
|||||||
|
|
||||||
func (bench sendFileBench) benchSendFile(b *testing.B) {
|
func (bench sendFileBench) benchSendFile(b *testing.B) {
|
||||||
fileSize := b.N * bench.chunkSize
|
fileSize := b.N * bench.chunkSize
|
||||||
f := createTempFile(b, fileSize)
|
f := createTempFile(b, int64(fileSize))
|
||||||
|
|
||||||
client, server := spawnTestSocketPair(b, bench.proto)
|
client, server := spawnTestSocketPair(b, bench.proto)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@ -578,25 +604,27 @@ func (bench sendFileBench) benchSendFile(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTempFile(b *testing.B, size int) *os.File {
|
func createTempFile(tb testing.TB, size int64) *os.File {
|
||||||
f, err := os.CreateTemp(b.TempDir(), "sendfile-bench")
|
f, err := os.CreateTemp(tb.TempDir(), "sendfile-bench")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to create temporary file: %v", err)
|
tb.Fatalf("failed to create temporary file: %v", err)
|
||||||
}
|
}
|
||||||
b.Cleanup(func() {
|
tb.Cleanup(func() {
|
||||||
f.Close()
|
f.Close()
|
||||||
})
|
})
|
||||||
|
|
||||||
data := make([]byte, size)
|
if _, err := io.CopyN(f, newRandReader(tb), size); err != nil {
|
||||||
if _, err := f.Write(data); err != nil {
|
tb.Fatalf("failed to fill the file with random data: %v", err)
|
||||||
b.Fatalf("failed to create and feed the file: %v", err)
|
|
||||||
}
|
|
||||||
if err := f.Sync(); err != nil {
|
|
||||||
b.Fatalf("failed to save the file: %v", err)
|
|
||||||
}
|
}
|
||||||
if _, err := f.Seek(0, io.SeekStart); err != nil {
|
if _, err := f.Seek(0, io.SeekStart); err != nil {
|
||||||
b.Fatalf("failed to rewind the file: %v", err)
|
tb.Fatalf("failed to rewind the file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newRandReader(tb testing.TB) io.Reader {
|
||||||
|
seed := time.Now().UnixNano()
|
||||||
|
tb.Logf("Deterministic RNG seed based on timestamp: 0x%x", seed)
|
||||||
|
return rand.New(rand.NewSource(seed))
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user