Imported Upstream version 4.8.1
[platform/upstream/gcc48.git] / libgo / go / net / sendfile_freebsd.go
1 // Copyright 2011 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 // maxSendfileSize is the largest chunk size we ask the kernel to copy
14 // at a time.
15 const maxSendfileSize int = 4 << 20
16
17 // sendFile copies the contents of r to c using the sendfile
18 // system call to minimize copies.
19 //
20 // if handled == true, sendFile returns the number of bytes copied and any
21 // non-EOF error.
22 //
23 // if handled == false, sendFile performed no work.
24 func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
25         // FreeBSD uses 0 as the "until EOF" value. If you pass in more bytes than the
26         // file contains, it will loop back to the beginning ad nauseum until it's sent
27         // exactly the number of bytes told to. As such, we need to know exactly how many
28         // bytes to send.
29         var remain int64 = 0
30
31         lr, ok := r.(*io.LimitedReader)
32         if ok {
33                 remain, r = lr.N, lr.R
34                 if remain <= 0 {
35                         return 0, nil, true
36                 }
37         }
38         f, ok := r.(*os.File)
39         if !ok {
40                 return 0, nil, false
41         }
42
43         if remain == 0 {
44                 fi, err := f.Stat()
45                 if err != nil {
46                         return 0, err, false
47                 }
48
49                 remain = fi.Size()
50         }
51
52         // The other quirk with FreeBSD's sendfile implementation is that it doesn't
53         // use the current position of the file -- if you pass it offset 0, it starts
54         // from offset 0. There's no way to tell it "start from current position", so
55         // we have to manage that explicitly.
56         pos, err := f.Seek(0, os.SEEK_CUR)
57         if err != nil {
58                 return 0, err, false
59         }
60
61         c.wio.Lock()
62         defer c.wio.Unlock()
63         if err := c.incref(false); err != nil {
64                 return 0, err, true
65         }
66         defer c.decref()
67
68         dst := c.sysfd
69         src := int(f.Fd())
70         for remain > 0 {
71                 n := maxSendfileSize
72                 if int64(n) > remain {
73                         n = int(remain)
74                 }
75                 pos1 := pos
76                 n, err1 := syscall.Sendfile(dst, src, &pos1, n)
77                 if n > 0 {
78                         pos += int64(n)
79                         written += int64(n)
80                         remain -= int64(n)
81                 }
82                 if n == 0 && err1 == nil {
83                         break
84                 }
85                 if err1 == syscall.EAGAIN {
86                         if err1 = c.pollServer.WaitWrite(c); err1 == nil {
87                                 continue
88                         }
89                 }
90                 if err1 == syscall.EINTR {
91                         continue
92                 }
93                 if err1 != nil {
94                         // This includes syscall.ENOSYS (no kernel
95                         // support) and syscall.EINVAL (fd types which
96                         // don't implement sendfile together)
97                         err = &OpError{"sendfile", c.net, c.raddr, err1}
98                         break
99                 }
100         }
101         if lr != nil {
102                 lr.N = remain
103         }
104         return written, err, written > 0
105 }