github.com/dannin/go@v0.0.0-20161031215817-d35dfd405eaa/src/net/writev_unix.go (about)

     1  // Copyright 2016 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  // +build darwin dragonfly freebsd linux netbsd openbsd
     6  
     7  package net
     8  
     9  import (
    10  	"io"
    11  	"os"
    12  	"syscall"
    13  	"unsafe"
    14  )
    15  
    16  func (c *conn) writeBuffers(v *Buffers) (int64, error) {
    17  	if !c.ok() {
    18  		return 0, syscall.EINVAL
    19  	}
    20  	n, err := c.fd.writeBuffers(v)
    21  	if err != nil {
    22  		return n, &OpError{Op: "writev", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
    23  	}
    24  	return n, nil
    25  }
    26  
    27  func (fd *netFD) writeBuffers(v *Buffers) (n int64, err error) {
    28  	if err := fd.writeLock(); err != nil {
    29  		return 0, err
    30  	}
    31  	defer fd.writeUnlock()
    32  	if err := fd.pd.prepareWrite(); err != nil {
    33  		return 0, err
    34  	}
    35  
    36  	var iovecs []syscall.Iovec
    37  	if fd.iovecs != nil {
    38  		iovecs = *fd.iovecs
    39  	}
    40  	// TODO: read from sysconf(_SC_IOV_MAX)? The Linux default is
    41  	// 1024 and this seems conservative enough for now. Darwin's
    42  	// UIO_MAXIOV also seems to be 1024.
    43  	maxVec := 1024
    44  
    45  	for len(*v) > 0 {
    46  		iovecs = iovecs[:0]
    47  		for _, chunk := range *v {
    48  			if len(chunk) == 0 {
    49  				continue
    50  			}
    51  			iovecs = append(iovecs, syscall.Iovec{Base: &chunk[0]})
    52  			if fd.isStream && len(chunk) > 1<<30 {
    53  				iovecs[len(iovecs)-1].SetLen(1 << 30)
    54  				break // continue chunk on next writev
    55  			}
    56  			iovecs[len(iovecs)-1].SetLen(len(chunk))
    57  			if len(iovecs) == maxVec {
    58  				break
    59  			}
    60  		}
    61  		if len(iovecs) == 0 {
    62  			break
    63  		}
    64  		fd.iovecs = &iovecs // cache
    65  
    66  		wrote, _, e0 := syscall.Syscall(syscall.SYS_WRITEV,
    67  			uintptr(fd.sysfd),
    68  			uintptr(unsafe.Pointer(&iovecs[0])),
    69  			uintptr(len(iovecs)))
    70  		if wrote == ^uintptr(0) {
    71  			wrote = 0
    72  		}
    73  		testHookDidWritev(int(wrote))
    74  		n += int64(wrote)
    75  		v.consume(int64(wrote))
    76  		if e0 == syscall.EAGAIN {
    77  			if err = fd.pd.waitWrite(); err == nil {
    78  				continue
    79  			}
    80  		} else if e0 != 0 {
    81  			err = syscall.Errno(e0)
    82  		}
    83  		if err != nil {
    84  			break
    85  		}
    86  		if n == 0 {
    87  			err = io.ErrUnexpectedEOF
    88  			break
    89  		}
    90  	}
    91  	if _, ok := err.(syscall.Errno); ok {
    92  		err = os.NewSyscallError("writev", err)
    93  	}
    94  	return n, err
    95  }