diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-07-27 22:27:54 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-08-01 11:21:40 -0700 |
commit | f75af8c1464e948b5e166cf5ab09ebf0d82fc253 (patch) | |
tree | 3ba3299859b504bdeb477727471216bd094a0191 /libgo/go/os | |
parent | 75a23e59031fe673fc3b2e60fd1fe5f4c70ecb85 (diff) | |
download | gcc-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')
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 |