Home | History | Annotate | Download | only in net
      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 }
     96