2017-07-18 François Dumont <fdumont@gcc.gnu.org>
[official-gcc.git] / libgo / go / net / sendfile_solaris.go
blobadd70c3147e653d69b66feb8190cc6342ec6f8cd
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.
5 package net
7 import (
8 "io"
9 "os"
10 "syscall"
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"
17 // maxSendfileSize is the largest chunk size we ask the kernel to copy
18 // at a time.
19 const maxSendfileSize int = 4 << 20
21 // sendFile copies the contents of r to c using the sendfile
22 // system call to minimize copies.
24 // if handled == true, sendFile returns the number of bytes copied and any
25 // non-EOF error.
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
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
42 f, ok := r.(*os.File)
43 if !ok {
44 return 0, nil, false
47 if remain == 0 {
48 fi, err := f.Stat()
49 if err != nil {
50 return 0, err, false
53 remain = fi.Size()
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
65 if err := c.writeLock(); err != nil {
66 return 0, err, true
68 defer c.writeUnlock()
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)
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
86 if n > 0 {
87 pos += int64(n)
88 written += int64(n)
89 remain -= int64(n)
91 if n == 0 && err1 == nil {
92 break
94 if err1 == syscall.EAGAIN {
95 if err1 = c.pd.waitWrite(); err1 == nil {
96 continue
99 if err1 == syscall.EINTR {
100 continue
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
110 if lr != nil {
111 lr.N = remain
113 if err != nil {
114 err = os.NewSyscallError("sendfile", err)
116 return written, err, written > 0