Brad Fitzpatrick | b0f39cc | 2011-05-25 10:15:26 -0700 | [diff] [blame] | 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. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 24 | func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { |
Brad Fitzpatrick | b0f39cc | 2011-05-25 10:15:26 -0700 | [diff] [blame] | 25 | var remain int64 = 1 << 62 // by default, copy until EOF |
| 26 | |
| 27 | lr, ok := r.(*io.LimitedReader) |
| 28 | if ok { |
| 29 | remain, r = lr.N, lr.R |
| 30 | if remain <= 0 { |
| 31 | return 0, nil, true |
| 32 | } |
| 33 | } |
| 34 | f, ok := r.(*os.File) |
| 35 | if !ok { |
| 36 | return 0, nil, false |
| 37 | } |
| 38 | |
| 39 | c.wio.Lock() |
| 40 | defer c.wio.Unlock() |
Russ Cox | 5e4e3d8 | 2012-02-14 00:40:37 -0500 | [diff] [blame] | 41 | if err := c.incref(false); err != nil { |
| 42 | return 0, err, true |
| 43 | } |
Brad Fitzpatrick | b0f39cc | 2011-05-25 10:15:26 -0700 | [diff] [blame] | 44 | defer c.decref() |
Brad Fitzpatrick | b0f39cc | 2011-05-25 10:15:26 -0700 | [diff] [blame] | 45 | |
| 46 | dst := c.sysfd |
Brad Fitzpatrick | 4152b43 | 2012-02-10 14:16:15 +1100 | [diff] [blame] | 47 | src := int(f.Fd()) |
Brad Fitzpatrick | b0f39cc | 2011-05-25 10:15:26 -0700 | [diff] [blame] | 48 | for remain > 0 { |
| 49 | n := maxSendfileSize |
| 50 | if int64(n) > remain { |
| 51 | n = int(remain) |
| 52 | } |
Mikio Hara | 28397be | 2012-02-01 00:36:45 +0900 | [diff] [blame] | 53 | n, err1 := syscall.Sendfile(dst, src, nil, n) |
Brad Fitzpatrick | b0f39cc | 2011-05-25 10:15:26 -0700 | [diff] [blame] | 54 | if n > 0 { |
| 55 | written += int64(n) |
| 56 | remain -= int64(n) |
| 57 | } |
Mikio Hara | 28397be | 2012-02-01 00:36:45 +0900 | [diff] [blame] | 58 | if n == 0 && err1 == nil { |
Brad Fitzpatrick | b0f39cc | 2011-05-25 10:15:26 -0700 | [diff] [blame] | 59 | break |
| 60 | } |
Dave Cheney | 9fb9699 | 2012-12-05 15:59:01 +1100 | [diff] [blame] | 61 | if err1 == syscall.EAGAIN { |
Dmitriy Vyukov | b09d881 | 2013-03-13 00:03:00 +0400 | [diff] [blame] | 62 | if err1 = c.pd.WaitWrite(); err1 == nil { |
Russ Cox | 5e4e3d8 | 2012-02-14 00:40:37 -0500 | [diff] [blame] | 63 | continue |
| 64 | } |
Brad Fitzpatrick | b0f39cc | 2011-05-25 10:15:26 -0700 | [diff] [blame] | 65 | } |
Mikio Hara | 28397be | 2012-02-01 00:36:45 +0900 | [diff] [blame] | 66 | if err1 != nil { |
Brad Fitzpatrick | b0f39cc | 2011-05-25 10:15:26 -0700 | [diff] [blame] | 67 | // This includes syscall.ENOSYS (no kernel |
| 68 | // support) and syscall.EINVAL (fd types which |
| 69 | // don't implement sendfile together) |
Mikio Hara | 28397be | 2012-02-01 00:36:45 +0900 | [diff] [blame] | 70 | err = &OpError{"sendfile", c.net, c.raddr, err1} |
Brad Fitzpatrick | b0f39cc | 2011-05-25 10:15:26 -0700 | [diff] [blame] | 71 | break |
| 72 | } |
| 73 | } |
| 74 | if lr != nil { |
| 75 | lr.N = remain |
| 76 | } |
| 77 | return written, err, written > 0 |
| 78 | } |