| // Copyright 2016 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build darwin dragonfly freebsd linux netbsd openbsd |
| |
| package net |
| |
| import ( |
| "io" |
| "os" |
| "syscall" |
| "unsafe" |
| ) |
| |
| func (c *conn) writeBuffers(v *Buffers) (int64, error) { |
| if !c.ok() { |
| return 0, syscall.EINVAL |
| } |
| n, err := c.fd.writeBuffers(v) |
| if err != nil { |
| return n, &OpError{Op: "writev", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} |
| } |
| return n, nil |
| } |
| |
| func (fd *netFD) writeBuffers(v *Buffers) (n int64, err error) { |
| if err := fd.writeLock(); err != nil { |
| return 0, err |
| } |
| defer fd.writeUnlock() |
| if err := fd.pd.prepareWrite(); err != nil { |
| return 0, err |
| } |
| |
| var iovecs []syscall.Iovec |
| if fd.iovecs != nil { |
| iovecs = *fd.iovecs |
| } |
| // TODO: read from sysconf(_SC_IOV_MAX)? The Linux default is |
| // 1024 and this seems conservative enough for now. Darwin's |
| // UIO_MAXIOV also seems to be 1024. |
| maxVec := 1024 |
| |
| for len(*v) > 0 { |
| iovecs = iovecs[:0] |
| for _, chunk := range *v { |
| if len(chunk) == 0 { |
| continue |
| } |
| iovecs = append(iovecs, syscall.Iovec{Base: &chunk[0]}) |
| iovecs[len(iovecs)-1].SetLen(len(chunk)) |
| if len(iovecs) == maxVec { |
| break |
| } |
| } |
| if len(iovecs) == 0 { |
| break |
| } |
| fd.iovecs = &iovecs // cache |
| |
| wrote, _, e0 := syscall.Syscall(syscall.SYS_WRITEV, |
| uintptr(fd.sysfd), |
| uintptr(unsafe.Pointer(&iovecs[0])), |
| uintptr(len(iovecs))) |
| if wrote < 0 { |
| wrote = 0 |
| } |
| testHookDidWritev(int(wrote)) |
| n += int64(wrote) |
| v.consume(int64(wrote)) |
| if e0 == syscall.EAGAIN { |
| if err = fd.pd.waitWrite(); err == nil { |
| continue |
| } |
| } else if e0 != 0 { |
| err = syscall.Errno(e0) |
| } |
| if err != nil { |
| break |
| } |
| if n == 0 { |
| err = io.ErrUnexpectedEOF |
| break |
| } |
| } |
| if _, ok := err.(syscall.Errno); ok { |
| err = os.NewSyscallError("writev", err) |
| } |
| return n, err |
| } |