mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
net: deduplicate sendfile files
The sendfile implementation for platforms supporting it is now in net/sendfile.go, rather than being duplicated in separate files for each platform. The only difference between the implementations was the poll.SendFile parameters, which have been harmonized, and also linux strictly asserting for os.File, which now have been relaxed to allow any type implementing syscall.Conn. Change-Id: Ia1a2d5ee7380710a36fc555dbf681f7e996ea2ec Reviewed-on: https://go-review.googlesource.com/c/go/+/664075 Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Auto-Submit: Quim Muntal <quimmuntal@gmail.com>
This commit is contained in:
parent
9aad012a6e
commit
2c35900fe4
@ -4,4 +4,4 @@
|
||||
|
||||
package poll
|
||||
|
||||
var TestHookDidSendFile = func(dstFD *FD, src int, written int64, err error, handled bool) {}
|
||||
var TestHookDidSendFile = func(dstFD *FD, src uintptr, written int64, err error, handled bool) {}
|
||||
|
@ -27,29 +27,29 @@ import (
|
||||
// If handled is false, sendfile was unable to perform the copy,
|
||||
// has not modified the source or destination,
|
||||
// and the caller should perform the copy using a fallback implementation.
|
||||
func SendFile(dstFD *FD, src int, size int64) (n int64, err error, handled bool) {
|
||||
func SendFile(dstFD *FD, src uintptr, size int64) (n int64, err error, handled bool) {
|
||||
if goos := runtime.GOOS; goos == "linux" || goos == "android" {
|
||||
// Linux's sendfile doesn't require any setup:
|
||||
// It sends from the current position of the source file and
|
||||
// updates the position of the source after sending.
|
||||
return sendFile(dstFD, src, nil, size)
|
||||
return sendFile(dstFD, int(src), nil, size)
|
||||
}
|
||||
|
||||
// Non-Linux sendfile implementations don't use the current position of the source file,
|
||||
// so we need to look up the position, pass it explicitly, and adjust it after
|
||||
// sendfile returns.
|
||||
start, err := ignoringEINTR2(func() (int64, error) {
|
||||
return syscall.Seek(src, 0, io.SeekCurrent)
|
||||
return syscall.Seek(int(src), 0, io.SeekCurrent)
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err, false
|
||||
}
|
||||
|
||||
pos := start
|
||||
n, err, handled = sendFile(dstFD, src, &pos, size)
|
||||
n, err, handled = sendFile(dstFD, int(src), &pos, size)
|
||||
if n > 0 {
|
||||
ignoringEINTR2(func() (int64, error) {
|
||||
return syscall.Seek(src, start+n, io.SeekStart)
|
||||
return syscall.Seek(int(src), start+n, io.SeekStart)
|
||||
})
|
||||
}
|
||||
return n, err, handled
|
||||
@ -58,7 +58,7 @@ func SendFile(dstFD *FD, src int, size int64) (n int64, err error, handled bool)
|
||||
// sendFile wraps the sendfile system call.
|
||||
func sendFile(dstFD *FD, src int, offset *int64, size int64) (written int64, err error, handled bool) {
|
||||
defer func() {
|
||||
TestHookDidSendFile(dstFD, src, written, err, handled)
|
||||
TestHookDidSendFile(dstFD, uintptr(src), written, err, handled)
|
||||
}()
|
||||
if err := dstFD.writeLock(); err != nil {
|
||||
return 0, err, false
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
// SendFile wraps the TransmitFile call.
|
||||
func SendFile(fd *FD, src syscall.Handle, size int64) (written int64, err error, handled bool) {
|
||||
func SendFile(fd *FD, src uintptr, size int64) (written int64, err error, handled bool) {
|
||||
defer func() {
|
||||
TestHookDidSendFile(fd, 0, written, err, written > 0)
|
||||
}()
|
||||
@ -18,7 +18,8 @@ func SendFile(fd *FD, src syscall.Handle, size int64) (written int64, err error,
|
||||
// TransmitFile does not work with pipes
|
||||
return 0, syscall.ESPIPE, false
|
||||
}
|
||||
if ft, _ := syscall.GetFileType(src); ft == syscall.FILE_TYPE_PIPE {
|
||||
hsrc := syscall.Handle(src)
|
||||
if ft, _ := syscall.GetFileType(hsrc); ft == syscall.FILE_TYPE_PIPE {
|
||||
return 0, syscall.ESPIPE, false
|
||||
}
|
||||
|
||||
@ -29,11 +30,11 @@ func SendFile(fd *FD, src syscall.Handle, size int64) (written int64, err error,
|
||||
|
||||
// Get the file size so we don't read past the end of the file.
|
||||
var fi syscall.ByHandleFileInformation
|
||||
if err := syscall.GetFileInformationByHandle(src, &fi); err != nil {
|
||||
if err := syscall.GetFileInformationByHandle(hsrc, &fi); err != nil {
|
||||
return 0, err, false
|
||||
}
|
||||
fileSize := int64(fi.FileSizeHigh)<<32 + int64(fi.FileSizeLow)
|
||||
startpos, err := syscall.Seek(src, 0, io.SeekCurrent)
|
||||
startpos, err := syscall.Seek(hsrc, 0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return 0, err, false
|
||||
}
|
||||
@ -49,7 +50,7 @@ func SendFile(fd *FD, src syscall.Handle, size int64) (written int64, err error,
|
||||
// Some versions of Windows (Windows 10 1803) do not set
|
||||
// file position after TransmitFile completes.
|
||||
// So just use Seek to set file position.
|
||||
_, serr := syscall.Seek(src, startpos+written, io.SeekStart)
|
||||
_, serr := syscall.Seek(hsrc, startpos+written, io.SeekStart)
|
||||
if err != nil {
|
||||
err = serr
|
||||
}
|
||||
@ -62,7 +63,7 @@ func SendFile(fd *FD, src syscall.Handle, size int64) (written int64, err error,
|
||||
const maxChunkSizePerCall = int64(0x7fffffff - 1)
|
||||
|
||||
o := &fd.wop
|
||||
o.handle = src
|
||||
o.handle = hsrc
|
||||
for size > 0 {
|
||||
chunkSize := maxChunkSizePerCall
|
||||
if chunkSize > size {
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (darwin && !ios) || dragonfly || freebsd || solaris
|
||||
//go:build linux || (darwin && !ios) || dragonfly || freebsd || solaris || windows
|
||||
|
||||
package net
|
||||
|
||||
@ -44,7 +44,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, handled = poll.SendFile(&c.pfd, int(fd), remain)
|
||||
written, werr, handled = poll.SendFile(&c.pfd, fd, remain)
|
||||
return true
|
||||
})
|
||||
if err == nil {
|
@ -1,55 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"internal/poll"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
const supportsSendfile = true
|
||||
|
||||
// sendFile copies the contents of r to c using the sendfile
|
||||
// system call to minimize copies.
|
||||
//
|
||||
// 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) {
|
||||
var remain int64 = 0 // 0 indicates sending until EOF
|
||||
|
||||
lr, ok := r.(*io.LimitedReader)
|
||||
if ok {
|
||||
remain, r = lr.N, lr.R
|
||||
if remain <= 0 {
|
||||
return 0, nil, true
|
||||
}
|
||||
}
|
||||
f, ok := r.(*os.File)
|
||||
if !ok {
|
||||
return 0, nil, false
|
||||
}
|
||||
|
||||
sc, err := f.SyscallConn()
|
||||
if err != nil {
|
||||
return 0, nil, false
|
||||
}
|
||||
|
||||
var werr error
|
||||
err = sc.Read(func(fd uintptr) bool {
|
||||
written, werr, handled = poll.SendFile(&c.pfd, int(fd), remain)
|
||||
return true
|
||||
})
|
||||
if err == nil {
|
||||
err = werr
|
||||
}
|
||||
|
||||
if lr != nil {
|
||||
lr.N = remain - written
|
||||
}
|
||||
return written, wrapSyscallError("sendfile", err), handled
|
||||
}
|
@ -49,7 +49,7 @@ func expectSendfile(t *testing.T, wantConn Conn, f func()) {
|
||||
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 uintptr, written int64, err error, handled bool) {
|
||||
if called {
|
||||
t.Error("internal/poll.SendFile called multiple times, want one call")
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"internal/poll"
|
||||
"io"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const supportsSendfile = true
|
||||
|
||||
// TODO: deduplicate this file with sendfile_linux.go and sendfile_unix_alt.go.
|
||||
|
||||
// sendFile copies the contents of r to c using the sendfile
|
||||
// system call to minimize copies.
|
||||
//
|
||||
// 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) {
|
||||
var remain int64 = 0 // by default, copy until EOF.
|
||||
|
||||
lr, ok := r.(*io.LimitedReader)
|
||||
if ok {
|
||||
remain, r = lr.N, lr.R
|
||||
if remain <= 0 {
|
||||
return 0, nil, true
|
||||
}
|
||||
}
|
||||
|
||||
// r might be an *os.File or an os.fileWithoutWriteTo.
|
||||
// Type assert to an interface rather than *os.File directly to handle the latter case.
|
||||
f, ok := r.(syscall.Conn)
|
||||
if !ok {
|
||||
return 0, nil, false
|
||||
}
|
||||
|
||||
sc, err := f.SyscallConn()
|
||||
if err != nil {
|
||||
return 0, nil, false
|
||||
}
|
||||
|
||||
var werr error
|
||||
err = sc.Read(func(fd uintptr) bool {
|
||||
written, werr, handled = poll.SendFile(&c.pfd, syscall.Handle(fd), remain)
|
||||
return true
|
||||
})
|
||||
if err == nil {
|
||||
err = werr
|
||||
}
|
||||
|
||||
if lr != nil {
|
||||
lr.N = remain - written
|
||||
}
|
||||
|
||||
return written, wrapSyscallError("sendfile", err), handled
|
||||
}
|
@ -48,10 +48,10 @@ func hookSendFileTB(tb testing.TB) *copyFileHook {
|
||||
tb.Cleanup(func() {
|
||||
poll.TestHookDidSendFile = orig
|
||||
})
|
||||
poll.TestHookDidSendFile = func(dstFD *poll.FD, src int, written int64, err error, handled bool) {
|
||||
poll.TestHookDidSendFile = func(dstFD *poll.FD, src uintptr, written int64, err error, handled bool) {
|
||||
hook.called = true
|
||||
hook.dstfd = dstFD.Sysfd
|
||||
hook.srcfd = src
|
||||
hook.srcfd = int(src)
|
||||
hook.written = written
|
||||
hook.err = err
|
||||
hook.handled = handled
|
||||
|
@ -111,10 +111,10 @@ func hookSendFile(t *testing.T) *sendFileHook {
|
||||
t.Cleanup(func() {
|
||||
poll.TestHookDidSendFile = orig
|
||||
})
|
||||
poll.TestHookDidSendFile = func(dstFD *poll.FD, src int, written int64, err error, handled bool) {
|
||||
poll.TestHookDidSendFile = func(dstFD *poll.FD, src uintptr, written int64, err error, handled bool) {
|
||||
h.called = true
|
||||
h.dstfd = dstFD.Sysfd
|
||||
h.srcfd = src
|
||||
h.srcfd = int(src)
|
||||
h.written = written
|
||||
h.err = err
|
||||
h.handled = handled
|
||||
|
@ -28,7 +28,7 @@ func (f *File) writeTo(w io.Writer) (written int64, handled bool, err error) {
|
||||
}
|
||||
|
||||
rerr := sc.Read(func(fd uintptr) (done bool) {
|
||||
written, err, handled = poll.SendFile(pfd, int(fd), 0)
|
||||
written, err, handled = poll.SendFile(pfd, fd, 0)
|
||||
return true
|
||||
})
|
||||
|
||||
|
@ -78,7 +78,7 @@ func (f *File) readFrom(r io.Reader) (written int64, handled bool, err error) {
|
||||
// https://docs.oracle.com/cd/E88353_01/html/E37843/sendfile-3c.html and
|
||||
// https://illumos.org/man/3EXT/sendfile for more details.
|
||||
rerr := sc.Read(func(fd uintptr) bool {
|
||||
written, err, handled = poll.SendFile(&f.pfd, int(fd), remain)
|
||||
written, err, handled = poll.SendFile(&f.pfd, fd, remain)
|
||||
return true
|
||||
})
|
||||
if lr != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user