github.com/fenixara/go@v0.0.0-20170127160404-96ea0918e670/src/net/sendfile_solaris.go (about)

     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.
     4  
     5  package net
     6  
     7  import (
     8  	"io"
     9  	"os"
    10  	"syscall"
    11  )
    12  
    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"
    16  
    17  // maxSendfileSize is the largest chunk size we ask the kernel to copy
    18  // at a time.
    19  const maxSendfileSize int = 4 << 20
    20  
    21  // sendFile copies the contents of r to c using the sendfile
    22  // system call to minimize copies.
    23  //
    24  // if handled == true, sendFile returns the number of bytes copied and any
    25  // non-EOF error.
    26  //
    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
    34  
    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
    40  		}
    41  	}
    42  	f, ok := r.(*os.File)
    43  	if !ok {
    44  		return 0, nil, false
    45  	}
    46  
    47  	if remain == 0 {
    48  		fi, err := f.Stat()
    49  		if err != nil {
    50  			return 0, err, false
    51  		}
    52  
    53  		remain = fi.Size()
    54  	}
    55  
    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
    63  	}
    64  
    65  	if err := c.writeLock(); err != nil {
    66  		return 0, err, true
    67  	}
    68  	defer c.writeUnlock()
    69  
    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)
    76  		}
    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
    84  			}
    85  		}
    86  		if n > 0 {
    87  			pos += int64(n)
    88  			written += int64(n)
    89  			remain -= int64(n)
    90  		}
    91  		if n == 0 && err1 == nil {
    92  			break
    93  		}
    94  		if err1 == syscall.EAGAIN {
    95  			if err1 = c.pd.waitWrite(); err1 == nil {
    96  				continue
    97  			}
    98  		}
    99  		if err1 == syscall.EINTR {
   100  			continue
   101  		}
   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
   108  		}
   109  	}
   110  	if lr != nil {
   111  		lr.N = remain
   112  	}
   113  	if err != nil {
   114  		err = os.NewSyscallError("sendfile", err)
   115  	}
   116  	return written, err, written > 0
   117  }