| // 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. | |
| //go:build unix | |
| package poll | |
| import ( | |
| "io" | |
| "runtime" | |
| "syscall" | |
| ) | |
| // Writev wraps the writev system call. | |
| func (fd *FD) Writev(v *[][]byte) (int64, error) { | |
| if err := fd.writeLock(); err != nil { | |
| return 0, err | |
| } | |
| defer fd.writeUnlock() | |
| if err := fd.pd.prepareWrite(fd.isFile); 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 | |
| if runtime.GOOS == "aix" || runtime.GOOS == "solaris" { | |
| // IOV_MAX is set to XOPEN_IOV_MAX on AIX and Solaris. | |
| maxVec = 16 | |
| } | |
| var n int64 | |
| var err error | |
| for len(*v) > 0 { | |
| iovecs = iovecs[:0] | |
| for _, chunk := range *v { | |
| if len(chunk) == 0 { | |
| continue | |
| } | |
| iovecs = append(iovecs, newIovecWithBase(&chunk[0])) | |
| if fd.IsStream && len(chunk) > 1<<30 { | |
| iovecs[len(iovecs)-1].SetLen(1 << 30) | |
| break // continue chunk on next writev | |
| } | |
| iovecs[len(iovecs)-1].SetLen(len(chunk)) | |
| if len(iovecs) == maxVec { | |
| break | |
| } | |
| } | |
| if len(iovecs) == 0 { | |
| break | |
| } | |
| if fd.iovecs == nil { | |
| fd.iovecs = new([]syscall.Iovec) | |
| } | |
| *fd.iovecs = iovecs // cache | |
| var wrote uintptr | |
| wrote, err = writev(fd.Sysfd, iovecs) | |
| if wrote == ^uintptr(0) { | |
| wrote = 0 | |
| } | |
| TestHookDidWritev(int(wrote)) | |
| n += int64(wrote) | |
| consume(v, int64(wrote)) | |
| clear(iovecs) | |
| if err != nil { | |
| if err == syscall.EINTR { | |
| continue | |
| } | |
| if err == syscall.EAGAIN { | |
| if err = fd.pd.waitWrite(fd.isFile); err == nil { | |
| continue | |
| } | |
| } | |
| break | |
| } | |
| if n == 0 { | |
| err = io.ErrUnexpectedEOF | |
| break | |
| } | |
| } | |
| return n, err | |
| } |