github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/net/sendfile_solaris.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package net 6 7 import ( 8 "io" 9 "os" 10 "syscall" 11 ) 12 13 // Not strictly needed, but very helpful for debugging, see issue #10221. 14 //go:cgo_import_dynamic _ _ "libsendfile.so" 15 //go:cgo_import_dynamic _ _ "libsocket.so" 16 17 // maxSendfileSize is the largest chunk size we ask the kernel to copy 18 // at a time. 19 const maxSendfileSize int = 4 << 20 20 21 // sendFile copies the contents of r to c using the sendfile 22 // system call to minimize copies. 23 // 24 // if handled == true, sendFile returns the number of bytes copied and any 25 // non-EOF error. 26 // 27 // if handled == false, sendFile performed no work. 28 func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { 29 // Solaris uses 0 as the "until EOF" value. If you pass in more bytes than the 30 // file contains, it will loop back to the beginning ad nauseam until it's sent 31 // exactly the number of bytes told to. As such, we need to know exactly how many 32 // bytes to send. 33 var remain int64 = 0 34 35 lr, ok := r.(*io.LimitedReader) 36 if ok { 37 remain, r = lr.N, lr.R 38 if remain <= 0 { 39 return 0, nil, true 40 } 41 } 42 f, ok := r.(*os.File) 43 if !ok { 44 return 0, nil, false 45 } 46 47 if remain == 0 { 48 fi, err := f.Stat() 49 if err != nil { 50 return 0, err, false 51 } 52 53 remain = fi.Size() 54 } 55 56 // The other quirk with Solaris's sendfile implementation is that it doesn't 57 // use the current position of the file -- if you pass it offset 0, it starts 58 // from offset 0. There's no way to tell it "start from current position", so 59 // we have to manage that explicitly. 60 pos, err := f.Seek(0, io.SeekCurrent) 61 if err != nil { 62 return 0, err, false 63 } 64 65 if err := c.writeLock(); err != nil { 66 return 0, err, true 67 } 68 defer c.writeUnlock() 69 70 dst := c.sysfd 71 src := int(f.Fd()) 72 for remain > 0 { 73 n := maxSendfileSize 74 if int64(n) > remain { 75 n = int(remain) 76 } 77 pos1 := pos 78 n, err1 := syscall.Sendfile(dst, src, &pos1, n) 79 if err1 == syscall.EAGAIN || err1 == syscall.EINTR { 80 // partial write may have occurred 81 if n = int(pos1 - pos); n == 0 { 82 // nothing more to write 83 err1 = nil 84 } 85 } 86 if n > 0 { 87 pos += int64(n) 88 written += int64(n) 89 remain -= int64(n) 90 } 91 if n == 0 && err1 == nil { 92 break 93 } 94 if err1 == syscall.EAGAIN { 95 if err1 = c.pd.waitWrite(); err1 == nil { 96 continue 97 } 98 } 99 if err1 == syscall.EINTR { 100 continue 101 } 102 if err1 != nil { 103 // This includes syscall.ENOSYS (no kernel 104 // support) and syscall.EINVAL (fd types which 105 // don't implement sendfile) 106 err = err1 107 break 108 } 109 } 110 if lr != nil { 111 lr.N = remain 112 } 113 if err != nil { 114 err = os.NewSyscallError("sendfile", err) 115 } 116 return written, err, written > 0 117 }