mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +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
|
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,
|
// If handled is false, sendfile was unable to perform the copy,
|
||||||
// has not modified the source or destination,
|
// has not modified the source or destination,
|
||||||
// and the caller should perform the copy using a fallback implementation.
|
// 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" {
|
if goos := runtime.GOOS; goos == "linux" || goos == "android" {
|
||||||
// Linux's sendfile doesn't require any setup:
|
// Linux's sendfile doesn't require any setup:
|
||||||
// It sends from the current position of the source file and
|
// It sends from the current position of the source file and
|
||||||
// updates the position of the source after sending.
|
// 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,
|
// 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
|
// so we need to look up the position, pass it explicitly, and adjust it after
|
||||||
// sendfile returns.
|
// sendfile returns.
|
||||||
start, err := ignoringEINTR2(func() (int64, error) {
|
start, err := ignoringEINTR2(func() (int64, error) {
|
||||||
return syscall.Seek(src, 0, io.SeekCurrent)
|
return syscall.Seek(int(src), 0, io.SeekCurrent)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err, false
|
return 0, err, false
|
||||||
}
|
}
|
||||||
|
|
||||||
pos := start
|
pos := start
|
||||||
n, err, handled = sendFile(dstFD, src, &pos, size)
|
n, err, handled = sendFile(dstFD, int(src), &pos, size)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
ignoringEINTR2(func() (int64, error) {
|
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
|
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.
|
// sendFile wraps the sendfile system call.
|
||||||
func sendFile(dstFD *FD, src int, offset *int64, size int64) (written int64, err error, handled bool) {
|
func sendFile(dstFD *FD, src int, offset *int64, size int64) (written int64, err error, handled bool) {
|
||||||
defer func() {
|
defer func() {
|
||||||
TestHookDidSendFile(dstFD, src, written, err, handled)
|
TestHookDidSendFile(dstFD, uintptr(src), written, err, handled)
|
||||||
}()
|
}()
|
||||||
if err := dstFD.writeLock(); err != nil {
|
if err := dstFD.writeLock(); err != nil {
|
||||||
return 0, err, false
|
return 0, err, false
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// SendFile wraps the TransmitFile call.
|
// 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() {
|
defer func() {
|
||||||
TestHookDidSendFile(fd, 0, written, err, written > 0)
|
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
|
// TransmitFile does not work with pipes
|
||||||
return 0, syscall.ESPIPE, false
|
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
|
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.
|
// Get the file size so we don't read past the end of the file.
|
||||||
var fi syscall.ByHandleFileInformation
|
var fi syscall.ByHandleFileInformation
|
||||||
if err := syscall.GetFileInformationByHandle(src, &fi); err != nil {
|
if err := syscall.GetFileInformationByHandle(hsrc, &fi); err != nil {
|
||||||
return 0, err, false
|
return 0, err, false
|
||||||
}
|
}
|
||||||
fileSize := int64(fi.FileSizeHigh)<<32 + int64(fi.FileSizeLow)
|
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 {
|
if err != nil {
|
||||||
return 0, err, false
|
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
|
// Some versions of Windows (Windows 10 1803) do not set
|
||||||
// file position after TransmitFile completes.
|
// file position after TransmitFile completes.
|
||||||
// So just use Seek to set file position.
|
// 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 {
|
if err != nil {
|
||||||
err = serr
|
err = serr
|
||||||
}
|
}
|
||||||
@ -62,7 +63,7 @@ func SendFile(fd *FD, src syscall.Handle, size int64) (written int64, err error,
|
|||||||
const maxChunkSizePerCall = int64(0x7fffffff - 1)
|
const maxChunkSizePerCall = int64(0x7fffffff - 1)
|
||||||
|
|
||||||
o := &fd.wop
|
o := &fd.wop
|
||||||
o.handle = src
|
o.handle = hsrc
|
||||||
for size > 0 {
|
for size > 0 {
|
||||||
chunkSize := maxChunkSizePerCall
|
chunkSize := maxChunkSizePerCall
|
||||||
if chunkSize > size {
|
if chunkSize > size {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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
|
package net
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
|
|||||||
|
|
||||||
var werr error
|
var werr error
|
||||||
err = sc.Read(func(fd uintptr) bool {
|
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
|
return true
|
||||||
})
|
})
|
||||||
if err == nil {
|
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
|
gotFD *poll.FD
|
||||||
gotErr error
|
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 {
|
if called {
|
||||||
t.Error("internal/poll.SendFile called multiple times, want one call")
|
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() {
|
tb.Cleanup(func() {
|
||||||
poll.TestHookDidSendFile = orig
|
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.called = true
|
||||||
hook.dstfd = dstFD.Sysfd
|
hook.dstfd = dstFD.Sysfd
|
||||||
hook.srcfd = src
|
hook.srcfd = int(src)
|
||||||
hook.written = written
|
hook.written = written
|
||||||
hook.err = err
|
hook.err = err
|
||||||
hook.handled = handled
|
hook.handled = handled
|
||||||
|
@ -111,10 +111,10 @@ func hookSendFile(t *testing.T) *sendFileHook {
|
|||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
poll.TestHookDidSendFile = orig
|
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.called = true
|
||||||
h.dstfd = dstFD.Sysfd
|
h.dstfd = dstFD.Sysfd
|
||||||
h.srcfd = src
|
h.srcfd = int(src)
|
||||||
h.written = written
|
h.written = written
|
||||||
h.err = err
|
h.err = err
|
||||||
h.handled = handled
|
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) {
|
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
|
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://docs.oracle.com/cd/E88353_01/html/E37843/sendfile-3c.html and
|
||||||
// https://illumos.org/man/3EXT/sendfile for more details.
|
// https://illumos.org/man/3EXT/sendfile for more details.
|
||||||
rerr := sc.Read(func(fd uintptr) bool {
|
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
|
return true
|
||||||
})
|
})
|
||||||
if lr != nil {
|
if lr != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user