github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/net/sendfile_freebsd.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  	// 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.pd.WaitWrite(); 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  }