github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/net/sendfile_linux.go (about)

     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  	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  	if err := c.writeLock(); err != nil {
    40  		return 0, err, true
    41  	}
    42  	defer c.writeUnlock()
    43  
    44  	dst := c.sysfd
    45  	src := int(f.Fd())
    46  	for remain > 0 {
    47  		n := maxSendfileSize
    48  		if int64(n) > remain {
    49  			n = int(remain)
    50  		}
    51  		n, err1 := syscall.Sendfile(dst, src, nil, n)
    52  		if n > 0 {
    53  			written += int64(n)
    54  			remain -= int64(n)
    55  		}
    56  		if n == 0 && err1 == nil {
    57  			break
    58  		}
    59  		if err1 == syscall.EAGAIN {
    60  			if err1 = c.pd.WaitWrite(); err1 == nil {
    61  				continue
    62  			}
    63  		}
    64  		if err1 != nil {
    65  			// This includes syscall.ENOSYS (no kernel
    66  			// support) and syscall.EINVAL (fd types which
    67  			// don't implement sendfile)
    68  			err = err1
    69  			break
    70  		}
    71  	}
    72  	if lr != nil {
    73  		lr.N = remain
    74  	}
    75  	if err != nil {
    76  		err = os.NewSyscallError("sendfile", err)
    77  	}
    78  	return written, err, written > 0
    79  }