mirror of
https://github.com/golang/go
synced 2024-11-23 20:50:04 -07:00
io: correctly process result of sendfile(2) when src returns 0 bytes
Fixes #53658. io.Copy() uses sendfile(2) to avoid allocating extra buffers when src is a file and dst is a TCPConn. However if src returns no bytes current logic treats it as failure and falls back to copying via user space. The following is a benchmark that illustrates the bug.
Benchmark: https://go.dev/play/p/zgZwpjUatSq
Before:
BenchmarkCopy-16 541006 2137 ns/op 4077 B/op 0 allocs/op
After:
BenchmarkCopy-16 490383 2365 ns/op 174 B/op 8 allocs/op
Change-Id: I703376d53b20e080c6204a73c96867cce16b24cf
GitHub-Last-Rev: 3a50be4f16
GitHub-Pull-Request: golang/go#53659
Reviewed-on: https://go-review.googlesource.com/c/go/+/415834
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
parent
f13849a7af
commit
27c3814275
@ -11,18 +11,21 @@ import "syscall"
|
||||
const maxSendfileSize int = 4 << 20
|
||||
|
||||
// SendFile wraps the sendfile system call.
|
||||
func SendFile(dstFD *FD, src int, remain int64) (int64, error) {
|
||||
func SendFile(dstFD *FD, src int, remain int64) (int64, error, bool) {
|
||||
if err := dstFD.writeLock(); err != nil {
|
||||
return 0, err
|
||||
return 0, err, false
|
||||
}
|
||||
defer dstFD.writeUnlock()
|
||||
if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil {
|
||||
return 0, err
|
||||
return 0, err, false
|
||||
}
|
||||
|
||||
dst := dstFD.Sysfd
|
||||
var written int64
|
||||
var err error
|
||||
var (
|
||||
written int64
|
||||
err error
|
||||
handled = true
|
||||
)
|
||||
for remain > 0 {
|
||||
n := maxSendfileSize
|
||||
if int64(n) > remain {
|
||||
@ -48,8 +51,9 @@ func SendFile(dstFD *FD, src int, remain int64) (int64, error) {
|
||||
// support) and syscall.EINVAL (fd types which
|
||||
// don't implement sendfile)
|
||||
err = err1
|
||||
handled = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return written, err
|
||||
return written, err, handled
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ import (
|
||||
// sendFile copies the contents of r to c using the sendfile
|
||||
// system call to minimize copies.
|
||||
//
|
||||
// if handled == true, sendFile returns the number of bytes copied and any
|
||||
// non-EOF error.
|
||||
// if handled == true, sendFile returns the number (potentially zero) of bytes
|
||||
// copied and any non-EOF error.
|
||||
//
|
||||
// if handled == false, sendFile performed no work.
|
||||
func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
|
||||
@ -39,7 +39,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
|
||||
|
||||
var werr error
|
||||
err = sc.Read(func(fd uintptr) bool {
|
||||
written, werr = poll.SendFile(&c.pfd, int(fd), remain)
|
||||
written, werr, handled = poll.SendFile(&c.pfd, int(fd), remain)
|
||||
return true
|
||||
})
|
||||
if err == nil {
|
||||
@ -49,5 +49,5 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
|
||||
if lr != nil {
|
||||
lr.N = remain - written
|
||||
}
|
||||
return written, wrapSyscallError("sendfile", err), written > 0
|
||||
return written, wrapSyscallError("sendfile", err), handled
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user