Update linux go to 1.15beta1
From https://ci.android.com/builds/submitted/6626886/linux/latest/go.zip
Test: m blueprint_tools
Change-Id: Ib0d1176e769611b25554177aef209bc7e6456694
diff --git a/src/internal/poll/copy_file_range_linux.go b/src/internal/poll/copy_file_range_linux.go
new file mode 100644
index 0000000..604607f
--- /dev/null
+++ b/src/internal/poll/copy_file_range_linux.go
@@ -0,0 +1,99 @@
+// Copyright 2020 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.
+
+package poll
+
+import (
+ "internal/syscall/unix"
+ "sync/atomic"
+ "syscall"
+)
+
+var copyFileRangeSupported int32 = 1 // accessed atomically
+
+const maxCopyFileRangeRound = 1 << 30
+
+// CopyFileRange copies at most remain bytes of data from src to dst, using
+// the copy_file_range system call. dst and src must refer to regular files.
+func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err error) {
+ if atomic.LoadInt32(©FileRangeSupported) == 0 {
+ return 0, false, nil
+ }
+ for remain > 0 {
+ max := remain
+ if max > maxCopyFileRangeRound {
+ max = maxCopyFileRangeRound
+ }
+ n, err := copyFileRange(dst, src, int(max))
+ switch err {
+ case syscall.ENOSYS:
+ // copy_file_range(2) was introduced in Linux 4.5.
+ // Go supports Linux >= 2.6.33, so the system call
+ // may not be present.
+ //
+ // If we see ENOSYS, we have certainly not transfered
+ // any data, so we can tell the caller that we
+ // couldn't handle the transfer and let them fall
+ // back to more generic code.
+ //
+ // Seeing ENOSYS also means that we will not try to
+ // use copy_file_range(2) again.
+ atomic.StoreInt32(©FileRangeSupported, 0)
+ return 0, false, nil
+ case syscall.EXDEV, syscall.EINVAL:
+ // Prior to Linux 5.3, it was not possible to
+ // copy_file_range across file systems. Similarly to
+ // the ENOSYS case above, if we see EXDEV, we have
+ // not transfered any data, and we can let the caller
+ // fall back to generic code.
+ //
+ // As for EINVAL, that is what we see if, for example,
+ // dst or src refer to a pipe rather than a regular
+ // file. This is another case where no data has been
+ // transfered, so we consider it unhandled.
+ return 0, false, nil
+ case nil:
+ if n == 0 {
+ // src is at EOF, which means we are done.
+ return written, true, nil
+ }
+ remain -= n
+ written += n
+ default:
+ return written, true, err
+ }
+ }
+ return written, true, nil
+}
+
+// copyFileRange performs one round of copy_file_range(2).
+func copyFileRange(dst, src *FD, max int) (written int64, err error) {
+ // The signature of copy_file_range(2) is:
+ //
+ // ssize_t copy_file_range(int fd_in, loff_t *off_in,
+ // int fd_out, loff_t *off_out,
+ // size_t len, unsigned int flags);
+ //
+ // Note that in the call to unix.CopyFileRange below, we use nil
+ // values for off_in and off_out. For the system call, this means
+ // "use and update the file offsets". That is why we must acquire
+ // locks for both file descriptors (and why this whole machinery is
+ // in the internal/poll package to begin with).
+ if err := dst.writeLock(); err != nil {
+ return 0, err
+ }
+ defer dst.writeUnlock()
+ if err := src.readLock(); err != nil {
+ return 0, err
+ }
+ defer src.readUnlock()
+ var n int
+ for {
+ n, err = unix.CopyFileRange(src.Sysfd, nil, dst.Sysfd, nil, max, 0)
+ if err != syscall.EINTR {
+ break
+ }
+ }
+ return int64(n), err
+}
diff --git a/src/internal/poll/export_posix_test.go b/src/internal/poll/export_posix_test.go
index 6b9bb8b..abadf50 100644
--- a/src/internal/poll/export_posix_test.go
+++ b/src/internal/poll/export_posix_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
// Export guts for testing on posix.
// Since testing imports os and os imports internal/poll,
diff --git a/src/internal/poll/fcntl_js.go b/src/internal/poll/fcntl_js.go
new file mode 100644
index 0000000..120fc11
--- /dev/null
+++ b/src/internal/poll/fcntl_js.go
@@ -0,0 +1,14 @@
+// Copyright 2019 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 js,wasm
+
+package poll
+
+import "syscall"
+
+// fcntl not supported on js/wasm
+func fcntl(fd int, cmd int, arg int) (int, error) {
+ return 0, syscall.ENOSYS
+}
diff --git a/src/internal/poll/fcntl_libc.go b/src/internal/poll/fcntl_libc.go
new file mode 100644
index 0000000..642472b
--- /dev/null
+++ b/src/internal/poll/fcntl_libc.go
@@ -0,0 +1,13 @@
+// Copyright 2019 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 aix darwin solaris
+
+package poll
+
+import _ "unsafe" // for go:linkname
+
+// Implemented in the syscall package.
+//go:linkname fcntl syscall.fcntl
+func fcntl(fd int, cmd int, arg int) (int, error)
diff --git a/src/internal/poll/fcntl_syscall.go b/src/internal/poll/fcntl_syscall.go
new file mode 100644
index 0000000..5ac8143
--- /dev/null
+++ b/src/internal/poll/fcntl_syscall.go
@@ -0,0 +1,20 @@
+// Copyright 2019 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 dragonfly freebsd linux netbsd openbsd
+
+package poll
+
+import (
+ "internal/syscall/unix"
+ "syscall"
+)
+
+func fcntl(fd int, cmd int, arg int) (int, error) {
+ r, _, e := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), uintptr(cmd), uintptr(arg))
+ if e != 0 {
+ return int(r), syscall.Errno(e)
+ }
+ return int(r), nil
+}
diff --git a/src/internal/poll/fd.go b/src/internal/poll/fd.go
index c0de50c..b72ea3d 100644
--- a/src/internal/poll/fd.go
+++ b/src/internal/poll/fd.go
@@ -35,16 +35,20 @@
return ErrNetClosing
}
-// ErrTimeout is returned for an expired deadline.
-var ErrTimeout error = &TimeoutError{}
+// ErrDeadlineExceeded is returned for an expired deadline.
+// This is exported by the os package as os.ErrDeadlineExceeded.
+var ErrDeadlineExceeded error = &DeadlineExceededError{}
-// TimeoutError is returned for an expired deadline.
-type TimeoutError struct{}
+// DeadlineExceededError is returned for an expired deadline.
+type DeadlineExceededError struct{}
// Implement the net.Error interface.
-func (e *TimeoutError) Error() string { return "i/o timeout" }
-func (e *TimeoutError) Timeout() bool { return true }
-func (e *TimeoutError) Temporary() bool { return true }
+// The string is "i/o timeout" because that is what was returned
+// by earlier Go versions. Changing it may break programs that
+// match on error strings.
+func (e *DeadlineExceededError) Error() string { return "i/o timeout" }
+func (e *DeadlineExceededError) Timeout() bool { return true }
+func (e *DeadlineExceededError) Temporary() bool { return true }
// ErrNotPollable is returned when the file or socket is not suitable
// for event notification.
diff --git a/src/internal/poll/fd_fsync_darwin.go b/src/internal/poll/fd_fsync_darwin.go
index c68ec97..9175149 100644
--- a/src/internal/poll/fd_fsync_darwin.go
+++ b/src/internal/poll/fd_fsync_darwin.go
@@ -4,10 +4,7 @@
package poll
-import (
- "syscall"
- _ "unsafe" // for go:linkname
-)
+import "syscall"
// Fsync invokes SYS_FCNTL with SYS_FULLFSYNC because
// on OS X, SYS_FSYNC doesn't fully flush contents to disk.
@@ -21,7 +18,3 @@
_, e1 := fcntl(fd.Sysfd, syscall.F_FULLFSYNC, 0)
return e1
}
-
-// Implemented in syscall/syscall_darwin.go.
-//go:linkname fcntl syscall.fcntl
-func fcntl(fd int, cmd int, arg int) (int, error)
diff --git a/src/internal/poll/fd_fsync_posix.go b/src/internal/poll/fd_fsync_posix.go
index 6705a3e..6935829 100644
--- a/src/internal/poll/fd_fsync_posix.go
+++ b/src/internal/poll/fd_fsync_posix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
+// +build aix dragonfly freebsd js,wasm linux netbsd openbsd solaris
package poll
@@ -16,11 +16,3 @@
defer fd.decref()
return syscall.Fsync(fd.Sysfd)
}
-
-func fcntl(fd int, cmd int, arg int) (int, error) {
- r, _, e := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
- if e != 0 {
- return int(r), syscall.Errno(e)
- }
- return int(r), nil
-}
diff --git a/src/internal/poll/fd_mutex_test.go b/src/internal/poll/fd_mutex_test.go
index 2c53c45..3029b9a 100644
--- a/src/internal/poll/fd_mutex_test.go
+++ b/src/internal/poll/fd_mutex_test.go
@@ -59,7 +59,7 @@
}
func TestMutexCloseUnblock(t *testing.T) {
- c := make(chan bool)
+ c := make(chan bool, 4)
var mu FDMutex
mu.RWLock(true)
for i := 0; i < 4; i++ {
@@ -151,12 +151,15 @@
N = 1e4
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
- done := make(chan bool)
+ done := make(chan bool, P)
var mu FDMutex
var readState [2]uint64
var writeState [2]uint64
for p := 0; p < P; p++ {
go func() {
+ defer func() {
+ done <- !t.Failed()
+ }()
r := rand.New(rand.NewSource(rand.Int63()))
for i := 0; i < N; i++ {
switch r.Intn(3) {
@@ -203,11 +206,12 @@
}
}
}
- done <- true
}()
}
for p := 0; p < P; p++ {
- <-done
+ if !<-done {
+ t.FailNow()
+ }
}
if !mu.IncrefAndClose() {
t.Fatal("broken")
diff --git a/src/internal/poll/fd_plan9.go b/src/internal/poll/fd_plan9.go
index 0fce329..0b5b937 100644
--- a/src/internal/poll/fd_plan9.go
+++ b/src/internal/poll/fd_plan9.go
@@ -7,6 +7,7 @@
import (
"errors"
"io"
+ "sync"
"sync/atomic"
"time"
)
@@ -24,6 +25,8 @@
Destroy func()
// deadlines
+ rmu sync.Mutex
+ wmu sync.Mutex
raio *asyncIO
waio *asyncIO
rtimer *time.Timer
@@ -59,9 +62,6 @@
// Read implements io.Reader.
func (fd *FD) Read(fn func([]byte) (int, error), b []byte) (int, error) {
- if fd.rtimedout.isSet() {
- return 0, ErrTimeout
- }
if err := fd.readLock(); err != nil {
return 0, err
}
@@ -69,32 +69,41 @@
if len(b) == 0 {
return 0, nil
}
+ fd.rmu.Lock()
+ if fd.rtimedout.isSet() {
+ fd.rmu.Unlock()
+ return 0, ErrDeadlineExceeded
+ }
fd.raio = newAsyncIO(fn, b)
+ fd.rmu.Unlock()
n, err := fd.raio.Wait()
fd.raio = nil
if isHangup(err) {
err = io.EOF
}
if isInterrupted(err) {
- err = ErrTimeout
+ err = ErrDeadlineExceeded
}
return n, err
}
// Write implements io.Writer.
func (fd *FD) Write(fn func([]byte) (int, error), b []byte) (int, error) {
- if fd.wtimedout.isSet() {
- return 0, ErrTimeout
- }
if err := fd.writeLock(); err != nil {
return 0, err
}
defer fd.writeUnlock()
+ fd.wmu.Lock()
+ if fd.wtimedout.isSet() {
+ fd.wmu.Unlock()
+ return 0, ErrDeadlineExceeded
+ }
fd.waio = newAsyncIO(fn, b)
+ fd.wmu.Unlock()
n, err := fd.waio.Wait()
fd.waio = nil
if isInterrupted(err) {
- err = ErrTimeout
+ err = ErrDeadlineExceeded
}
return n, err
}
@@ -117,9 +126,13 @@
func setDeadlineImpl(fd *FD, t time.Time, mode int) error {
d := t.Sub(time.Now())
if mode == 'r' || mode == 'r'+'w' {
+ fd.rmu.Lock()
+ defer fd.rmu.Unlock()
fd.rtimedout.setFalse()
}
if mode == 'w' || mode == 'r'+'w' {
+ fd.wmu.Lock()
+ defer fd.wmu.Unlock()
fd.wtimedout.setFalse()
}
if t.IsZero() || d < 0 {
@@ -140,18 +153,22 @@
// Interrupt I/O operation once timer has expired
if mode == 'r' || mode == 'r'+'w' {
fd.rtimer = time.AfterFunc(d, func() {
+ fd.rmu.Lock()
fd.rtimedout.setTrue()
if fd.raio != nil {
fd.raio.Cancel()
}
+ fd.rmu.Unlock()
})
}
if mode == 'w' || mode == 'r'+'w' {
fd.wtimer = time.AfterFunc(d, func() {
+ fd.wmu.Lock()
fd.wtimedout.setTrue()
if fd.waio != nil {
fd.waio.Cancel()
}
+ fd.wmu.Unlock()
})
}
}
diff --git a/src/internal/poll/fd_poll_nacljs.go b/src/internal/poll/fd_poll_js.go
similarity index 97%
rename from src/internal/poll/fd_poll_nacljs.go
rename to src/internal/poll/fd_poll_js.go
index 0871f34..d6b28e5 100644
--- a/src/internal/poll/fd_poll_nacljs.go
+++ b/src/internal/poll/fd_poll_js.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build nacl js,wasm
+// +build js,wasm
package poll
@@ -45,7 +45,7 @@
if isFile { // TODO(neelance): wasm: Use callbacks from JS to block until the read/write finished.
return nil
}
- return ErrTimeout
+ return ErrDeadlineExceeded
}
func (pd *pollDesc) waitRead(isFile bool) error { return pd.wait('r', isFile) }
diff --git a/src/internal/poll/fd_poll_runtime.go b/src/internal/poll/fd_poll_runtime.go
index d32f4a0..222e5c6 100644
--- a/src/internal/poll/fd_poll_runtime.go
+++ b/src/internal/poll/fd_poll_runtime.go
@@ -107,15 +107,24 @@
return pd.runtimeCtx != 0
}
+// Error values returned by runtime_pollReset and runtime_pollWait.
+// These must match the values in runtime/netpoll.go.
+const (
+ pollNoError = 0
+ pollErrClosing = 1
+ pollErrTimeout = 2
+ pollErrNotPollable = 3
+)
+
func convertErr(res int, isFile bool) error {
switch res {
- case 0:
+ case pollNoError:
return nil
- case 1:
+ case pollErrClosing:
return errClosing(isFile)
- case 2:
- return ErrTimeout
- case 3:
+ case pollErrTimeout:
+ return ErrDeadlineExceeded
+ case pollErrNotPollable:
return ErrNotPollable
}
println("unreachable: ", res)
diff --git a/src/internal/poll/fd_posix.go b/src/internal/poll/fd_posix.go
index b43ad51..54747b4 100644
--- a/src/internal/poll/fd_posix.go
+++ b/src/internal/poll/fd_posix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows
package poll
@@ -20,6 +20,15 @@
return err
}
+// Shutdown wraps syscall.Shutdown.
+func (fd *FD) Shutdown(how int) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+ return syscall.Shutdown(fd.Sysfd, how)
+}
+
// Fchmod wraps syscall.Fchmod.
func (fd *FD) Fchmod(mode uint32) error {
if err := fd.incref(); err != nil {
@@ -46,3 +55,14 @@
defer fd.decref()
return syscall.Ftruncate(fd.Sysfd, size)
}
+
+// RawControl invokes the user-defined function f for a non-IO
+// operation.
+func (fd *FD) RawControl(f func(uintptr)) error {
+ if err := fd.incref(); err != nil {
+ return err
+ }
+ defer fd.decref()
+ f(uintptr(fd.Sysfd))
+ return nil
+}
diff --git a/src/internal/poll/fd_posix_test.go b/src/internal/poll/fd_posix_test.go
index 246d498..4449eb3 100644
--- a/src/internal/poll/fd_posix_test.go
+++ b/src/internal/poll/fd_posix_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
package poll_test
diff --git a/src/internal/poll/fd_unix.go b/src/internal/poll/fd_unix.go
index 8185269..4872fa9 100644
--- a/src/internal/poll/fd_unix.go
+++ b/src/internal/poll/fd_unix.go
@@ -2,13 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris
package poll
import (
"io"
- "runtime"
"sync/atomic"
"syscall"
)
@@ -112,15 +111,6 @@
return err
}
-// Shutdown wraps the shutdown network call.
-func (fd *FD) Shutdown(how int) error {
- if err := fd.incref(); err != nil {
- return err
- }
- defer fd.decref()
- return syscall.Shutdown(fd.Sysfd, how)
-}
-
// SetBlocking puts the file into blocking mode.
func (fd *FD) SetBlocking() error {
if err := fd.incref(); err != nil {
@@ -162,7 +152,7 @@
p = p[:maxRW]
}
for {
- n, err := syscall.Read(fd.Sysfd, p)
+ n, err := ignoringEINTR(syscall.Read, fd.Sysfd, p)
if err != nil {
n = 0
if err == syscall.EAGAIN && fd.pd.pollable() {
@@ -170,12 +160,6 @@
continue
}
}
-
- // On MacOS we can see EINTR here if the user
- // pressed ^Z. See issue #22838.
- if runtime.GOOS == "darwin" && err == syscall.EINTR {
- continue
- }
}
err = fd.eofError(n, err)
return n, err
@@ -193,7 +177,16 @@
if fd.IsStream && len(p) > maxRW {
p = p[:maxRW]
}
- n, err := syscall.Pread(fd.Sysfd, p, off)
+ var (
+ n int
+ err error
+ )
+ for {
+ n, err = syscall.Pread(fd.Sysfd, p, off)
+ if err != syscall.EINTR {
+ break
+ }
+ }
if err != nil {
n = 0
}
@@ -214,6 +207,9 @@
for {
n, sa, err := syscall.Recvfrom(fd.Sysfd, p, 0)
if err != nil {
+ if err == syscall.EINTR {
+ continue
+ }
n = 0
if err == syscall.EAGAIN && fd.pd.pollable() {
if err = fd.pd.waitRead(fd.isFile); err == nil {
@@ -238,6 +234,9 @@
for {
n, oobn, flags, sa, err := syscall.Recvmsg(fd.Sysfd, p, oob, 0)
if err != nil {
+ if err == syscall.EINTR {
+ continue
+ }
// TODO(dfc) should n and oobn be set to 0
if err == syscall.EAGAIN && fd.pd.pollable() {
if err = fd.pd.waitRead(fd.isFile); err == nil {
@@ -265,7 +264,7 @@
if fd.IsStream && max-nn > maxRW {
max = nn + maxRW
}
- n, err := syscall.Write(fd.Sysfd, p[nn:max])
+ n, err := ignoringEINTR(syscall.Write, fd.Sysfd, p[nn:max])
if n > 0 {
nn += n
}
@@ -302,6 +301,9 @@
max = nn + maxRW
}
n, err := syscall.Pwrite(fd.Sysfd, p[nn:max], off+int64(nn))
+ if err == syscall.EINTR {
+ continue
+ }
if n > 0 {
nn += n
}
@@ -328,6 +330,9 @@
}
for {
err := syscall.Sendto(fd.Sysfd, p, 0, sa)
+ if err == syscall.EINTR {
+ continue
+ }
if err == syscall.EAGAIN && fd.pd.pollable() {
if err = fd.pd.waitWrite(fd.isFile); err == nil {
continue
@@ -351,6 +356,9 @@
}
for {
n, err := syscall.SendmsgN(fd.Sysfd, p, oob, sa, 0)
+ if err == syscall.EINTR {
+ continue
+ }
if err == syscall.EAGAIN && fd.pd.pollable() {
if err = fd.pd.waitWrite(fd.isFile); err == nil {
continue
@@ -379,6 +387,8 @@
return s, rsa, "", err
}
switch err {
+ case syscall.EINTR:
+ continue
case syscall.EAGAIN:
if fd.pd.pollable() {
if err = fd.pd.waitRead(fd.isFile); err == nil {
@@ -413,7 +423,7 @@
}
defer fd.decref()
for {
- n, err := syscall.ReadDirent(fd.Sysfd, buf)
+ n, err := ignoringEINTR(syscall.ReadDirent, fd.Sysfd, buf)
if err != nil {
n = 0
if err == syscall.EAGAIN && fd.pd.pollable() {
@@ -451,7 +461,7 @@
// DupCloseOnExec dups fd and marks it close-on-exec.
func DupCloseOnExec(fd int) (int, string, error) {
- if atomic.LoadInt32(&tryDupCloexec) == 1 {
+ if syscall.F_DUPFD_CLOEXEC != 0 && atomic.LoadInt32(&tryDupCloexec) == 1 {
r0, e1 := fcntl(fd, syscall.F_DUPFD_CLOEXEC, 0)
if e1 == nil {
return r0, "", nil
@@ -469,7 +479,7 @@
return dupCloseOnExecOld(fd)
}
-// dupCloseOnExecUnixOld is the traditional way to dup an fd and
+// dupCloseOnExecOld is the traditional way to dup an fd and
// set its O_CLOEXEC bit, using two system calls.
func dupCloseOnExecOld(fd int) (int, string, error) {
syscall.ForkLock.RLock()
@@ -504,18 +514,7 @@
return 0, err
}
defer fd.writeUnlock()
- return syscall.Write(fd.Sysfd, p)
-}
-
-// RawControl invokes the user-defined function f for a non-IO
-// operation.
-func (fd *FD) RawControl(f func(uintptr)) error {
- if err := fd.incref(); err != nil {
- return err
- }
- defer fd.decref()
- f(uintptr(fd.Sysfd))
- return nil
+ return ignoringEINTR(syscall.Write, fd.Sysfd, p)
}
// RawRead invokes the user-defined function f for a read operation.
@@ -555,3 +554,19 @@
}
}
}
+
+// ignoringEINTR makes a function call and repeats it if it returns
+// an EINTR error. This appears to be required even though we install
+// all signal handlers with SA_RESTART: see #22838, #38033, #38836.
+// Also #20400 and #36644 are issues in which a signal handler is
+// installed without setting SA_RESTART. None of these are the common case,
+// but there are enough of them that it seems that we can't avoid
+// an EINTR loop.
+func ignoringEINTR(fn func(fd int, p []byte) (int, error), fd int, p []byte) (int, error) {
+ for {
+ n, err := fn(fd, p)
+ if err != syscall.EINTR {
+ return n, err
+ }
+ }
+}
diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go
index f96e441..e1ef619 100644
--- a/src/internal/poll/fd_windows.go
+++ b/src/internal/poll/fd_windows.go
@@ -9,7 +9,6 @@
"internal/race"
"internal/syscall/windows"
"io"
- "runtime"
"sync"
"syscall"
"unicode/utf16"
@@ -22,18 +21,6 @@
ioSync uint64
)
-// CancelIo Windows API cancels all outstanding IO for a particular
-// socket on current thread. To overcome that limitation, we run
-// special goroutine, locked to OS single thread, that both starts
-// and cancels IO. It means, there are 2 unavoidable thread switches
-// for every IO.
-// Some newer versions of Windows has new CancelIoEx API, that does
-// not have that limitation and can be used from any thread. This
-// package uses CancelIoEx API, if present, otherwise it fallback
-// to CancelIo.
-
-var canCancelIO bool // determines if CancelIoEx API is present
-
// This package uses the SetFileCompletionNotificationModes Windows
// API to skip calling GetQueuedCompletionStatus if an IO operation
// completes synchronously. There is a known bug where
@@ -72,7 +59,6 @@
if e != nil {
initErr = e
}
- canCancelIO = syscall.LoadCancelIoEx() == nil
checkSetFileCompletionNotificationModes()
}
@@ -90,7 +76,6 @@
// fields used only by net package
fd *FD
- errc chan error
buf syscall.WSABuf
msg windows.WSAMsg
sa syscall.Sockaddr
@@ -155,46 +140,15 @@
}
}
-// ioSrv executes net IO requests.
-type ioSrv struct {
- req chan ioSrvReq
-}
-
-type ioSrvReq struct {
- o *operation
- submit func(o *operation) error // if nil, cancel the operation
-}
-
-// ProcessRemoteIO will execute submit IO requests on behalf
-// of other goroutines, all on a single os thread, so it can
-// cancel them later. Results of all operations will be sent
-// back to their requesters via channel supplied in request.
-// It is used only when the CancelIoEx API is unavailable.
-func (s *ioSrv) ProcessRemoteIO() {
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- for r := range s.req {
- if r.submit != nil {
- r.o.errc <- r.submit(r.o)
- } else {
- r.o.errc <- syscall.CancelIo(r.o.fd.Sysfd)
- }
- }
-}
-
-// ExecIO executes a single IO operation o. It submits and cancels
+// execIO executes a single IO operation o. It submits and cancels
// IO in the current thread for systems where Windows CancelIoEx API
// is available. Alternatively, it passes the request onto
// runtime netpoll and waits for completion or cancels request.
-func (s *ioSrv) ExecIO(o *operation, submit func(o *operation) error) (int, error) {
+func execIO(o *operation, submit func(o *operation) error) (int, error) {
if o.fd.pd.runtimeCtx == 0 {
return 0, errors.New("internal error: polling on unsupported descriptor type")
}
- if !canCancelIO {
- onceStartServer.Do(startServer)
- }
-
fd := o.fd
// Notify runtime netpoll about starting IO.
err := fd.pd.prepare(int(o.mode), fd.isFile)
@@ -202,14 +156,7 @@
return 0, err
}
// Start IO.
- if canCancelIO {
- err = submit(o)
- } else {
- // Send request to a special dedicated thread,
- // so it can stop the IO with CancelIO later.
- s.req <- ioSrvReq{o, submit}
- err = <-o.errc
- }
+ err = submit(o)
switch err {
case nil:
// IO completed immediately
@@ -241,22 +188,17 @@
// IO is interrupted by "close" or "timeout"
netpollErr := err
switch netpollErr {
- case ErrNetClosing, ErrFileClosing, ErrTimeout:
+ case ErrNetClosing, ErrFileClosing, ErrDeadlineExceeded:
// will deal with those.
default:
panic("unexpected runtime.netpoll error: " + netpollErr.Error())
}
// Cancel our request.
- if canCancelIO {
- err := syscall.CancelIoEx(fd.Sysfd, &o.o)
- // Assuming ERROR_NOT_FOUND is returned, if IO is completed.
- if err != nil && err != syscall.ERROR_NOT_FOUND {
- // TODO(brainman): maybe do something else, but panic.
- panic(err)
- }
- } else {
- s.req <- ioSrvReq{o, nil}
- <-o.errc
+ err = syscall.CancelIoEx(fd.Sysfd, &o.o)
+ // Assuming ERROR_NOT_FOUND is returned, if IO is completed.
+ if err != nil && err != syscall.ERROR_NOT_FOUND {
+ // TODO(brainman): maybe do something else, but panic.
+ panic(err)
}
// Wait for cancellation to complete.
fd.pd.waitCanceled(int(o.mode))
@@ -273,21 +215,6 @@
return int(o.qty), nil
}
-// Start helper goroutines.
-var rsrv, wsrv ioSrv
-var onceStartServer sync.Once
-
-func startServer() {
- // This is called, once, when only the CancelIo API is available.
- // Start two special goroutines, both locked to an OS thread,
- // that start and cancel IO requests.
- // One will process read requests, while the other will do writes.
- rsrv.req = make(chan ioSrvReq)
- go rsrv.ProcessRemoteIO()
- wsrv.req = make(chan ioSrvReq)
- go wsrv.ProcessRemoteIO()
-}
-
// FD is a file descriptor. The net and os packages embed this type in
// a larger type representing a network connection or OS file.
type FD struct {
@@ -385,9 +312,9 @@
// if the user is doing their own overlapped I/O.
// See issue #21172.
//
- // In general the code below avoids calling the ExecIO
- // method for non-network sockets. If some method does
- // somehow call ExecIO, then ExecIO, and therefore the
+ // In general the code below avoids calling the execIO
+ // function for non-network sockets. If some method does
+ // somehow call execIO, then execIO, and therefore the
// calling method, will return an error, because
// fd.pd.runtimeCtx will be 0.
err = fd.pd.init(fd)
@@ -402,7 +329,7 @@
// We do not use events, so we can skip them always.
flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE)
// It's not safe to skip completion notifications for UDP:
- // https://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx
+ // https://docs.microsoft.com/en-us/archive/blogs/winserverperformance/designing-applications-for-high-performance-part-iii
if net == "tcp" {
flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
}
@@ -429,10 +356,6 @@
fd.wop.fd = fd
fd.rop.runtimeCtx = fd.pd.runtimeCtx
fd.wop.runtimeCtx = fd.pd.runtimeCtx
- if !canCancelIO {
- fd.rop.errc = make(chan error)
- fd.wop.errc = make(chan error)
- }
return "", nil
}
@@ -476,15 +399,6 @@
return err
}
-// Shutdown wraps the shutdown network call.
-func (fd *FD) Shutdown(how int) error {
- if err := fd.incref(); err != nil {
- return err
- }
- defer fd.decref()
- return syscall.Shutdown(fd.Sysfd, how)
-}
-
// Windows ReadFile and WSARecv use DWORD (uint32) parameter to pass buffer length.
// This prevents us reading blocks larger than 4GB.
// See golang.org/issue/26923.
@@ -524,7 +438,7 @@
} else {
o := &fd.rop
o.InitBuf(buf)
- n, err = rsrv.ExecIO(o, func(o *operation) error {
+ n, err = execIO(o, func(o *operation) error {
return syscall.WSARecv(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil)
})
if race.Enabled {
@@ -664,7 +578,7 @@
defer fd.readUnlock()
o := &fd.rop
o.InitBuf(buf)
- n, err := rsrv.ExecIO(o, func(o *operation) error {
+ n, err := execIO(o, func(o *operation) error {
if o.rsa == nil {
o.rsa = new(syscall.RawSockaddrAny)
}
@@ -720,7 +634,7 @@
}
o := &fd.wop
o.InitBuf(b)
- n, err = wsrv.ExecIO(o, func(o *operation) error {
+ n, err = execIO(o, func(o *operation) error {
return syscall.WSASend(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil)
})
}
@@ -829,7 +743,7 @@
}
o := &fd.wop
o.InitBufs(buf)
- n, err := wsrv.ExecIO(o, func(o *operation) error {
+ n, err := execIO(o, func(o *operation) error {
return syscall.WSASend(o.fd.Sysfd, &o.bufs[0], uint32(len(o.bufs)), &o.qty, 0, &o.o, nil)
})
o.ClearBufs()
@@ -850,7 +764,7 @@
o := &fd.wop
o.InitBuf(buf)
o.sa = sa
- n, err := wsrv.ExecIO(o, func(o *operation) error {
+ n, err := execIO(o, func(o *operation) error {
return syscall.WSASendto(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil)
})
return n, err
@@ -865,7 +779,7 @@
o := &fd.wop
o.InitBuf(b)
o.sa = sa
- n, err := wsrv.ExecIO(o, func(o *operation) error {
+ n, err := execIO(o, func(o *operation) error {
return syscall.WSASendto(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil)
})
ntotal += int(n)
@@ -883,7 +797,7 @@
func (fd *FD) ConnectEx(ra syscall.Sockaddr) error {
o := &fd.wop
o.sa = ra
- _, err := wsrv.ExecIO(o, func(o *operation) error {
+ _, err := execIO(o, func(o *operation) error {
return ConnectExFunc(o.fd.Sysfd, o.sa, nil, 0, nil, &o.o)
})
return err
@@ -893,7 +807,7 @@
// Submit accept request.
o.handle = s
o.rsan = int32(unsafe.Sizeof(rawsa[0]))
- _, err := rsrv.ExecIO(o, func(o *operation) error {
+ _, err := execIO(o, func(o *operation) error {
return AcceptFunc(o.fd.Sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o)
})
if err != nil {
@@ -999,17 +913,6 @@
return syscall.GetFileInformationByHandle(fd.Sysfd, data)
}
-// RawControl invokes the user-defined function f for a non-IO
-// operation.
-func (fd *FD) RawControl(f func(uintptr)) error {
- if err := fd.incref(); err != nil {
- return err
- }
- defer fd.decref()
- f(uintptr(fd.Sysfd))
- return nil
-}
-
// RawRead invokes the user-defined function f for a read operation.
func (fd *FD) RawRead(f func(uintptr) bool) error {
if err := fd.readLock(); err != nil {
@@ -1028,7 +931,7 @@
if !fd.IsStream {
o.flags |= windows.MSG_PEEK
}
- _, err := rsrv.ExecIO(o, func(o *operation) error {
+ _, err := execIO(o, func(o *operation) error {
return syscall.WSARecv(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil)
})
if err == windows.WSAEMSGSIZE {
@@ -1096,9 +999,9 @@
o := &fd.rop
o.InitMsg(p, oob)
o.rsa = new(syscall.RawSockaddrAny)
- o.msg.Name = o.rsa
+ o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa))
o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa))
- n, err := rsrv.ExecIO(o, func(o *operation) error {
+ n, err := execIO(o, func(o *operation) error {
return windows.WSARecvMsg(o.fd.Sysfd, &o.msg, &o.qty, &o.o, nil)
})
err = fd.eofError(n, err)
@@ -1127,10 +1030,10 @@
if err != nil {
return 0, 0, err
}
- o.msg.Name = (*syscall.RawSockaddrAny)(rsa)
+ o.msg.Name = (syscall.Pointer)(rsa)
o.msg.Namelen = len
}
- n, err := wsrv.ExecIO(o, func(o *operation) error {
+ n, err := execIO(o, func(o *operation) error {
return windows.WSASendMsg(o.fd.Sysfd, &o.msg, 0, &o.qty, &o.o, nil)
})
return n, int(o.msg.Control.Len), err
diff --git a/src/internal/poll/fd_writev_unix.go b/src/internal/poll/fd_writev_unix.go
index 86af795..daeec96 100644
--- a/src/internal/poll/fd_writev_unix.go
+++ b/src/internal/poll/fd_writev_unix.go
@@ -12,9 +12,18 @@
)
func writev(fd int, iovecs []syscall.Iovec) (uintptr, error) {
- r, _, e := syscall.Syscall(syscall.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs)))
+ var (
+ r uintptr
+ e syscall.Errno
+ )
+ for {
+ r, _, e = syscall.Syscall(syscall.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs)))
+ if e != syscall.EINTR {
+ break
+ }
+ }
if e != 0 {
- return r, syscall.Errno(e)
+ return r, e
}
return r, nil
}
diff --git a/src/internal/poll/hook_unix.go b/src/internal/poll/hook_unix.go
index a7512b1..11f90e9 100644
--- a/src/internal/poll/hook_unix.go
+++ b/src/internal/poll/hook_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris
package poll
diff --git a/src/internal/poll/sendfile_bsd.go b/src/internal/poll/sendfile_bsd.go
index 40ae346..a24e41d 100644
--- a/src/internal/poll/sendfile_bsd.go
+++ b/src/internal/poll/sendfile_bsd.go
@@ -35,6 +35,9 @@
} else if n == 0 && err1 == nil {
break
}
+ if err1 == syscall.EINTR {
+ continue
+ }
if err1 == syscall.EAGAIN {
if err1 = dstFD.pd.waitWrite(dstFD.isFile); err1 == nil {
continue
diff --git a/src/internal/poll/sendfile_linux.go b/src/internal/poll/sendfile_linux.go
index 8e93806..d642830 100644
--- a/src/internal/poll/sendfile_linux.go
+++ b/src/internal/poll/sendfile_linux.go
@@ -32,6 +32,9 @@
} else if n == 0 && err1 == nil {
break
}
+ if err1 == syscall.EINTR {
+ continue
+ }
if err1 == syscall.EAGAIN {
if err1 = dstFD.pd.waitWrite(dstFD.isFile); err1 == nil {
continue
diff --git a/src/internal/poll/sendfile_windows.go b/src/internal/poll/sendfile_windows.go
index 0fe9b9b..50c3ee8 100644
--- a/src/internal/poll/sendfile_windows.go
+++ b/src/internal/poll/sendfile_windows.go
@@ -4,10 +4,13 @@
package poll
-import "syscall"
+import (
+ "io"
+ "syscall"
+)
// SendFile wraps the TransmitFile call.
-func SendFile(fd *FD, src syscall.Handle, n int64) (int64, error) {
+func SendFile(fd *FD, src syscall.Handle, n int64) (written int64, err error) {
if fd.kind == kindPipe {
// TransmitFile does not work with pipes
return 0, syscall.ESPIPE
@@ -19,26 +22,60 @@
defer fd.writeUnlock()
o := &fd.wop
- o.qty = uint32(n)
o.handle = src
// TODO(brainman): skip calling syscall.Seek if OS allows it
- curpos, err := syscall.Seek(o.handle, 0, 1)
+ curpos, err := syscall.Seek(o.handle, 0, io.SeekCurrent)
if err != nil {
return 0, err
}
- o.o.Offset = uint32(curpos)
- o.o.OffsetHigh = uint32(curpos >> 32)
+ if n <= 0 { // We don't know the size of the file so infer it.
+ // Find the number of bytes offset from curpos until the end of the file.
+ n, err = syscall.Seek(o.handle, -curpos, io.SeekEnd)
+ if err != nil {
+ return
+ }
+ // Now seek back to the original position.
+ if _, err = syscall.Seek(o.handle, curpos, io.SeekStart); err != nil {
+ return
+ }
+ }
- done, err := wsrv.ExecIO(o, func(o *operation) error {
- return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
- })
- if err == nil {
+ // TransmitFile can be invoked in one call with at most
+ // 2,147,483,646 bytes: the maximum value for a 32-bit integer minus 1.
+ // See https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
+ const maxChunkSizePerCall = int64(0x7fffffff - 1)
+
+ for n > 0 {
+ chunkSize := maxChunkSizePerCall
+ if chunkSize > n {
+ chunkSize = n
+ }
+
+ o.qty = uint32(chunkSize)
+ o.o.Offset = uint32(curpos)
+ o.o.OffsetHigh = uint32(curpos >> 32)
+
+ nw, err := execIO(o, func(o *operation) error {
+ return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
+ })
+ if err != nil {
+ return written, err
+ }
+
+ curpos += int64(nw)
+
// Some versions of Windows (Windows 10 1803) do not set
// file position after TransmitFile completes.
// So just use Seek to set file position.
- _, err = syscall.Seek(o.handle, curpos+int64(done), 0)
+ if _, err = syscall.Seek(o.handle, curpos, io.SeekStart); err != nil {
+ return written, err
+ }
+
+ n -= int64(nw)
+ written += int64(nw)
}
- return int64(done), err
+
+ return
}
diff --git a/src/internal/poll/splice_linux.go b/src/internal/poll/splice_linux.go
index 4f97298..01baf14 100644
--- a/src/internal/poll/splice_linux.go
+++ b/src/internal/poll/splice_linux.go
@@ -5,6 +5,7 @@
package poll
import (
+ "internal/syscall/unix"
"sync/atomic"
"syscall"
"unsafe"
@@ -86,6 +87,9 @@
}
for {
n, err := splice(pipefd, sock.Sysfd, max, spliceNonblock)
+ if err == syscall.EINTR {
+ continue
+ }
if err != syscall.EAGAIN {
return n, err
}
@@ -169,7 +173,7 @@
defer atomic.StorePointer(&disableSplice, unsafe.Pointer(p))
// F_GETPIPE_SZ was added in 2.6.35, which does not have the -EAGAIN bug.
- if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fds[0]), syscall.F_GETPIPE_SZ, 0); errno != 0 {
+ if _, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fds[0]), syscall.F_GETPIPE_SZ, 0); errno != 0 {
*p = true
destroyTempPipe(fds[0], fds[1])
return -1, -1, "fcntl", errno
diff --git a/src/internal/poll/sys_cloexec.go b/src/internal/poll/sys_cloexec.go
index 64e4612..7b87f13 100644
--- a/src/internal/poll/sys_cloexec.go
+++ b/src/internal/poll/sys_cloexec.go
@@ -5,7 +5,7 @@
// This file implements sysSocket and accept for platforms that do not
// provide a fast path for setting SetNonblock and CloseOnExec.
-// +build aix darwin js,wasm nacl solaris
+// +build aix darwin js,wasm solaris
package poll
diff --git a/src/internal/poll/writev.go b/src/internal/poll/writev.go
index 6050d1f..305e2fd 100644
--- a/src/internal/poll/writev.go
+++ b/src/internal/poll/writev.go
@@ -68,7 +68,10 @@
iovecs[i] = syscall.Iovec{}
}
if err != nil {
- if err.(syscall.Errno) == syscall.EAGAIN {
+ if err == syscall.EINTR {
+ continue
+ }
+ if err == syscall.EAGAIN {
if err = fd.pd.waitWrite(fd.isFile); err == nil {
continue
}