aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2020-07-27 22:27:54 -0700
committerIan Lance Taylor <iant@golang.org>2020-08-01 11:21:40 -0700
commitf75af8c1464e948b5e166cf5ab09ebf0d82fc253 (patch)
tree3ba3299859b504bdeb477727471216bd094a0191 /libgo/go/os
parent75a23e59031fe673fc3b2e60fd1fe5f4c70ecb85 (diff)
downloadgcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.zip
gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.tar.gz
gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.tar.bz2
libgo: update to go1.15rc1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/245157
Diffstat (limited to 'libgo/go/os')
-rw-r--r--libgo/go/os/error.go20
-rw-r--r--libgo/go/os/exec/exec_linux_test.go45
-rw-r--r--libgo/go/os/exec/exec_test.go106
-rw-r--r--libgo/go/os/exec/read3.go99
-rw-r--r--libgo/go/os/exec_unix.go15
-rw-r--r--libgo/go/os/exec_windows.go2
-rw-r--r--libgo/go/os/export_linux_test.go7
-rw-r--r--libgo/go/os/file.go34
-rw-r--r--libgo/go/os/file_plan9.go16
-rw-r--r--libgo/go/os/file_posix.go45
-rw-r--r--libgo/go/os/file_unix.go75
-rw-r--r--libgo/go/os/issue37161/a1
-rw-r--r--libgo/go/os/issue37161/b1
-rw-r--r--libgo/go/os/issue37161/c1
-rw-r--r--libgo/go/os/os_test.go111
-rw-r--r--libgo/go/os/os_unix_test.go2
-rw-r--r--libgo/go/os/os_windows_test.go119
-rw-r--r--libgo/go/os/path_test.go4
-rw-r--r--libgo/go/os/pipe_test.go19
-rw-r--r--libgo/go/os/readfrom_linux.go46
-rw-r--r--libgo/go/os/readfrom_linux_test.go363
-rw-r--r--libgo/go/os/readfrom_stub.go13
-rw-r--r--libgo/go/os/removeall_at.go3
-rw-r--r--libgo/go/os/signal/internal/pty/pty.go8
-rw-r--r--libgo/go/os/signal/signal.go14
-rw-r--r--libgo/go/os/signal/signal_cgo_test.go46
-rw-r--r--libgo/go/os/signal/signal_plan9.go4
-rw-r--r--libgo/go/os/signal/signal_test.go427
-rw-r--r--libgo/go/os/signal/signal_unix.go2
-rw-r--r--libgo/go/os/testdata/issue37161/a1
-rw-r--r--libgo/go/os/testdata/issue37161/b1
-rw-r--r--libgo/go/os/testdata/issue37161/c1
-rw-r--r--libgo/go/os/timeout_test.go31
-rw-r--r--libgo/go/os/user/lookup_windows.go4
-rw-r--r--libgo/go/os/wait_wait6.go23
-rw-r--r--libgo/go/os/wait_waitid.go12
36 files changed, 1310 insertions, 411 deletions
diff --git a/libgo/go/os/error.go b/libgo/go/os/error.go
index 26bfe4c..875cc97 100644
--- a/libgo/go/os/error.go
+++ b/libgo/go/os/error.go
@@ -18,11 +18,12 @@ var (
// Methods on File will return this error when the receiver is nil.
ErrInvalid = errInvalid() // "invalid argument"
- ErrPermission = errPermission() // "permission denied"
- ErrExist = errExist() // "file already exists"
- ErrNotExist = errNotExist() // "file does not exist"
- ErrClosed = errClosed() // "file already closed"
- ErrNoDeadline = errNoDeadline() // "file type does not support deadline"
+ ErrPermission = errPermission() // "permission denied"
+ ErrExist = errExist() // "file already exists"
+ ErrNotExist = errNotExist() // "file does not exist"
+ ErrClosed = errClosed() // "file already closed"
+ ErrNoDeadline = errNoDeadline() // "file type does not support deadline"
+ ErrDeadlineExceeded = errDeadlineExceeded() // "i/o timeout"
)
func errInvalid() error { return oserror.ErrInvalid }
@@ -32,6 +33,15 @@ func errNotExist() error { return oserror.ErrNotExist }
func errClosed() error { return oserror.ErrClosed }
func errNoDeadline() error { return poll.ErrNoDeadline }
+// errDeadlineExceeded returns the value for os.ErrDeadlineExceeded.
+// This error comes from the internal/poll package, which is also
+// used by package net. Doing this this way ensures that the net
+// package will return os.ErrDeadlineExceeded for an exceeded deadline,
+// as documented by net.Conn.SetDeadline, without requiring any extra
+// work in the net package and without requiring the internal/poll
+// package to import os (which it can't, because that would be circular).
+func errDeadlineExceeded() error { return poll.ErrDeadlineExceeded }
+
type timeout interface {
Timeout() bool
}
diff --git a/libgo/go/os/exec/exec_linux_test.go b/libgo/go/os/exec/exec_linux_test.go
new file mode 100644
index 0000000..6f85020
--- /dev/null
+++ b/libgo/go/os/exec/exec_linux_test.go
@@ -0,0 +1,45 @@
+// 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.
+
+// +build linux,cgo
+
+// On systems that use glibc, calling malloc can create a new arena,
+// and creating a new arena can read /sys/devices/system/cpu/online.
+// If we are using cgo, we will call malloc when creating a new thread.
+// That can break TestExtraFiles if we create a new thread that creates
+// a new arena and opens the /sys file while we are checking for open
+// file descriptors. Work around the problem by creating threads up front.
+// See issue 25628.
+
+package exec_test
+
+import (
+ "os"
+ "sync"
+ "syscall"
+ "time"
+)
+
+func init() {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
+ return
+ }
+
+ // Start some threads. 10 is arbitrary but intended to be enough
+ // to ensure that the code won't have to create any threads itself.
+ // In particular this should be more than the number of threads
+ // the garbage collector might create.
+ const threads = 10
+
+ var wg sync.WaitGroup
+ wg.Add(threads)
+ ts := syscall.NsecToTimespec((100 * time.Microsecond).Nanoseconds())
+ for i := 0; i < threads; i++ {
+ go func() {
+ defer wg.Done()
+ syscall.Nanosleep(&ts, nil)
+ }()
+ }
+ wg.Wait()
+}
diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go
index dce66c5..dafbc64 100644
--- a/libgo/go/os/exec/exec_test.go
+++ b/libgo/go/os/exec/exec_test.go
@@ -488,25 +488,6 @@ func numOpenFDsAndroid(t *testing.T) (n int, lsof []byte) {
return bytes.Count(lsof, []byte("\n")), lsof
}
-// basefds returns the number of expected file descriptors
-// to be present in a process at start.
-// stdin, stdout, stderr, epoll/kqueue, epoll/kqueue pipe, maybe testlog
-func basefds() uintptr {
- n := os.Stderr.Fd() + 1
- // The poll (epoll/kqueue) descriptor can be numerically
- // either between stderr and the testlog-fd, or after
- // testlog-fd.
- for poll.IsPollDescriptor(n) {
- n++
- }
- for _, arg := range os.Args {
- if strings.HasPrefix(arg, "-test.testlogfile=") {
- n++
- }
- }
- return n
-}
-
func TestExtraFilesFDShuffle(t *testing.T) {
t.Skip("flaky test; see https://golang.org/issue/5780")
switch runtime.GOOS {
@@ -622,6 +603,7 @@ func TestExtraFiles(t *testing.T) {
}
testenv.MustHaveExec(t)
+ testenv.MustHaveGoBuild(t)
if runtime.GOOS == "windows" {
t.Skipf("skipping test on %q", runtime.GOOS)
@@ -676,7 +658,31 @@ func TestExtraFiles(t *testing.T) {
t.Fatalf("Seek: %v", err)
}
- c := helperCommand(t, "read3")
+ tempdir := t.TempDir()
+ exe := filepath.Join(tempdir, "read3.exe")
+
+ c := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "read3.go")
+ // Build the test without cgo, so that C library functions don't
+ // open descriptors unexpectedly. See issue 25628.
+ c.Env = append(os.Environ(), "CGO_ENABLED=0")
+ if output, err := c.CombinedOutput(); err != nil {
+ t.Logf("go build -o %s read3.go\n%s", exe, output)
+ t.Fatalf("go build failed: %v", err)
+ }
+
+ // Use a deadline to try to get some output even if the program hangs.
+ ctx := context.Background()
+ if deadline, ok := t.Deadline(); ok {
+ // Leave a 20% grace period to flush output, which may be large on the
+ // linux/386 builders because we're running the subprocess under strace.
+ deadline = deadline.Add(-time.Until(deadline) / 5)
+
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithDeadline(ctx, deadline)
+ defer cancel()
+ }
+
+ c = exec.CommandContext(ctx, exe)
var stdout, stderr bytes.Buffer
c.Stdout = &stdout
c.Stderr = &stderr
@@ -757,17 +763,6 @@ func TestHelperProcess(*testing.T) {
}
defer os.Exit(0)
- // Determine which command to use to display open files.
- ofcmd := "lsof"
- switch runtime.GOOS {
- case "dragonfly", "freebsd", "netbsd", "openbsd":
- ofcmd = "fstat"
- case "plan9":
- ofcmd = "/bin/cat"
- case "aix":
- ofcmd = "procfiles"
- }
-
args := os.Args
for len(args) > 0 {
if args[0] == "--" {
@@ -841,55 +836,6 @@ func TestHelperProcess(*testing.T) {
os.Exit(1)
}
os.Exit(0)
- case "read3": // read fd 3
- fd3 := os.NewFile(3, "fd3")
- bs, err := ioutil.ReadAll(fd3)
- if err != nil {
- fmt.Printf("ReadAll from fd 3: %v", err)
- os.Exit(1)
- }
- // Now verify that there are no other open fds.
- var files []*os.File
- for wantfd := basefds() + 1; wantfd <= 100; wantfd++ {
- if poll.IsPollDescriptor(wantfd) {
- continue
- }
- f, err := os.Open(os.Args[0])
- if err != nil {
- fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
- os.Exit(1)
- }
- if got := f.Fd(); got != wantfd {
- fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd)
- var args []string
- switch runtime.GOOS {
- case "plan9":
- args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
- case "aix":
- args = []string{fmt.Sprint(os.Getpid())}
- default:
- args = []string{"-p", fmt.Sprint(os.Getpid())}
- }
- cmd := exec.Command(ofcmd, args...)
- out, err := cmd.CombinedOutput()
- if err != nil {
- fmt.Fprintf(os.Stderr, "%s failed: %v\n", strings.Join(cmd.Args, " "), err)
- }
- fmt.Printf("%s", out)
- os.Exit(1)
- }
- files = append(files, f)
- }
- for _, f := range files {
- f.Close()
- }
- // Referring to fd3 here ensures that it is not
- // garbage collected, and therefore closed, while
- // executing the wantfd loop above. It doesn't matter
- // what we do with fd3 as long as we refer to it;
- // closing it is the easy choice.
- fd3.Close()
- os.Stdout.Write(bs)
case "exit":
n, _ := strconv.Atoi(args[0])
os.Exit(n)
diff --git a/libgo/go/os/exec/read3.go b/libgo/go/os/exec/read3.go
new file mode 100644
index 0000000..25d732a
--- /dev/null
+++ b/libgo/go/os/exec/read3.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.
+
+// +build ignore
+
+// This is a test program that verifies that it can read from
+// descriptor 3 and that no other descriptors are open.
+// This is not done via TestHelperProcess and GO_WANT_HELPER_PROCESS
+// because we want to ensure that this program does not use cgo,
+// because C libraries can open file descriptors behind our backs
+// and confuse the test. See issue 25628.
+package main
+
+import (
+ "fmt"
+ "internal/poll"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "runtime"
+ "strings"
+)
+
+func main() {
+ fd3 := os.NewFile(3, "fd3")
+ bs, err := ioutil.ReadAll(fd3)
+ if err != nil {
+ fmt.Printf("ReadAll from fd 3: %v\n", err)
+ os.Exit(1)
+ }
+
+ // Now verify that there are no other open fds.
+ // stdin == 0
+ // stdout == 1
+ // stderr == 2
+ // descriptor from parent == 3
+ // All descriptors 4 and up should be available,
+ // except for any used by the network poller.
+ var files []*os.File
+ for wantfd := uintptr(4); wantfd <= 100; wantfd++ {
+ if poll.IsPollDescriptor(wantfd) {
+ continue
+ }
+ f, err := os.Open(os.Args[0])
+ if err != nil {
+ fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
+ os.Exit(1)
+ }
+ if got := f.Fd(); got != wantfd {
+ fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd)
+ fdfile := fmt.Sprintf("/proc/self/fd/%d", wantfd)
+ link, err := os.Readlink(fdfile)
+ fmt.Printf("readlink(%q) = %q, %v\n", fdfile, link, err)
+ var args []string
+ switch runtime.GOOS {
+ case "plan9":
+ args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
+ case "aix":
+ args = []string{fmt.Sprint(os.Getpid())}
+ default:
+ args = []string{"-p", fmt.Sprint(os.Getpid())}
+ }
+
+ // Determine which command to use to display open files.
+ ofcmd := "lsof"
+ switch runtime.GOOS {
+ case "dragonfly", "freebsd", "netbsd", "openbsd":
+ ofcmd = "fstat"
+ case "plan9":
+ ofcmd = "/bin/cat"
+ case "aix":
+ ofcmd = "procfiles"
+ }
+
+ cmd := exec.Command(ofcmd, args...)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s failed: %v\n", strings.Join(cmd.Args, " "), err)
+ }
+ fmt.Printf("%s", out)
+ os.Exit(1)
+ }
+ files = append(files, f)
+ }
+
+ for _, f := range files {
+ f.Close()
+ }
+
+ // Referring to fd3 here ensures that it is not
+ // garbage collected, and therefore closed, while
+ // executing the wantfd loop above. It doesn't matter
+ // what we do with fd3 as long as we refer to it;
+ // closing it is the easy choice.
+ fd3.Close()
+
+ os.Stdout.Write(bs)
+}
diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go
index 238d755..a99b45d 100644
--- a/libgo/go/os/exec_unix.go
+++ b/libgo/go/os/exec_unix.go
@@ -33,9 +33,18 @@ func (p *Process) wait() (ps *ProcessState, err error) {
p.sigMu.Unlock()
}
- var status syscall.WaitStatus
- var rusage syscall.Rusage
- pid1, e := syscall.Wait4(p.Pid, &status, 0, &rusage)
+ var (
+ status syscall.WaitStatus
+ rusage syscall.Rusage
+ pid1 int
+ e error
+ )
+ for {
+ pid1, e = syscall.Wait4(p.Pid, &status, 0, &rusage)
+ if e != syscall.EINTR {
+ break
+ }
+ }
if e != nil {
return nil, NewSyscallError("wait", e)
}
diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go
index 10503c5..24ddf89 100644
--- a/libgo/go/os/exec_windows.go
+++ b/libgo/go/os/exec_windows.go
@@ -98,7 +98,7 @@ func findProcess(pid int) (p *Process, err error) {
}
func init() {
- cmd := windows.UTF16PtrToString(syscall.GetCommandLine(), 0xffff)
+ cmd := windows.UTF16PtrToString(syscall.GetCommandLine())
if len(cmd) == 0 {
arg0, _ := Executable()
Args = []string{arg0}
diff --git a/libgo/go/os/export_linux_test.go b/libgo/go/os/export_linux_test.go
new file mode 100644
index 0000000..d947d05
--- /dev/null
+++ b/libgo/go/os/export_linux_test.go
@@ -0,0 +1,7 @@
+// 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 os
+
+var PollCopyFileRangeP = &pollCopyFileRange
diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go
index 9f8c827..a2b71cb 100644
--- a/libgo/go/os/file.go
+++ b/libgo/go/os/file.go
@@ -143,6 +143,26 @@ func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
return
}
+// ReadFrom implements io.ReaderFrom.
+func (f *File) ReadFrom(r io.Reader) (n int64, err error) {
+ if err := f.checkValid("write"); err != nil {
+ return 0, err
+ }
+ n, handled, e := f.readFrom(r)
+ if !handled {
+ return genericReadFrom(f, r) // without wrapping
+ }
+ return n, f.wrapErr("write", e)
+}
+
+func genericReadFrom(f *File, r io.Reader) (int64, error) {
+ return io.Copy(onlyWriter{f}, r)
+}
+
+type onlyWriter struct {
+ io.Writer
+}
+
// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
@@ -364,7 +384,7 @@ func TempDir() string {
// within this one and use that.
//
// On Unix systems, it returns $XDG_CACHE_HOME as specified by
-// https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html if
+// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if
// non-empty, else $HOME/.cache.
// On Darwin, it returns $HOME/Library/Caches.
// On Windows, it returns %LocalAppData%.
@@ -482,7 +502,7 @@ func UserHomeDir() (string, error) {
case "android":
return "/sdcard", nil
case "darwin":
- if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
+ if runtime.GOARCH == "arm64" {
return "/", nil
}
}
@@ -526,10 +546,12 @@ func (f *File) Chmod(mode FileMode) error { return f.chmod(mode) }
// After a deadline has been exceeded, the connection can be refreshed
// by setting a deadline in the future.
//
-// An error returned after a timeout fails will implement the
-// Timeout method, and calling the Timeout method will return true.
-// The PathError and SyscallError types implement the Timeout method.
-// In general, call IsTimeout to test whether an error indicates a timeout.
+// If the deadline is exceeded a call to Read or Write or to other I/O
+// methods will return an error that wraps ErrDeadlineExceeded.
+// This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
+// That error implements the Timeout method, and calling the Timeout
+// method will return true, but there are other possible errors for which
+// the Timeout will return true even if the deadline has not been exceeded.
//
// An idle timeout can be implemented by repeatedly extending
// the deadline after successful Read or Write calls.
diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go
index 48bf5f5..eb15890 100644
--- a/libgo/go/os/file_plan9.go
+++ b/libgo/go/os/file_plan9.go
@@ -112,10 +112,9 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
} else {
fd, e = syscall.Open(name, flag)
if IsNotExist(e) && create {
- var e1 error
- fd, e1 = syscall.Create(name, flag, syscallMode(perm))
- if e1 == nil {
- e = nil
+ fd, e = syscall.Create(name, flag, syscallMode(perm))
+ if e != nil {
+ return nil, &PathError{"create", name, e}
}
}
}
@@ -234,10 +233,10 @@ func (f *File) Sync() error {
var buf [syscall.STATFIXLEN]byte
n, err := d.Marshal(buf[:])
if err != nil {
- return NewSyscallError("fsync", err)
+ return &PathError{"sync", f.name, err}
}
if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
- return NewSyscallError("fsync", err)
+ return &PathError{"sync", f.name, err}
}
return nil
}
@@ -290,6 +289,11 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) {
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
+ if f.dirinfo != nil {
+ // Free cached dirinfo, so we allocate a new one if we
+ // access this file as a directory again. See #35767 and #37161.
+ f.dirinfo = nil
+ }
return syscall.Seek(f.fd, offset, whence)
}
diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go
index f59d137..ee0728b 100644
--- a/libgo/go/os/file_posix.go
+++ b/libgo/go/os/file_posix.go
@@ -7,12 +7,57 @@
package os
import (
+ "runtime"
"syscall"
"time"
)
func sigpipe() // implemented in package runtime
+// Close closes the File, rendering it unusable for I/O.
+// On files that support SetDeadline, any pending I/O operations will
+// be canceled and return immediately with an error.
+// Close will return an error if it has already been called.
+func (f *File) Close() error {
+ if f == nil {
+ return ErrInvalid
+ }
+ return f.file.close()
+}
+
+// read reads up to len(b) bytes from the File.
+// It returns the number of bytes read and an error, if any.
+func (f *File) read(b []byte) (n int, err error) {
+ n, err = f.pfd.Read(b)
+ runtime.KeepAlive(f)
+ return n, err
+}
+
+// pread reads len(b) bytes from the File starting at byte offset off.
+// It returns the number of bytes read and the error, if any.
+// EOF is signaled by a zero count with err set to nil.
+func (f *File) pread(b []byte, off int64) (n int, err error) {
+ n, err = f.pfd.Pread(b, off)
+ runtime.KeepAlive(f)
+ return n, err
+}
+
+// write writes len(b) bytes to the File.
+// It returns the number of bytes written and an error, if any.
+func (f *File) write(b []byte) (n int, err error) {
+ n, err = f.pfd.Write(b)
+ runtime.KeepAlive(f)
+ return n, err
+}
+
+// pwrite writes len(b) bytes to the File starting at byte offset off.
+// It returns the number of bytes written and an error, if any.
+func (f *File) pwrite(b []byte, off int64) (n int, err error) {
+ n, err = f.pfd.Pwrite(b, off)
+ runtime.KeepAlive(f)
+ return n, err
+}
+
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
func syscallMode(i FileMode) (o uint32) {
o |= uint32(i.Perm())
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index 2fc6318..aed7713 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -177,6 +177,15 @@ type dirInfo struct {
dir *syscall.DIR // from opendir
}
+func (d *dirInfo) close() {
+ if d.dir != nil {
+ syscall.Entersyscall()
+ libc_closedir(d.dir)
+ syscall.Exitsyscall()
+ d.dir = nil
+ }
+}
+
// epipecheck raises SIGPIPE if we get an EPIPE error on standard
// output or standard error. See the SIGPIPE docs in os/signal, and
// issue 11845.
@@ -208,10 +217,8 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
break
}
- // On OS X, sigaction(2) doesn't guarantee that SA_RESTART will cause
- // open(2) to be restarted for regular files. This is easy to reproduce on
- // fuse file systems (see https://golang.org/issue/11180).
- if runtime.GOOS == "darwin" && e == syscall.EINTR {
+ // We have to check EINTR here, per issues 11180 and 39237.
+ if e == syscall.EINTR {
continue
}
@@ -232,31 +239,13 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
return newFile(uintptr(r), name, kindOpenFile), nil
}
-// Close closes the File, rendering it unusable for I/O.
-// On files that support SetDeadline, any pending I/O operations will
-// be canceled and return immediately with an error.
-// Close will return an error if it has already been called.
-func (f *File) Close() error {
- if f == nil {
- return ErrInvalid
- }
- return f.file.close()
-}
-
func (file *file) close() error {
if file == nil {
return syscall.EINVAL
}
var err error
if file.dirinfo != nil {
- syscall.Entersyscall()
- i := libc_closedir(file.dirinfo.dir)
- errno := syscall.GetErrno()
- syscall.Exitsyscall()
- file.dirinfo = nil
- if i < 0 && errno != 0 {
- err = &PathError{"closedir", file.name, errno}
- }
+ file.dirinfo.close()
}
if e := file.pfd.Close(); e != nil {
if e == poll.ErrFileClosing {
@@ -270,45 +259,17 @@ func (file *file) close() error {
return err
}
-// read reads up to len(b) bytes from the File.
-// It returns the number of bytes read and an error, if any.
-func (f *File) read(b []byte) (n int, err error) {
- n, err = f.pfd.Read(b)
- runtime.KeepAlive(f)
- return n, err
-}
-
-// pread reads len(b) bytes from the File starting at byte offset off.
-// It returns the number of bytes read and the error, if any.
-// EOF is signaled by a zero count with err set to nil.
-func (f *File) pread(b []byte, off int64) (n int, err error) {
- n, err = f.pfd.Pread(b, off)
- runtime.KeepAlive(f)
- return n, err
-}
-
-// write writes len(b) bytes to the File.
-// It returns the number of bytes written and an error, if any.
-func (f *File) write(b []byte) (n int, err error) {
- n, err = f.pfd.Write(b)
- runtime.KeepAlive(f)
- return n, err
-}
-
-// pwrite writes len(b) bytes to the File starting at byte offset off.
-// It returns the number of bytes written and an error, if any.
-func (f *File) pwrite(b []byte, off int64) (n int, err error) {
- n, err = f.pfd.Pwrite(b, off)
- runtime.KeepAlive(f)
- return n, err
-}
-
// seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
- f.seekInvalidate()
+ if f.dirinfo != nil {
+ // Free cached dirinfo, so we allocate a new one if we
+ // access this file as a directory again. See #35767 and #37161.
+ f.dirinfo.close()
+ f.dirinfo = nil
+ }
ret, err = f.pfd.Seek(offset, whence)
runtime.KeepAlive(f)
return ret, err
diff --git a/libgo/go/os/issue37161/a b/libgo/go/os/issue37161/a
new file mode 100644
index 0000000..78981922
--- /dev/null
+++ b/libgo/go/os/issue37161/a
@@ -0,0 +1 @@
+a
diff --git a/libgo/go/os/issue37161/b b/libgo/go/os/issue37161/b
new file mode 100644
index 0000000..6178079
--- /dev/null
+++ b/libgo/go/os/issue37161/b
@@ -0,0 +1 @@
+b
diff --git a/libgo/go/os/issue37161/c b/libgo/go/os/issue37161/c
new file mode 100644
index 0000000..f2ad6c7
--- /dev/null
+++ b/libgo/go/os/issue37161/c
@@ -0,0 +1 @@
+c
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
index 8ec6de7..8a2f917 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -52,7 +52,7 @@ var sysdir = func() *sysDir {
}
case "darwin":
switch runtime.GOARCH {
- case "arm", "arm64":
+ case "arm64":
wd, err := syscall.Getwd()
if err != nil {
wd = err.Error()
@@ -144,7 +144,7 @@ func localTmp() string {
return TempDir()
case "darwin":
switch runtime.GOARCH {
- case "arm", "arm64":
+ case "arm64":
return TempDir()
}
}
@@ -481,7 +481,7 @@ func TestReaddirnamesOneAtATime(t *testing.T) {
dir = "/system/bin"
case "darwin":
switch runtime.GOARCH {
- case "arm", "arm64":
+ case "arm64":
wd, err := Getwd()
if err != nil {
t.Fatal(err)
@@ -1240,6 +1240,41 @@ func testChtimes(t *testing.T, name string) {
}
}
+func TestFileChdir(t *testing.T) {
+ // TODO(brainman): file.Chdir() is not implemented on windows.
+ if runtime.GOOS == "windows" {
+ return
+ }
+
+ wd, err := Getwd()
+ if err != nil {
+ t.Fatalf("Getwd: %s", err)
+ }
+ defer Chdir(wd)
+
+ fd, err := Open(".")
+ if err != nil {
+ t.Fatalf("Open .: %s", err)
+ }
+ defer fd.Close()
+
+ if err := Chdir("/"); err != nil {
+ t.Fatalf("Chdir /: %s", err)
+ }
+
+ if err := fd.Chdir(); err != nil {
+ t.Fatalf("fd.Chdir: %s", err)
+ }
+
+ wdNew, err := Getwd()
+ if err != nil {
+ t.Fatalf("Getwd: %s", err)
+ }
+ if wdNew != wd {
+ t.Fatalf("fd.Chdir failed, got %s, want %s", wdNew, wd)
+ }
+}
+
func TestChdirAndGetwd(t *testing.T) {
// TODO(brainman): file.Chdir() is not implemented on windows.
if runtime.GOOS == "windows" {
@@ -1260,7 +1295,7 @@ func TestChdirAndGetwd(t *testing.T) {
dirs = []string{"/", "/usr"}
case "darwin":
switch runtime.GOARCH {
- case "arm", "arm64":
+ case "arm64":
dirs = nil
for _, d := range []string{"d1", "d2"} {
dir, err := ioutil.TempDir("", d)
@@ -1323,8 +1358,9 @@ func TestChdirAndGetwd(t *testing.T) {
// Test that Chdir+Getwd is program-wide.
func TestProgWideChdir(t *testing.T) {
const N = 10
+ const ErrPwd = "Error!"
c := make(chan bool)
- cpwd := make(chan string)
+ cpwd := make(chan string, N)
for i := 0; i < N; i++ {
go func(i int) {
// Lock half the goroutines in their own operating system
@@ -1337,10 +1373,15 @@ func TestProgWideChdir(t *testing.T) {
// See issue 9428.
runtime.LockOSThread()
}
- <-c
+ hasErr, closed := <-c
+ if !closed && hasErr {
+ cpwd <- ErrPwd
+ return
+ }
pwd, err := Getwd()
if err != nil {
t.Errorf("Getwd on goroutine %d: %v", i, err)
+ cpwd <- ErrPwd
return
}
cpwd <- pwd
@@ -1348,10 +1389,12 @@ func TestProgWideChdir(t *testing.T) {
}
oldwd, err := Getwd()
if err != nil {
+ c <- true
t.Fatalf("Getwd: %v", err)
}
d, err := ioutil.TempDir("", "test")
if err != nil {
+ c <- true
t.Fatalf("TempDir: %v", err)
}
defer func() {
@@ -1361,17 +1404,22 @@ func TestProgWideChdir(t *testing.T) {
RemoveAll(d)
}()
if err := Chdir(d); err != nil {
+ c <- true
t.Fatalf("Chdir: %v", err)
}
// OS X sets TMPDIR to a symbolic link.
// So we resolve our working directory again before the test.
d, err = Getwd()
if err != nil {
+ c <- true
t.Fatalf("Getwd: %v", err)
}
close(c)
for i := 0; i < N; i++ {
pwd := <-cpwd
+ if pwd == ErrPwd {
+ t.FailNow()
+ }
if pwd != d {
t.Errorf("Getwd returned %q; want %q", pwd, d)
}
@@ -1787,7 +1835,7 @@ func TestAppend(t *testing.T) {
func TestStatDirWithTrailingSlash(t *testing.T) {
// Create new temporary directory and arrange to clean it up.
- path, err := ioutil.TempDir("", "/_TestStatDirWithSlash_")
+ path, err := ioutil.TempDir("", "_TestStatDirWithSlash_")
if err != nil {
t.Fatalf("TempDir: %s", err)
}
@@ -2451,14 +2499,53 @@ func TestDirSeek(t *testing.T) {
}
}
-// Test that opening a file does not change its permissions. Issue 38225.
-func TestOpenFileKeepsPermissions(t *testing.T) {
- t.Parallel()
- dir, err := ioutil.TempDir("", "TestOpenFileKeepsPermissions")
+func TestReaddirSmallSeek(t *testing.T) {
+ // See issue 37161. Read only one entry from a directory,
+ // seek to the beginning, and read again. We should not see
+ // duplicate entries.
+ if runtime.GOOS == "windows" {
+ testenv.SkipFlaky(t, 36019)
+ }
+ wd, err := Getwd()
if err != nil {
t.Fatal(err)
}
- defer RemoveAll(dir)
+ df, err := Open(filepath.Join(wd, "testdata", "issue37161"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ names1, err := df.Readdirnames(1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err = df.Seek(0, 0); err != nil {
+ t.Fatal(err)
+ }
+ names2, err := df.Readdirnames(0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(names2) != 3 {
+ t.Fatalf("first names: %v, second names: %v", names1, names2)
+ }
+}
+
+// isDeadlineExceeded reports whether err is or wraps os.ErrDeadlineExceeded.
+// We also check that the error has a Timeout method that returns true.
+func isDeadlineExceeded(err error) bool {
+ if !IsTimeout(err) {
+ return false
+ }
+ if !errors.Is(err, ErrDeadlineExceeded) {
+ return false
+ }
+ return true
+}
+
+// Test that opening a file does not change its permissions. Issue 38225.
+func TestOpenFileKeepsPermissions(t *testing.T) {
+ t.Parallel()
+ dir := t.TempDir()
name := filepath.Join(dir, "x")
f, err := Create(name)
if err != nil {
diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go
index 7af20d7..7b6fa0c 100644
--- a/libgo/go/os/os_unix_test.go
+++ b/libgo/go/os/os_unix_test.go
@@ -275,7 +275,7 @@ func newFileTest(t *testing.T, blocking bool) {
_, err := file.Read(b)
if !blocking {
// We want it to fail with a timeout.
- if !IsTimeout(err) {
+ if !isDeadlineExceeded(err) {
t.Fatalf("No timeout reading from file: %v", err)
}
} else {
diff --git a/libgo/go/os/os_windows_test.go b/libgo/go/os/os_windows_test.go
index 8c14103..f03ec75 100644
--- a/libgo/go/os/os_windows_test.go
+++ b/libgo/go/os/os_windows_test.go
@@ -965,9 +965,10 @@ func TestWindowsDevNullFile(t *testing.T) {
// works on Windows when developer mode is active.
// This is supported starting Windows 10 (1703, v10.0.14972).
func TestSymlinkCreation(t *testing.T) {
- if !isWindowsDeveloperModeActive() {
+ if !testenv.HasSymlink() && !isWindowsDeveloperModeActive() {
t.Skip("Windows developer mode is not active")
}
+ t.Parallel()
temp, err := ioutil.TempDir("", "TestSymlinkCreation")
if err != nil {
@@ -1005,6 +1006,122 @@ func isWindowsDeveloperModeActive() bool {
return val != 0
}
+// TestRootRelativeDirSymlink verifies that symlinks to paths relative to the
+// drive root (beginning with "\" but no volume name) are created with the
+// correct symlink type.
+// (See https://golang.org/issue/39183#issuecomment-632175728.)
+func TestRootRelativeDirSymlink(t *testing.T) {
+ testenv.MustHaveSymlink(t)
+ t.Parallel()
+
+ temp := t.TempDir()
+ dir := filepath.Join(temp, "dir")
+ if err := os.Mkdir(dir, 0755); err != nil {
+ t.Fatal(err)
+ }
+
+ volumeRelDir := strings.TrimPrefix(dir, filepath.VolumeName(dir)) // leaves leading backslash
+
+ link := filepath.Join(temp, "link")
+ err := os.Symlink(volumeRelDir, link)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("Symlink(%#q, %#q)", volumeRelDir, link)
+
+ f, err := os.Open(link)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ if fi, err := f.Stat(); err != nil {
+ t.Fatal(err)
+ } else if !fi.IsDir() {
+ t.Errorf("Open(%#q).Stat().IsDir() = false; want true", f.Name())
+ }
+}
+
+// TestWorkingDirectoryRelativeSymlink verifies that symlinks to paths relative
+// to the current working directory for the drive, such as "C:File.txt", are
+// correctly converted to absolute links of the correct symlink type (per
+// https://docs.microsoft.com/en-us/windows/win32/fileio/creating-symbolic-links).
+func TestWorkingDirectoryRelativeSymlink(t *testing.T) {
+ testenv.MustHaveSymlink(t)
+
+ // Construct a directory to be symlinked.
+ temp := t.TempDir()
+ if v := filepath.VolumeName(temp); len(v) < 2 || v[1] != ':' {
+ t.Skipf("Can't test relative symlinks: t.TempDir() (%#q) does not begin with a drive letter.", temp)
+ }
+
+ absDir := filepath.Join(temp, `dir\sub`)
+ if err := os.MkdirAll(absDir, 0755); err != nil {
+ t.Fatal(err)
+ }
+
+ // Change to the temporary directory and construct a
+ // working-directory-relative symlink.
+ oldwd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() {
+ if err := os.Chdir(oldwd); err != nil {
+ t.Fatal(err)
+ }
+ }()
+ if err := os.Chdir(temp); err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("Chdir(%#q)", temp)
+
+ wdRelDir := filepath.VolumeName(temp) + `dir\sub` // no backslash after volume.
+ absLink := filepath.Join(temp, "link")
+ err = os.Symlink(wdRelDir, absLink)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("Symlink(%#q, %#q)", wdRelDir, absLink)
+
+ // Now change back to the original working directory and verify that the
+ // symlink still refers to its original path and is correctly marked as a
+ // directory.
+ if err := os.Chdir(oldwd); err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("Chdir(%#q)", oldwd)
+
+ resolved, err := os.Readlink(absLink)
+ if err != nil {
+ t.Errorf("Readlink(%#q): %v", absLink, err)
+ } else if resolved != absDir {
+ t.Errorf("Readlink(%#q) = %#q; want %#q", absLink, resolved, absDir)
+ }
+
+ linkFile, err := os.Open(absLink)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer linkFile.Close()
+
+ linkInfo, err := linkFile.Stat()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !linkInfo.IsDir() {
+ t.Errorf("Open(%#q).Stat().IsDir() = false; want true", absLink)
+ }
+
+ absInfo, err := os.Stat(absDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !os.SameFile(absInfo, linkInfo) {
+ t.Errorf("SameFile(Stat(%#q), Open(%#q).Stat()) = false; want true", absDir, absLink)
+ }
+}
+
// TestStatOfInvalidName is regression test for issue #24999.
func TestStatOfInvalidName(t *testing.T) {
_, err := os.Stat("*.go")
diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go
index 6cb25bc..d586daf 100644
--- a/libgo/go/os/path_test.go
+++ b/libgo/go/os/path_test.go
@@ -109,8 +109,8 @@ func TestMkdirAllAtSlash(t *testing.T) {
t.Skipf("skipping on %s", runtime.GOOS)
case "darwin":
switch runtime.GOARCH {
- case "arm", "arm64":
- t.Skipf("skipping on darwin/%s, mkdir returns EPERM", runtime.GOARCH)
+ case "arm64":
+ t.Skipf("skipping on darwin/arm64, mkdir returns EPERM")
}
}
RemoveAll("/_go_os_test")
diff --git a/libgo/go/os/pipe_test.go b/libgo/go/os/pipe_test.go
index 2e93e39..429bd81 100644
--- a/libgo/go/os/pipe_test.go
+++ b/libgo/go/os/pipe_test.go
@@ -104,6 +104,25 @@ func TestStdPipe(t *testing.T) {
}
}
}
+
+ // Test redirecting stdout but not stderr. Issue 40076.
+ cmd := osexec.Command(os.Args[0], "-test.run", "TestStdPipeHelper")
+ cmd.Stdout = w
+ var stderr bytes.Buffer
+ cmd.Stderr = &stderr
+ cmd.Env = append(os.Environ(), "GO_TEST_STD_PIPE_HELPER=1")
+ if err := cmd.Run(); err == nil {
+ t.Errorf("unexpected success of write to closed stdout")
+ } else if ee, ok := err.(*osexec.ExitError); !ok {
+ t.Errorf("unexpected exec error type %T: %v", err, err)
+ } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
+ t.Errorf("unexpected wait status type %T: %v", ee.Sys(), ee.Sys())
+ } else if !ws.Signaled() || ws.Signal() != syscall.SIGPIPE {
+ t.Errorf("unexpected exit status %v for write to closed stdout", err)
+ }
+ if output := stderr.Bytes(); len(output) > 0 {
+ t.Errorf("unexpected output on stderr: %s", output)
+ }
}
// This is a helper for TestStdPipe. It's not a test in itself.
diff --git a/libgo/go/os/readfrom_linux.go b/libgo/go/os/readfrom_linux.go
new file mode 100644
index 0000000..63ea45c
--- /dev/null
+++ b/libgo/go/os/readfrom_linux.go
@@ -0,0 +1,46 @@
+// 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 os
+
+import (
+ "internal/poll"
+ "io"
+)
+
+var pollCopyFileRange = poll.CopyFileRange
+
+func (f *File) readFrom(r io.Reader) (written int64, handled bool, err error) {
+ // copy_file_range(2) does not support destinations opened with
+ // O_APPEND, so don't even try.
+ if f.appendMode {
+ return 0, false, nil
+ }
+
+ remain := int64(1 << 62)
+
+ lr, ok := r.(*io.LimitedReader)
+ if ok {
+ remain, r = lr.N, lr.R
+ if remain <= 0 {
+ return 0, true, nil
+ }
+ }
+
+ src, ok := r.(*File)
+ if !ok {
+ return 0, false, nil
+ }
+ if src.checkValid("ReadFrom") != nil {
+ // Avoid returning the error as we report handled as false,
+ // leave further error handling as the responsibility of the caller.
+ return 0, false, nil
+ }
+
+ written, handled, err = pollCopyFileRange(&f.pfd, &src.pfd, remain)
+ if lr != nil {
+ lr.N -= written
+ }
+ return written, handled, NewSyscallError("copy_file_range", err)
+}
diff --git a/libgo/go/os/readfrom_linux_test.go b/libgo/go/os/readfrom_linux_test.go
new file mode 100644
index 0000000..00faf39
--- /dev/null
+++ b/libgo/go/os/readfrom_linux_test.go
@@ -0,0 +1,363 @@
+// 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 os_test
+
+import (
+ "bytes"
+ "internal/poll"
+ "io"
+ "io/ioutil"
+ "math/rand"
+ . "os"
+ "path/filepath"
+ "strconv"
+ "syscall"
+ "testing"
+ "time"
+)
+
+func TestCopyFileRange(t *testing.T) {
+ sizes := []int{
+ 1,
+ 42,
+ 1025,
+ syscall.Getpagesize() + 1,
+ 32769,
+ }
+ t.Run("Basic", func(t *testing.T) {
+ for _, size := range sizes {
+ t.Run(strconv.Itoa(size), func(t *testing.T) {
+ testCopyFileRange(t, int64(size), -1)
+ })
+ }
+ })
+ t.Run("Limited", func(t *testing.T) {
+ t.Run("OneLess", func(t *testing.T) {
+ for _, size := range sizes {
+ t.Run(strconv.Itoa(size), func(t *testing.T) {
+ testCopyFileRange(t, int64(size), int64(size)-1)
+ })
+ }
+ })
+ t.Run("Half", func(t *testing.T) {
+ for _, size := range sizes {
+ t.Run(strconv.Itoa(size), func(t *testing.T) {
+ testCopyFileRange(t, int64(size), int64(size)/2)
+ })
+ }
+ })
+ t.Run("More", func(t *testing.T) {
+ for _, size := range sizes {
+ t.Run(strconv.Itoa(size), func(t *testing.T) {
+ testCopyFileRange(t, int64(size), int64(size)+7)
+ })
+ }
+ })
+ })
+ t.Run("DoesntTryInAppendMode", func(t *testing.T) {
+ dst, src, data, hook := newCopyFileRangeTest(t, 42)
+
+ dst2, err := OpenFile(dst.Name(), O_RDWR|O_APPEND, 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer dst2.Close()
+
+ if _, err := io.Copy(dst2, src); err != nil {
+ t.Fatal(err)
+ }
+ if hook.called {
+ t.Fatal("called poll.CopyFileRange for destination in O_APPEND mode")
+ }
+ mustSeekStart(t, dst2)
+ mustContainData(t, dst2, data) // through traditional means
+ })
+ t.Run("NotRegular", func(t *testing.T) {
+ t.Run("BothPipes", func(t *testing.T) {
+ hook := hookCopyFileRange(t)
+
+ pr1, pw1, err := Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer pr1.Close()
+ defer pw1.Close()
+
+ pr2, pw2, err := Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer pr2.Close()
+ defer pw2.Close()
+
+ // The pipe is empty, and PIPE_BUF is large enough
+ // for this, by (POSIX) definition, so there is no
+ // need for an additional goroutine.
+ data := []byte("hello")
+ if _, err := pw1.Write(data); err != nil {
+ t.Fatal(err)
+ }
+ pw1.Close()
+
+ n, err := io.Copy(pw2, pr1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n != int64(len(data)) {
+ t.Fatalf("transfered %d, want %d", n, len(data))
+ }
+ if !hook.called {
+ t.Fatalf("should have called poll.CopyFileRange")
+ }
+ pw2.Close()
+ mustContainData(t, pr2, data)
+ })
+ t.Run("DstPipe", func(t *testing.T) {
+ dst, src, data, hook := newCopyFileRangeTest(t, 255)
+ dst.Close()
+
+ pr, pw, err := Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer pr.Close()
+ defer pw.Close()
+
+ n, err := io.Copy(pw, src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n != int64(len(data)) {
+ t.Fatalf("transfered %d, want %d", n, len(data))
+ }
+ if !hook.called {
+ t.Fatalf("should have called poll.CopyFileRange")
+ }
+ pw.Close()
+ mustContainData(t, pr, data)
+ })
+ t.Run("SrcPipe", func(t *testing.T) {
+ dst, src, data, hook := newCopyFileRangeTest(t, 255)
+ src.Close()
+
+ pr, pw, err := Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer pr.Close()
+ defer pw.Close()
+
+ // The pipe is empty, and PIPE_BUF is large enough
+ // for this, by (POSIX) definition, so there is no
+ // need for an additional goroutine.
+ if _, err := pw.Write(data); err != nil {
+ t.Fatal(err)
+ }
+ pw.Close()
+
+ n, err := io.Copy(dst, pr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n != int64(len(data)) {
+ t.Fatalf("transfered %d, want %d", n, len(data))
+ }
+ if !hook.called {
+ t.Fatalf("should have called poll.CopyFileRange")
+ }
+ mustSeekStart(t, dst)
+ mustContainData(t, dst, data)
+ })
+ })
+ t.Run("Nil", func(t *testing.T) {
+ var nilFile *File
+ anyFile, err := ioutil.TempFile("", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer Remove(anyFile.Name())
+ defer anyFile.Close()
+
+ if _, err := io.Copy(nilFile, nilFile); err != ErrInvalid {
+ t.Errorf("io.Copy(nilFile, nilFile) = %v, want %v", err, ErrInvalid)
+ }
+ if _, err := io.Copy(anyFile, nilFile); err != ErrInvalid {
+ t.Errorf("io.Copy(anyFile, nilFile) = %v, want %v", err, ErrInvalid)
+ }
+ if _, err := io.Copy(nilFile, anyFile); err != ErrInvalid {
+ t.Errorf("io.Copy(nilFile, anyFile) = %v, want %v", err, ErrInvalid)
+ }
+
+ if _, err := nilFile.ReadFrom(nilFile); err != ErrInvalid {
+ t.Errorf("nilFile.ReadFrom(nilFile) = %v, want %v", err, ErrInvalid)
+ }
+ if _, err := anyFile.ReadFrom(nilFile); err != ErrInvalid {
+ t.Errorf("anyFile.ReadFrom(nilFile) = %v, want %v", err, ErrInvalid)
+ }
+ if _, err := nilFile.ReadFrom(anyFile); err != ErrInvalid {
+ t.Errorf("nilFile.ReadFrom(anyFile) = %v, want %v", err, ErrInvalid)
+ }
+ })
+}
+
+func testCopyFileRange(t *testing.T, size int64, limit int64) {
+ dst, src, data, hook := newCopyFileRangeTest(t, size)
+
+ // If we have a limit, wrap the reader.
+ var (
+ realsrc io.Reader
+ lr *io.LimitedReader
+ )
+ if limit >= 0 {
+ lr = &io.LimitedReader{N: limit, R: src}
+ realsrc = lr
+ if limit < int64(len(data)) {
+ data = data[:limit]
+ }
+ } else {
+ realsrc = src
+ }
+
+ // Now call ReadFrom (through io.Copy), which will hopefully call
+ // poll.CopyFileRange.
+ n, err := io.Copy(dst, realsrc)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // If we didn't have a limit, we should have called poll.CopyFileRange
+ // with the right file descriptor arguments.
+ if limit > 0 && !hook.called {
+ t.Fatal("never called poll.CopyFileRange")
+ }
+ if hook.called && hook.dstfd != int(dst.Fd()) {
+ t.Fatalf("wrong destination file descriptor: got %d, want %d", hook.dstfd, dst.Fd())
+ }
+ if hook.called && hook.srcfd != int(src.Fd()) {
+ t.Fatalf("wrong source file descriptor: got %d, want %d", hook.srcfd, src.Fd())
+ }
+
+ // Check that the offsets after the transfer make sense, that the size
+ // of the transfer was reported correctly, and that the destination
+ // file contains exactly the bytes we expect it to contain.
+ dstoff, err := dst.Seek(0, io.SeekCurrent)
+ if err != nil {
+ t.Fatal(err)
+ }
+ srcoff, err := src.Seek(0, io.SeekCurrent)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if dstoff != srcoff {
+ t.Errorf("offsets differ: dstoff = %d, srcoff = %d", dstoff, srcoff)
+ }
+ if dstoff != int64(len(data)) {
+ t.Errorf("dstoff = %d, want %d", dstoff, len(data))
+ }
+ if n != int64(len(data)) {
+ t.Errorf("short ReadFrom: wrote %d bytes, want %d", n, len(data))
+ }
+ mustSeekStart(t, dst)
+ mustContainData(t, dst, data)
+
+ // If we had a limit, check that it was updated.
+ if lr != nil {
+ if want := limit - n; lr.N != want {
+ t.Fatalf("didn't update limit correctly: got %d, want %d", lr.N, want)
+ }
+ }
+}
+
+// newCopyFileRangeTest initializes a new test for copy_file_range.
+//
+// It creates source and destination files, and populates the source file
+// with random data of the specified size. It also hooks package os' call
+// to poll.CopyFileRange and returns the hook so it can be inspected.
+func newCopyFileRangeTest(t *testing.T, size int64) (dst, src *File, data []byte, hook *copyFileRangeHook) {
+ t.Helper()
+
+ hook = hookCopyFileRange(t)
+ tmp := t.TempDir()
+
+ src, err := Create(filepath.Join(tmp, "src"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() { src.Close() })
+
+ dst, err = Create(filepath.Join(tmp, "dst"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() { dst.Close() })
+
+ // Populate the source file with data, then rewind it, so it can be
+ // consumed by copy_file_range(2).
+ prng := rand.New(rand.NewSource(time.Now().Unix()))
+ data = make([]byte, size)
+ prng.Read(data)
+ if _, err := src.Write(data); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := src.Seek(0, io.SeekStart); err != nil {
+ t.Fatal(err)
+ }
+
+ return dst, src, data, hook
+}
+
+// mustContainData ensures that the specified file contains exactly the
+// specified data.
+func mustContainData(t *testing.T, f *File, data []byte) {
+ t.Helper()
+
+ got := make([]byte, len(data))
+ if _, err := io.ReadFull(f, got); err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(got, data) {
+ t.Fatalf("didn't get the same data back from %s", f.Name())
+ }
+ if _, err := f.Read(make([]byte, 1)); err != io.EOF {
+ t.Fatalf("not at EOF")
+ }
+}
+
+func mustSeekStart(t *testing.T, f *File) {
+ if _, err := f.Seek(0, io.SeekStart); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func hookCopyFileRange(t *testing.T) *copyFileRangeHook {
+ h := new(copyFileRangeHook)
+ h.install()
+ t.Cleanup(h.uninstall)
+ return h
+}
+
+type copyFileRangeHook struct {
+ called bool
+ dstfd int
+ srcfd int
+ remain int64
+
+ original func(dst, src *poll.FD, remain int64) (int64, bool, error)
+}
+
+func (h *copyFileRangeHook) install() {
+ h.original = *PollCopyFileRangeP
+ *PollCopyFileRangeP = func(dst, src *poll.FD, remain int64) (int64, bool, error) {
+ h.called = true
+ h.dstfd = dst.Sysfd
+ h.srcfd = src.Sysfd
+ h.remain = remain
+ return h.original(dst, src, remain)
+ }
+}
+
+func (h *copyFileRangeHook) uninstall() {
+ *PollCopyFileRangeP = h.original
+}
diff --git a/libgo/go/os/readfrom_stub.go b/libgo/go/os/readfrom_stub.go
new file mode 100644
index 0000000..65429d0
--- /dev/null
+++ b/libgo/go/os/readfrom_stub.go
@@ -0,0 +1,13 @@
+// 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.
+
+// +build !linux
+
+package os
+
+import "io"
+
+func (f *File) readFrom(r io.Reader) (n int64, handled bool, err error) {
+ return 0, false, nil
+}
diff --git a/libgo/go/os/removeall_at.go b/libgo/go/os/removeall_at.go
index afee01d..8bbdcf0 100644
--- a/libgo/go/os/removeall_at.go
+++ b/libgo/go/os/removeall_at.go
@@ -9,7 +9,6 @@ package os
import (
"internal/syscall/unix"
"io"
- "runtime"
"syscall"
)
@@ -178,7 +177,7 @@ func openFdAt(dirfd int, name string) (*File, error) {
}
// See comment in openFileNolog.
- if runtime.GOOS == "darwin" && e == syscall.EINTR {
+ if e == syscall.EINTR {
continue
}
diff --git a/libgo/go/os/signal/internal/pty/pty.go b/libgo/go/os/signal/internal/pty/pty.go
index f7d61f9..dd0aee1 100644
--- a/libgo/go/os/signal/internal/pty/pty.go
+++ b/libgo/go/os/signal/internal/pty/pty.go
@@ -50,8 +50,8 @@ func (e *PtyError) Error() string {
func (e *PtyError) Unwrap() error { return e.Errno }
-// Open returns a master pty and the name of the linked slave tty.
-func Open() (master *os.File, slave string, err error) {
+// Open returns a control pty and the name of the linked process tty.
+func Open() (pty *os.File, processTTY string, err error) {
m := posix_openpt(_O_RDWR)
if m < 0 {
return nil, "", ptyError("posix_openpt", syscall.GetErrno())
@@ -74,6 +74,6 @@ func Open() (master *os.File, slave string, err error) {
break
}
}
- slave = string(s)
- return os.NewFile(uintptr(m), "pty-master"), slave, nil
+ processTTY = string(s)
+ return os.NewFile(uintptr(m), "pty"), processTTY, nil
}
diff --git a/libgo/go/os/signal/signal.go b/libgo/go/os/signal/signal.go
index 136dd9c..8e31aa2 100644
--- a/libgo/go/os/signal/signal.go
+++ b/libgo/go/os/signal/signal.go
@@ -122,12 +122,6 @@ func Notify(c chan<- os.Signal, sig ...os.Signal) {
panic("os/signal: Notify using nil channel")
}
- watchSignalLoopOnce.Do(func() {
- if watchSignalLoop != nil {
- go watchSignalLoop()
- }
- })
-
handlers.Lock()
defer handlers.Unlock()
@@ -148,6 +142,14 @@ func Notify(c chan<- os.Signal, sig ...os.Signal) {
h.set(n)
if handlers.ref[n] == 0 {
enableSignal(n)
+
+ // The runtime requires that we enable a
+ // signal before starting the watcher.
+ watchSignalLoopOnce.Do(func() {
+ if watchSignalLoop != nil {
+ go watchSignalLoop()
+ }
+ })
}
handlers.ref[n]++
}
diff --git a/libgo/go/os/signal/signal_cgo_test.go b/libgo/go/os/signal/signal_cgo_test.go
index 3c23090..a117221 100644
--- a/libgo/go/os/signal/signal_cgo_test.go
+++ b/libgo/go/os/signal/signal_cgo_test.go
@@ -19,7 +19,7 @@ import (
"io"
"os"
"os/exec"
- "os/signal/internal/pty"
+ ptypkg "os/signal/internal/pty"
"strconv"
"strings"
"sync"
@@ -71,20 +71,20 @@ func TestTerminalSignal(t *testing.T) {
// The test only fails when using a "slow device," in this
// case a pseudo-terminal.
- master, sname, err := pty.Open()
+ pty, procTTYName, err := ptypkg.Open()
if err != nil {
- ptyErr := err.(*pty.PtyError)
+ ptyErr := err.(*ptypkg.PtyError)
if ptyErr.FuncName == "posix_openpt" && ptyErr.Errno == syscall.EACCES {
t.Skip("posix_openpt failed with EACCES, assuming chroot and skipping")
}
t.Fatal(err)
}
- defer master.Close()
- slave, err := os.OpenFile(sname, os.O_RDWR, 0)
+ defer pty.Close()
+ procTTY, err := os.OpenFile(procTTYName, os.O_RDWR, 0)
if err != nil {
t.Fatal(err)
}
- defer slave.Close()
+ defer procTTY.Close()
// Start an interactive shell.
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
@@ -92,34 +92,34 @@ func TestTerminalSignal(t *testing.T) {
cmd := exec.CommandContext(ctx, bash, "--norc", "--noprofile", "-i")
// Clear HISTFILE so that we don't read or clobber the user's bash history.
cmd.Env = append(os.Environ(), "HISTFILE=")
- cmd.Stdin = slave
- cmd.Stdout = slave
- cmd.Stderr = slave
+ cmd.Stdin = procTTY
+ cmd.Stdout = procTTY
+ cmd.Stderr = procTTY
cmd.SysProcAttr = &syscall.SysProcAttr{
Setsid: true,
Setctty: true,
- Ctty: int(slave.Fd()),
+ Ctty: 0,
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
- if err := slave.Close(); err != nil {
- t.Errorf("closing slave: %v", err)
+ if err := procTTY.Close(); err != nil {
+ t.Errorf("closing procTTY: %v", err)
}
progReady := make(chan bool)
sawPrompt := make(chan bool, 10)
const prompt = "prompt> "
- // Read data from master in the background.
+ // Read data from pty in the background.
var wg sync.WaitGroup
wg.Add(1)
defer wg.Wait()
go func() {
defer wg.Done()
- input := bufio.NewReader(master)
+ input := bufio.NewReader(pty)
var line, handled []byte
for {
b, err := input.ReadByte()
@@ -130,11 +130,11 @@ func TestTerminalSignal(t *testing.T) {
if perr, ok := err.(*os.PathError); ok {
err = perr.Err
}
- // EOF means master is closed.
+ // EOF means pty is closed.
// EIO means child process is done.
- // "file already closed" means deferred close of master has happened.
+ // "file already closed" means deferred close of pty has happened.
if err != io.EOF && err != syscall.EIO && !strings.Contains(err.Error(), "file already closed") {
- t.Logf("error reading from master: %v", err)
+ t.Logf("error reading from pty: %v", err)
}
return
}
@@ -161,7 +161,7 @@ func TestTerminalSignal(t *testing.T) {
}()
// Set the bash prompt so that we can see it.
- if _, err := master.Write([]byte("PS1='" + prompt + "'\n")); err != nil {
+ if _, err := pty.Write([]byte("PS1='" + prompt + "'\n")); err != nil {
t.Fatalf("setting prompt: %v", err)
}
select {
@@ -172,7 +172,7 @@ func TestTerminalSignal(t *testing.T) {
// Start a small program that reads from stdin
// (namely the code at the top of this function).
- if _, err := master.Write([]byte("GO_TEST_TERMINAL_SIGNALS=1 " + os.Args[0] + " -test.run=TestTerminalSignal\n")); err != nil {
+ if _, err := pty.Write([]byte("GO_TEST_TERMINAL_SIGNALS=1 " + os.Args[0] + " -test.run=TestTerminalSignal\n")); err != nil {
t.Fatal(err)
}
@@ -190,7 +190,7 @@ func TestTerminalSignal(t *testing.T) {
time.Sleep(pause)
// Send a ^Z to stop the program.
- if _, err := master.Write([]byte{26}); err != nil {
+ if _, err := pty.Write([]byte{26}); err != nil {
t.Fatalf("writing ^Z to pty: %v", err)
}
@@ -202,7 +202,7 @@ func TestTerminalSignal(t *testing.T) {
}
// Restart the stopped program.
- if _, err := master.Write([]byte("fg\n")); err != nil {
+ if _, err := pty.Write([]byte("fg\n")); err != nil {
t.Fatalf("writing %q to pty: %v", "fg", err)
}
@@ -217,7 +217,7 @@ func TestTerminalSignal(t *testing.T) {
// Write some data for the program to read,
// which should cause it to exit.
- if _, err := master.Write([]byte{'\n'}); err != nil {
+ if _, err := pty.Write([]byte{'\n'}); err != nil {
t.Fatalf("writing %q to pty: %v", "\n", err)
}
@@ -229,7 +229,7 @@ func TestTerminalSignal(t *testing.T) {
}
// Exit the shell with the program's exit status.
- if _, err := master.Write([]byte("exit $?\n")); err != nil {
+ if _, err := pty.Write([]byte("exit $?\n")); err != nil {
t.Fatalf("writing %q to pty: %v", "exit", err)
}
diff --git a/libgo/go/os/signal/signal_plan9.go b/libgo/go/os/signal/signal_plan9.go
index 8408607..7d487151 100644
--- a/libgo/go/os/signal/signal_plan9.go
+++ b/libgo/go/os/signal/signal_plan9.go
@@ -11,7 +11,7 @@ import (
var sigtab = make(map[os.Signal]int)
-// In sig.s; jumps to runtime.
+// Defined by the runtime package.
func signal_disable(uint32)
func signal_enable(uint32)
func signal_ignore(uint32)
@@ -19,8 +19,6 @@ func signal_ignored(uint32) bool
func signal_recv() string
func init() {
- signal_enable(0) // first call - initialize
-
watchSignalLoop = loop
}
diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go
index 0708d4c..98a1cc1 100644
--- a/libgo/go/os/signal/signal_test.go
+++ b/libgo/go/os/signal/signal_test.go
@@ -22,51 +22,87 @@ import (
"time"
)
-var testDeadline time.Time
-
-func TestMain(m *testing.M) {
- flag.Parse()
-
- // TODO(golang.org/issue/28135): Remove this setup and use t.Deadline instead.
- timeoutFlag := flag.Lookup("test.timeout")
- if timeoutFlag != nil {
- if d := timeoutFlag.Value.(flag.Getter).Get().(time.Duration); d != 0 {
- testDeadline = time.Now().Add(d)
+// settleTime is an upper bound on how long we expect signals to take to be
+// delivered. Lower values make the test faster, but also flakier — especially
+// on heavily loaded systems.
+//
+// The current value is set based on flakes observed in the Go builders.
+var settleTime = 100 * time.Millisecond
+
+func init() {
+ if testenv.Builder() == "solaris-amd64-oraclerel" {
+ // The solaris-amd64-oraclerel builder has been observed to time out in
+ // TestNohup even with a 250ms settle time.
+ //
+ // Use a much longer settle time on that builder to try to suss out whether
+ // the test is flaky due to builder slowness (which may mean we need a
+ // longer GO_TEST_TIMEOUT_SCALE) or due to a dropped signal (which may
+ // instead need a test-skip and upstream bug filed against the Solaris
+ // kernel).
+ //
+ // This constant is chosen so as to make the test as generous as possible
+ // while still reliably completing within 3 minutes in non-short mode.
+ //
+ // See https://golang.org/issue/33174.
+ settleTime = 11 * time.Second
+ } else if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
+ if scale, err := strconv.Atoi(s); err == nil {
+ settleTime *= time.Duration(scale)
}
}
-
- os.Exit(m.Run())
}
func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
+ t.Helper()
waitSig1(t, c, sig, false)
}
func waitSigAll(t *testing.T, c <-chan os.Signal, sig os.Signal) {
+ t.Helper()
waitSig1(t, c, sig, true)
}
func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) {
+ t.Helper()
+
// Sleep multiple times to give the kernel more tries to
// deliver the signal.
- for i := 0; i < 10; i++ {
+ start := time.Now()
+ timer := time.NewTimer(settleTime / 10)
+ defer timer.Stop()
+ // If the caller notified for all signals on c, filter out SIGURG,
+ // which is used for runtime preemption and can come at unpredictable times.
+ // General user code should filter out all unexpected signals instead of just
+ // SIGURG, but since os/signal is tightly coupled to the runtime it seems
+ // appropriate to be stricter here.
+ for time.Since(start) < settleTime {
select {
case s := <-c:
- // If the caller notified for all signals on
- // c, filter out SIGURG, which is used for
- // runtime preemption and can come at
- // unpredictable times.
- if all && s == syscall.SIGURG {
- continue
+ if s == sig {
+ return
}
- if s != sig {
+ if !all || s != syscall.SIGURG {
t.Fatalf("signal was %v, want %v", s, sig)
}
- return
-
- case <-time.After(100 * time.Millisecond):
+ case <-timer.C:
+ timer.Reset(settleTime / 10)
}
}
- t.Fatalf("timeout waiting for %v", sig)
+ t.Fatalf("timeout after %v waiting for %v", settleTime, sig)
+}
+
+// quiesce waits until we can be reasonably confident that all pending signals
+// have been delivered by the OS.
+func quiesce() {
+ // The kernel will deliver a signal as a thread returns
+ // from a syscall. If the only active thread is sleeping,
+ // and the system is busy, the kernel may not get around
+ // to waking up a thread to catch the signal.
+ // We try splitting up the sleep to give the kernel
+ // many chances to deliver the signal.
+ start := time.Now()
+ for time.Since(start) < settleTime {
+ time.Sleep(settleTime / 10)
+ }
}
// Test that basic signal handling works.
@@ -112,50 +148,39 @@ func TestStress(t *testing.T) {
dur = 100 * time.Millisecond
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
- done := make(chan bool)
- finished := make(chan bool)
- go func() {
- sig := make(chan os.Signal, 1)
- Notify(sig, syscall.SIGUSR1)
- defer Stop(sig)
- Loop:
- for {
- select {
- case <-sig:
- case <-done:
- break Loop
- }
- }
- finished <- true
- }()
+
+ sig := make(chan os.Signal, 1)
+ Notify(sig, syscall.SIGUSR1)
+
go func() {
- Loop:
+ stop := time.After(dur)
for {
select {
- case <-done:
- break Loop
+ case <-stop:
+ // Allow enough time for all signals to be delivered before we stop
+ // listening for them.
+ quiesce()
+ Stop(sig)
+ // According to its documentation, “[w]hen Stop returns, it in
+ // guaranteed that c will receive no more signals.” So we can safely
+ // close sig here: if there is a send-after-close race here, that is a
+ // bug in Stop and we would like to detect it.
+ close(sig)
+ return
+
default:
syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
runtime.Gosched()
}
}
- finished <- true
}()
- time.Sleep(dur)
- close(done)
- <-finished
- <-finished
- // When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
- // into subsequent TestSignal() causing failure.
- // Sleep for a while to reduce the possibility of the failure.
- time.Sleep(10 * time.Millisecond)
+
+ for range sig {
+ // Receive signals until the sender closes sig.
+ }
}
func testCancel(t *testing.T, ignore bool) {
- // Send SIGWINCH. By default this signal should be ignored.
- syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
- time.Sleep(100 * time.Millisecond)
-
// Ask to be notified on c1 when a SIGWINCH is received.
c1 := make(chan os.Signal, 1)
Notify(c1, syscall.SIGWINCH)
@@ -175,25 +200,16 @@ func testCancel(t *testing.T, ignore bool) {
waitSig(t, c2, syscall.SIGHUP)
// Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP.
+ // Either way, this should undo both calls to Notify above.
if ignore {
Ignore(syscall.SIGWINCH, syscall.SIGHUP)
+ // Don't bother deferring a call to Reset: it is documented to undo Notify,
+ // but its documentation says nothing about Ignore, and (as of the time of
+ // writing) it empirically does not undo an Ignore.
} else {
Reset(syscall.SIGWINCH, syscall.SIGHUP)
}
- // At this point we do not expect any further signals on c1.
- // However, it is just barely possible that the initial SIGWINCH
- // at the start of this function was delivered after we called
- // Notify on c1. In that case the waitSig for SIGWINCH may have
- // picked up that initial SIGWINCH, and the second SIGWINCH may
- // then have been delivered on the channel. This sequence of events
- // may have caused issue 15661.
- // So, read any possible signal from the channel now.
- select {
- case <-c1:
- default:
- }
-
// Send this process a SIGWINCH. It should be ignored.
syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
@@ -202,22 +218,28 @@ func testCancel(t *testing.T, ignore bool) {
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
}
+ quiesce()
+
select {
case s := <-c1:
- t.Fatalf("unexpected signal %v", s)
- case <-time.After(100 * time.Millisecond):
+ t.Errorf("unexpected signal %v", s)
+ default:
// nothing to read - good
}
select {
case s := <-c2:
- t.Fatalf("unexpected signal %v", s)
- case <-time.After(100 * time.Millisecond):
+ t.Errorf("unexpected signal %v", s)
+ default:
// nothing to read - good
}
- // Reset the signal handlers for all signals.
- Reset()
+ // One or both of the signals may have been blocked for this process
+ // by the calling process.
+ // Discard any queued signals now to avoid interfering with other tests.
+ Notify(c1, syscall.SIGWINCH)
+ Notify(c2, syscall.SIGHUP)
+ quiesce()
}
// Test that Reset cancels registration for listed signals on all channels.
@@ -289,7 +311,10 @@ func TestDetectNohup(t *testing.T) {
}
}
-var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
+var (
+ sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
+ dieFromSighup = flag.Bool("die_from_sighup", false, "wait to die from uncaught SIGHUP")
+)
// Test that Stop cancels the channel's registrations.
func TestStop(t *testing.T) {
@@ -300,59 +325,74 @@ func TestStop(t *testing.T) {
}
for _, sig := range sigs {
- // Send the signal.
- // If it's SIGWINCH, we should not see it.
- // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
- if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) {
- syscall.Kill(syscall.Getpid(), sig)
- }
-
- // The kernel will deliver a signal as a thread returns
- // from a syscall. If the only active thread is sleeping,
- // and the system is busy, the kernel may not get around
- // to waking up a thread to catch the signal.
- // We try splitting up the sleep to give the kernel
- // another chance to deliver the signal.
- time.Sleep(50 * time.Millisecond)
- time.Sleep(50 * time.Millisecond)
-
- // Ask for signal
- c := make(chan os.Signal, 1)
- Notify(c, sig)
- defer Stop(c)
-
- // Send this process that signal
- syscall.Kill(syscall.Getpid(), sig)
- waitSig(t, c, sig)
+ sig := sig
+ t.Run(fmt.Sprint(sig), func(t *testing.T) {
+ // When calling Notify with a specific signal,
+ // independent signals should not interfere with each other,
+ // and we end up needing to wait for signals to quiesce a lot.
+ // Test the three different signals concurrently.
+ t.Parallel()
+
+ // If the signal is not ignored, send the signal before registering a
+ // channel to verify the behavior of the default Go handler.
+ // If it's SIGWINCH or SIGUSR1 we should not see it.
+ // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
+ mayHaveBlockedSignal := false
+ if !Ignored(sig) && (sig != syscall.SIGHUP || *sendUncaughtSighup == 1) {
+ syscall.Kill(syscall.Getpid(), sig)
+ quiesce()
+
+ // We don't know whether sig is blocked for this process; see
+ // https://golang.org/issue/38165. Assume that it could be.
+ mayHaveBlockedSignal = true
+ }
- Stop(c)
- time.Sleep(50 * time.Millisecond)
- select {
- case s := <-c:
- t.Fatalf("unexpected signal %v", s)
- case <-time.After(50 * time.Millisecond):
- // nothing to read - good
- }
+ // Ask for signal
+ c := make(chan os.Signal, 1)
+ Notify(c, sig)
- // Send the signal.
- // If it's SIGWINCH, we should not see it.
- // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
- if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
+ // Send this process the signal again.
syscall.Kill(syscall.Getpid(), sig)
- }
+ waitSig(t, c, sig)
+
+ if mayHaveBlockedSignal {
+ // We may have received a queued initial signal in addition to the one
+ // that we sent after Notify. If so, waitSig may have observed that
+ // initial signal instead of the second one, and we may need to wait for
+ // the second signal to clear. Do that now.
+ quiesce()
+ select {
+ case <-c:
+ default:
+ }
+ }
- time.Sleep(50 * time.Millisecond)
- select {
- case s := <-c:
- t.Fatalf("unexpected signal %v", s)
- case <-time.After(50 * time.Millisecond):
- // nothing to read - good
- }
+ // Stop watching for the signal and send it again.
+ // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
+ Stop(c)
+ if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
+ syscall.Kill(syscall.Getpid(), sig)
+ quiesce()
+
+ select {
+ case s := <-c:
+ t.Errorf("unexpected signal %v", s)
+ default:
+ // nothing to read - good
+ }
+
+ // If we're going to receive a signal, it has almost certainly been
+ // received by now. However, it may have been blocked for this process —
+ // we don't know. Explicitly unblock it and wait for it to clear now.
+ Notify(c, sig)
+ quiesce()
+ Stop(c)
+ }
+ })
}
}
-// Test that when run under nohup, an uncaught SIGHUP does not kill the program,
-// but a
+// Test that when run under nohup, an uncaught SIGHUP does not kill the program.
func TestNohup(t *testing.T) {
// Ugly: ask for SIGHUP so that child will not have no-hup set
// even if test is running under nohup environment.
@@ -371,12 +411,38 @@ func TestNohup(t *testing.T) {
//
// Both should fail without nohup and succeed with nohup.
+ var subTimeout time.Duration
+
+ var wg sync.WaitGroup
+ wg.Add(2)
+ if deadline, ok := t.Deadline(); ok {
+ subTimeout = time.Until(deadline)
+ subTimeout -= subTimeout / 10 // Leave 10% headroom for propagating output.
+ }
for i := 1; i <= 2; i++ {
- out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
- if err == nil {
- t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out)
- }
+ i := i
+ go t.Run(fmt.Sprintf("uncaught-%d", i), func(t *testing.T) {
+ defer wg.Done()
+
+ args := []string{
+ "-test.v",
+ "-test.run=TestStop",
+ "-send_uncaught_sighup=" + strconv.Itoa(i),
+ "-die_from_sighup",
+ }
+ if subTimeout != 0 {
+ args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
+ }
+ out, err := exec.Command(os.Args[0], args...).CombinedOutput()
+
+ if err == nil {
+ t.Errorf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out)
+ } else {
+ t.Logf("test with -send_uncaught_sighup=%d failed as expected.\nError: %v\nOutput:\n%s", i, err, out)
+ }
+ })
}
+ wg.Wait()
Stop(c)
@@ -387,21 +453,46 @@ func TestNohup(t *testing.T) {
}
// Again, this time with nohup, assuming we can find it.
- _, err := os.Stat("/usr/bin/nohup")
+ _, err := exec.LookPath("nohup")
if err != nil {
t.Skip("cannot find nohup; skipping second half of test")
}
+ wg.Add(2)
+ if deadline, ok := t.Deadline(); ok {
+ subTimeout = time.Until(deadline)
+ subTimeout -= subTimeout / 10 // Leave 10% headroom for propagating output.
+ }
for i := 1; i <= 2; i++ {
- os.Remove("nohup.out")
- out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
+ i := i
+ go t.Run(fmt.Sprintf("nohup-%d", i), func(t *testing.T) {
+ defer wg.Done()
- data, _ := ioutil.ReadFile("nohup.out")
- os.Remove("nohup.out")
- if err != nil {
- t.Fatalf("ran test with -send_uncaught_sighup=%d under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", i, err, out, data)
- }
+ // POSIX specifies that nohup writes to a file named nohup.out if standard
+ // output is a terminal. However, for an exec.Command, standard output is
+ // not a terminal — so we don't need to read or remove that file (and,
+ // indeed, cannot even create it if the current user is unable to write to
+ // GOROOT/src, such as when GOROOT is installed and owned by root).
+
+ args := []string{
+ os.Args[0],
+ "-test.v",
+ "-test.run=TestStop",
+ "-send_uncaught_sighup=" + strconv.Itoa(i),
+ }
+ if subTimeout != 0 {
+ args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
+ }
+ out, err := exec.Command("nohup", args...).CombinedOutput()
+
+ if err != nil {
+ t.Errorf("ran test with -send_uncaught_sighup=%d under nohup and it failed: expected success.\nError: %v\nOutput:\n%s", i, err, out)
+ } else {
+ t.Logf("ran test with -send_uncaught_sighup=%d under nohup.\nOutput:\n%s", i, out)
+ }
+ })
}
+ wg.Wait()
}
// Test that SIGCONT works (issue 8953).
@@ -416,7 +507,7 @@ func TestSIGCONT(t *testing.T) {
// Test race between stopping and receiving a signal (issue 14571).
func TestAtomicStop(t *testing.T) {
if os.Getenv("GO_TEST_ATOMIC_STOP") != "" {
- atomicStopTestProgram()
+ atomicStopTestProgram(t)
t.Fatal("atomicStopTestProgram returned")
}
@@ -438,8 +529,8 @@ func TestAtomicStop(t *testing.T) {
const execs = 10
for i := 0; i < execs; i++ {
timeout := "0"
- if !testDeadline.IsZero() {
- timeout = testDeadline.Sub(time.Now()).String()
+ if deadline, ok := t.Deadline(); ok {
+ timeout = time.Until(deadline).String()
}
cmd := exec.Command(os.Args[0], "-test.run=TestAtomicStop", "-test.timeout="+timeout)
cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1")
@@ -478,7 +569,7 @@ func TestAtomicStop(t *testing.T) {
// atomicStopTestProgram is run in a subprocess by TestAtomicStop.
// It tries to trigger a signal delivery race. This function should
// either catch a signal or die from it.
-func atomicStopTestProgram() {
+func atomicStopTestProgram(t *testing.T) {
// This test won't work if SIGINT is ignored here.
if Ignored(syscall.SIGINT) {
fmt.Println("SIGINT is ignored")
@@ -488,10 +579,10 @@ func atomicStopTestProgram() {
const tries = 10
timeout := 2 * time.Second
- if !testDeadline.IsZero() {
+ if deadline, ok := t.Deadline(); ok {
// Give each try an equal slice of the deadline, with one slice to spare for
// cleanup.
- timeout = testDeadline.Sub(time.Now()) / (tries + 1)
+ timeout = time.Until(deadline) / (tries + 1)
}
pid := syscall.Getpid()
@@ -541,43 +632,45 @@ func TestTime(t *testing.T) {
dur = 100 * time.Millisecond
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
- done := make(chan bool)
- finished := make(chan bool)
- go func() {
- sig := make(chan os.Signal, 1)
- Notify(sig, syscall.SIGUSR1)
- defer Stop(sig)
- Loop:
- for {
- select {
- case <-sig:
- case <-done:
- break Loop
- }
- }
- finished <- true
- }()
+
+ sig := make(chan os.Signal, 1)
+ Notify(sig, syscall.SIGUSR1)
+
+ stop := make(chan struct{})
go func() {
- Loop:
for {
select {
- case <-done:
- break Loop
+ case <-stop:
+ // Allow enough time for all signals to be delivered before we stop
+ // listening for them.
+ quiesce()
+ Stop(sig)
+ // According to its documentation, “[w]hen Stop returns, it in
+ // guaranteed that c will receive no more signals.” So we can safely
+ // close sig here: if there is a send-after-close race, that is a bug in
+ // Stop and we would like to detect it.
+ close(sig)
+ return
+
default:
syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
runtime.Gosched()
}
}
- finished <- true
}()
+
+ done := make(chan struct{})
+ go func() {
+ for range sig {
+ // Receive signals until the sender closes sig.
+ }
+ close(done)
+ }()
+
t0 := time.Now()
for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() {
} // hammering on getting time
- close(done)
- <-finished
- <-finished
- // When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
- // into subsequent TestSignal() causing failure.
- // Sleep for a while to reduce the possibility of the failure.
- time.Sleep(10 * time.Millisecond)
+
+ close(stop)
+ <-done
}
diff --git a/libgo/go/os/signal/signal_unix.go b/libgo/go/os/signal/signal_unix.go
index 532c0b5..c18db0c 100644
--- a/libgo/go/os/signal/signal_unix.go
+++ b/libgo/go/os/signal/signal_unix.go
@@ -25,8 +25,6 @@ func loop() {
}
func init() {
- signal_enable(0) // first call - initialize
-
watchSignalLoop = loop
}
diff --git a/libgo/go/os/testdata/issue37161/a b/libgo/go/os/testdata/issue37161/a
new file mode 100644
index 0000000..78981922
--- /dev/null
+++ b/libgo/go/os/testdata/issue37161/a
@@ -0,0 +1 @@
+a
diff --git a/libgo/go/os/testdata/issue37161/b b/libgo/go/os/testdata/issue37161/b
new file mode 100644
index 0000000..6178079
--- /dev/null
+++ b/libgo/go/os/testdata/issue37161/b
@@ -0,0 +1 @@
+b
diff --git a/libgo/go/os/testdata/issue37161/c b/libgo/go/os/testdata/issue37161/c
new file mode 100644
index 0000000..f2ad6c7
--- /dev/null
+++ b/libgo/go/os/testdata/issue37161/c
@@ -0,0 +1 @@
+c
diff --git a/libgo/go/os/timeout_test.go b/libgo/go/os/timeout_test.go
index 0fe03fa..99b94c2 100644
--- a/libgo/go/os/timeout_test.go
+++ b/libgo/go/os/timeout_test.go
@@ -10,7 +10,6 @@ package os_test
import (
"fmt"
- "internal/poll"
"io"
"io/ioutil"
"math/rand"
@@ -57,9 +56,9 @@ var readTimeoutTests = []struct {
}{
// Tests that read deadlines work, even if there's data ready
// to be read.
- {-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
+ {-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
- {50 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
+ {50 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
}
func TestReadTimeout(t *testing.T) {
@@ -85,7 +84,7 @@ func TestReadTimeout(t *testing.T) {
for {
n, err := r.Read(b[:])
if xerr != nil {
- if !os.IsTimeout(err) {
+ if !isDeadlineExceeded(err) {
t.Fatalf("#%d/%d: %v", i, j, err)
}
}
@@ -148,9 +147,9 @@ var writeTimeoutTests = []struct {
}{
// Tests that write deadlines work, even if there's buffer
// space available to write.
- {-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
+ {-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
- {10 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
+ {10 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
}
func TestWriteTimeout(t *testing.T) {
@@ -172,7 +171,7 @@ func TestWriteTimeout(t *testing.T) {
for {
n, err := w.Write([]byte("WRITE TIMEOUT TEST"))
if xerr != nil {
- if !os.IsTimeout(err) {
+ if !isDeadlineExceeded(err) {
t.Fatalf("%d: %v", j, err)
}
}
@@ -246,7 +245,7 @@ func timeoutReader(r *os.File, d, min, max time.Duration, ch chan<- error) {
var n int
n, err = r.Read(b)
t1 := time.Now()
- if n != 0 || err == nil || !os.IsTimeout(err) {
+ if n != 0 || err == nil || !isDeadlineExceeded(err) {
err = fmt.Errorf("Read did not return (0, timeout): (%d, %v)", n, err)
return
}
@@ -275,7 +274,7 @@ func TestReadTimeoutFluctuation(t *testing.T) {
case <-max.C:
t.Fatal("Read took over 1s; expected 0.1s")
case err := <-ch:
- if !os.IsTimeout(err) {
+ if !isDeadlineExceeded(err) {
t.Fatal(err)
}
}
@@ -297,7 +296,7 @@ func timeoutWriter(w *os.File, d, min, max time.Duration, ch chan<- error) {
}
}
t1 := time.Now()
- if err == nil || !os.IsTimeout(err) {
+ if err == nil || !isDeadlineExceeded(err) {
err = fmt.Errorf("Write did not return (any, timeout): (%d, %v)", n, err)
return
}
@@ -327,7 +326,7 @@ func TestWriteTimeoutFluctuation(t *testing.T) {
case <-max.C:
t.Fatalf("Write took over %v; expected 0.1s", d)
case err := <-ch:
- if !os.IsTimeout(err) {
+ if !isDeadlineExceeded(err) {
t.Fatal(err)
}
}
@@ -438,7 +437,7 @@ func testVariousDeadlines(t *testing.T) {
select {
case res := <-actvch:
- if os.IsTimeout(res.err) {
+ if !isDeadlineExceeded(err) {
t.Logf("good client timeout after %v, reading %d bytes", res.d, res.n)
} else {
t.Fatalf("client Copy = %d, %v; want timeout", res.n, res.err)
@@ -494,7 +493,7 @@ func TestReadWriteDeadlineRace(t *testing.T) {
var b [1]byte
for i := 0; i < N; i++ {
_, err := r.Read(b[:])
- if err != nil && !os.IsTimeout(err) {
+ if err != nil && !isDeadlineExceeded(err) {
t.Error("Read returned non-timeout error", err)
}
}
@@ -504,7 +503,7 @@ func TestReadWriteDeadlineRace(t *testing.T) {
var b [1]byte
for i := 0; i < N; i++ {
_, err := w.Write(b[:])
- if err != nil && !os.IsTimeout(err) {
+ if err != nil && !isDeadlineExceeded(err) {
t.Error("Write returned non-timeout error", err)
}
}
@@ -541,7 +540,7 @@ func TestRacyRead(t *testing.T) {
_, err := r.Read(b1)
copy(b1, b2) // Mutate b1 to trigger potential race
if err != nil {
- if !os.IsTimeout(err) {
+ if !isDeadlineExceeded(err) {
t.Error(err)
}
r.SetReadDeadline(time.Now().Add(time.Millisecond))
@@ -580,7 +579,7 @@ func TestRacyWrite(t *testing.T) {
_, err := w.Write(b1)
copy(b1, b2) // Mutate b1 to trigger potential race
if err != nil {
- if !os.IsTimeout(err) {
+ if !isDeadlineExceeded(err) {
t.Error(err)
}
w.SetWriteDeadline(time.Now().Add(time.Millisecond))
diff --git a/libgo/go/os/user/lookup_windows.go b/libgo/go/os/user/lookup_windows.go
index faaddd2..f65773ce 100644
--- a/libgo/go/os/user/lookup_windows.go
+++ b/libgo/go/os/user/lookup_windows.go
@@ -44,7 +44,7 @@ func lookupFullNameServer(servername, username string) (string, error) {
}
defer syscall.NetApiBufferFree(p)
i := (*syscall.UserInfo10)(unsafe.Pointer(p))
- return windows.UTF16PtrToString(i.FullName, 1024), nil
+ return windows.UTF16PtrToString(i.FullName), nil
}
func lookupFullName(domain, username, domainAndUser string) (string, error) {
@@ -167,7 +167,7 @@ func listGroupsForUsernameAndDomain(username, domain string) ([]string, error) {
if entry.Name == nil {
continue
}
- sid, err := lookupGroupName(windows.UTF16PtrToString(entry.Name, 1024))
+ sid, err := lookupGroupName(windows.UTF16PtrToString(entry.Name))
if err != nil {
return nil, err
}
diff --git a/libgo/go/os/wait_wait6.go b/libgo/go/os/wait_wait6.go
index 45bf649..5420b2d 100644
--- a/libgo/go/os/wait_wait6.go
+++ b/libgo/go/os/wait_wait6.go
@@ -18,15 +18,20 @@ const _P_PID = 0
// It does not actually call p.Wait.
func (p *Process) blockUntilWaitable() (bool, error) {
var errno syscall.Errno
- // The arguments on 32-bit FreeBSD look like the following:
- // - freebsd32_wait6_args{ idtype, id1, id2, status, options, wrusage, info } or
- // - freebsd32_wait6_args{ idtype, pad, id1, id2, status, options, wrusage, info } when PAD64_REQUIRED=1 on ARM, MIPS or PowerPC
- if runtime.GOARCH == "386" {
- _, _, errno = syscall.Syscall9(syscall.SYS_WAIT6, _P_PID, uintptr(p.Pid), 0, 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0, 0, 0)
- } else if runtime.GOARCH == "arm" {
- _, _, errno = syscall.Syscall9(syscall.SYS_WAIT6, _P_PID, 0, uintptr(p.Pid), 0, 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0, 0)
- } else {
- _, _, errno = syscall.Syscall6(syscall.SYS_WAIT6, _P_PID, uintptr(p.Pid), 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0)
+ for {
+ // The arguments on 32-bit FreeBSD look like the following:
+ // - freebsd32_wait6_args{ idtype, id1, id2, status, options, wrusage, info } or
+ // - freebsd32_wait6_args{ idtype, pad, id1, id2, status, options, wrusage, info } when PAD64_REQUIRED=1 on ARM, MIPS or PowerPC
+ if runtime.GOARCH == "386" {
+ _, _, errno = syscall.Syscall9(syscall.SYS_WAIT6, _P_PID, uintptr(p.Pid), 0, 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0, 0, 0)
+ } else if runtime.GOARCH == "arm" {
+ _, _, errno = syscall.Syscall9(syscall.SYS_WAIT6, _P_PID, 0, uintptr(p.Pid), 0, 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0, 0)
+ } else {
+ _, _, errno = syscall.Syscall6(syscall.SYS_WAIT6, _P_PID, uintptr(p.Pid), 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0)
+ }
+ if errno != syscall.EINTR {
+ break
+ }
}
runtime.KeepAlive(p)
if errno != 0 {
diff --git a/libgo/go/os/wait_waitid.go b/libgo/go/os/wait_waitid.go
index 4bb77f9..2c39f9b 100644
--- a/libgo/go/os/wait_waitid.go
+++ b/libgo/go/os/wait_waitid.go
@@ -23,12 +23,18 @@ const _P_PID = 1
func (p *Process) blockUntilWaitable() (bool, error) {
// The waitid system call expects a pointer to a siginfo_t,
// which is 128 bytes on all GNU/Linux systems.
- // On Darwin, it requires greater than or equal to 64 bytes
- // for darwin/{386,arm} and 104 bytes for darwin/amd64.
+ // On darwin/amd64, it requires 104 bytes.
// We don't care about the values it returns.
var siginfo [16]uint64
psig := &siginfo[0]
- r, _, e := syscall.Syscall6(syscall.SYS_WAITID, _P_PID, uintptr(p.Pid), uintptr(unsafe.Pointer(psig)), syscall.WEXITED|syscall.WNOWAIT, 0, 0)
+ var r uintptr
+ var e syscall.Errno
+ for {
+ r, _, e = syscall.Syscall6(syscall.SYS_WAITID, _P_PID, uintptr(p.Pid), uintptr(unsafe.Pointer(psig)), syscall.WEXITED|syscall.WNOWAIT, 0, 0)
+ if e != syscall.EINTR {
+ break
+ }
+ }
runtime.KeepAlive(p)
// Check r as well as e because syscall.Syscall6 currently
// just returns errno, and the SIGCHLD signal handler may