writev.mx raw

   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  //go:build unix
   6  
   7  package poll
   8  
   9  import (
  10  	"io"
  11  	"runtime"
  12  	"syscall"
  13  )
  14  
  15  // Writev wraps the writev system call.
  16  func (fd *FD) Writev(v *[][]byte) (int64, error) {
  17  	if err := fd.writeLock(); err != nil {
  18  		return 0, err
  19  	}
  20  	defer fd.writeUnlock()
  21  	if err := fd.pd.prepareWrite(fd.isFile); err != nil {
  22  		return 0, err
  23  	}
  24  
  25  	var iovecs []syscall.Iovec
  26  	if fd.iovecs != nil {
  27  		iovecs = *fd.iovecs
  28  	}
  29  	// TODO: read from sysconf(_SC_IOV_MAX)? The Linux default is
  30  	// 1024 and this seems conservative enough for now. Darwin's
  31  	// UIO_MAXIOV also seems to be 1024.
  32  	maxVec := 1024
  33  	if runtime.GOOS == "aix" || runtime.GOOS == "solaris" {
  34  		// IOV_MAX is set to XOPEN_IOV_MAX on AIX and Solaris.
  35  		maxVec = 16
  36  	}
  37  
  38  	var n int64
  39  	var err error
  40  	for len(*v) > 0 {
  41  		iovecs = iovecs[:0]
  42  		for _, chunk := range *v {
  43  			if len(chunk) == 0 {
  44  				continue
  45  			}
  46  			iovecs = append(iovecs, newIovecWithBase(&chunk[0]))
  47  			if fd.IsStream && len(chunk) > 1<<30 {
  48  				iovecs[len(iovecs)-1].SetLen(1 << 30)
  49  				break // continue chunk on next writev
  50  			}
  51  			iovecs[len(iovecs)-1].SetLen(len(chunk))
  52  			if len(iovecs) == maxVec {
  53  				break
  54  			}
  55  		}
  56  		if len(iovecs) == 0 {
  57  			break
  58  		}
  59  		if fd.iovecs == nil {
  60  			fd.iovecs = &[]syscall.Iovec{}
  61  		}
  62  		*fd.iovecs = iovecs // cache
  63  
  64  		var wrote uintptr
  65  		wrote, err = writev(fd.Sysfd, iovecs)
  66  		if wrote == ^uintptr(0) {
  67  			wrote = 0
  68  		}
  69  		TestHookDidWritev(int(wrote))
  70  		n += int64(wrote)
  71  		consume(v, int64(wrote))
  72  		clear(iovecs)
  73  		if err != nil {
  74  			if err == syscall.EINTR {
  75  				continue
  76  			}
  77  			if err == syscall.EAGAIN {
  78  				if err = fd.pd.waitWrite(fd.isFile); err == nil {
  79  					continue
  80  				}
  81  			}
  82  			break
  83  		}
  84  		if n == 0 {
  85  			err = io.ErrUnexpectedEOF
  86  			break
  87  		}
  88  	}
  89  	return n, err
  90  }
  91