aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2021-01-27 17:55:50 -0800
committerIan Lance Taylor <iant@golang.org>2021-01-29 11:04:55 -0800
commit726b7aa004d6885388a76521222602b8552a41ee (patch)
tree5179037ef840a43dcea0f3be4e07dbcbcfcb2c4a /libgo/go/os
parent91a95ad2ae0e0f2fa953fafe55ff2ec32c8277d5 (diff)
downloadgcc-726b7aa004d6885388a76521222602b8552a41ee.zip
gcc-726b7aa004d6885388a76521222602b8552a41ee.tar.gz
gcc-726b7aa004d6885388a76521222602b8552a41ee.tar.bz2
libgo: update to Go1.16rc1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/287493
Diffstat (limited to 'libgo/go/os')
-rw-r--r--libgo/go/os/file_plan9.go10
-rw-r--r--libgo/go/os/file_unix.go8
-rw-r--r--libgo/go/os/os_test.go45
-rw-r--r--libgo/go/os/os_windows_test.go11
-rw-r--r--libgo/go/os/signal/signal_linux_test.go42
-rw-r--r--libgo/go/os/signal/signal_test.go102
-rw-r--r--libgo/go/os/tempfile.go21
-rw-r--r--libgo/go/os/testdata/dirfs/a0
-rw-r--r--libgo/go/os/testdata/dirfs/b0
-rw-r--r--libgo/go/os/testdata/dirfs/dir/x0
10 files changed, 171 insertions, 68 deletions
diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go
index bbc7328..4f384e9 100644
--- a/libgo/go/os/file_plan9.go
+++ b/libgo/go/os/file_plan9.go
@@ -336,16 +336,6 @@ func hasPrefix(s, prefix string) bool {
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
-// LastIndexByte from the strings package.
-func lastIndex(s string, sep byte) int {
- for i := len(s) - 1; i >= 0; i-- {
- if s[i] == sep {
- return i
- }
- }
- return -1
-}
-
func rename(oldname, newname string) error {
dirname := oldname[:lastIndex(oldname, '/')+1]
if hasPrefix(newname, dirname) {
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index 8315887..5888d48 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -66,6 +66,10 @@ type file struct {
// making it invalid; see runtime.SetFinalizer for more information on when
// a finalizer might be run. On Unix systems this will cause the SetDeadline
// methods to stop working.
+// Because file descriptors can be reused, the returned file descriptor may
+// only be closed through the Close method of f, or by its finalizer during
+// garbage collection. Otherwise, during garbage collection the finalizer
+// may close an unrelated file descriptor with the same (reused) number.
//
// As an alternative, see the f.SyscallConn method.
func (f *File) Fd() uintptr {
@@ -90,6 +94,10 @@ func (f *File) Fd() uintptr {
// descriptor. On Unix systems, if the file descriptor is in
// non-blocking mode, NewFile will attempt to return a pollable File
// (one for which the SetDeadline methods work).
+//
+// After passing it to NewFile, fd may become invalid under the same
+// conditions described in the comments of the Fd method, and the same
+// constraints apply.
func NewFile(fd uintptr, name string) *File {
kind := kindNewFile
if nb, err := unix.IsNonblock(int(fd)); err == nil && nb {
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
index ccef141..40ba056 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -11,6 +11,7 @@ import (
"fmt"
"internal/testenv"
"io"
+ "io/fs"
"os"
. "os"
osexec "os/exec"
@@ -2300,6 +2301,7 @@ func TestLongPath(t *testing.T) {
func testKillProcess(t *testing.T, processKiller func(p *Process)) {
testenv.MustHaveExec(t)
+ t.Parallel()
// Re-exec the test binary itself to emulate "sleep 1".
cmd := osexec.Command(Args[0], "-test.run", "TestSleep")
@@ -2307,14 +2309,15 @@ func testKillProcess(t *testing.T, processKiller func(p *Process)) {
if err != nil {
t.Fatalf("Failed to start test process: %v", err)
}
- go func() {
- time.Sleep(100 * time.Millisecond)
- processKiller(cmd.Process)
+
+ defer func() {
+ if err := cmd.Wait(); err == nil {
+ t.Errorf("Test process succeeded, but expected to fail")
+ }
}()
- err = cmd.Wait()
- if err == nil {
- t.Errorf("Test process succeeded, but expected to fail")
- }
+
+ time.Sleep(100 * time.Millisecond)
+ processKiller(cmd.Process)
}
// TestSleep emulates "sleep 1". It is a helper for testKillProcess, so we
@@ -2689,7 +2692,33 @@ func TestOpenFileKeepsPermissions(t *testing.T) {
}
func TestDirFS(t *testing.T) {
- if err := fstest.TestFS(DirFS("./signal"), "signal.go", "internal/pty/pty.go"); err != nil {
+ // On Windows, we force the MFT to update by reading the actual metadata from GetFileInformationByHandle and then
+ // explicitly setting that. Otherwise it might get out of sync with FindFirstFile. See golang.org/issues/42637.
+ if runtime.GOOS == "windows" {
+ if err := filepath.WalkDir("./testdata/dirfs", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ t.Fatal(err)
+ }
+ info, err := d.Info()
+ if err != nil {
+ t.Fatal(err)
+ }
+ stat, err := Stat(path) // This uses GetFileInformationByHandle internally.
+ if err != nil {
+ t.Fatal(err)
+ }
+ if stat.ModTime() == info.ModTime() {
+ return nil
+ }
+ if err := Chtimes(path, stat.ModTime(), stat.ModTime()); err != nil {
+ t.Log(err) // We only log, not die, in case the test directory is not writable.
+ }
+ return nil
+ }); err != nil {
+ t.Fatal(err)
+ }
+ }
+ if err := fstest.TestFS(DirFS("./testdata/dirfs"), "a", "b", "dir/x"); err != nil {
t.Fatal(err)
}
}
diff --git a/libgo/go/os/os_windows_test.go b/libgo/go/os/os_windows_test.go
index 8d1d1f6..b0929b4 100644
--- a/libgo/go/os/os_windows_test.go
+++ b/libgo/go/os/os_windows_test.go
@@ -692,7 +692,16 @@ func TestReadStdin(t *testing.T) {
poll.ReadConsole = old
}()
- testConsole := os.NewConsoleFile(syscall.Stdin, "test")
+ p, err := syscall.GetCurrentProcess()
+ if err != nil {
+ t.Fatalf("Unable to get handle to current process: %v", err)
+ }
+ var stdinDuplicate syscall.Handle
+ err = syscall.DuplicateHandle(p, syscall.Handle(syscall.Stdin), p, &stdinDuplicate, 0, false, syscall.DUPLICATE_SAME_ACCESS)
+ if err != nil {
+ t.Fatalf("Unable to duplicate stdin: %v", err)
+ }
+ testConsole := os.NewConsoleFile(stdinDuplicate, "test")
var tests = []string{
"abc",
diff --git a/libgo/go/os/signal/signal_linux_test.go b/libgo/go/os/signal/signal_linux_test.go
new file mode 100644
index 0000000..2e553d0
--- /dev/null
+++ b/libgo/go/os/signal/signal_linux_test.go
@@ -0,0 +1,42 @@
+// 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 signal
+
+import (
+ "os"
+ "syscall"
+ "testing"
+ "time"
+)
+
+const prSetKeepCaps = 8
+
+// This test validates that syscall.AllThreadsSyscall() can reliably
+// reach all 'm' (threads) of the nocgo runtime even when one thread
+// is blocked waiting to receive signals from the kernel. This monitors
+// for a regression vs. the fix for #43149.
+func TestAllThreadsSyscallSignals(t *testing.T) {
+ if _, _, err := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, prSetKeepCaps, 0, 0); err == syscall.ENOTSUP {
+ t.Skip("AllThreadsSyscall disabled with cgo")
+ }
+
+ sig := make(chan os.Signal, 1)
+ Notify(sig, os.Interrupt)
+
+ for i := 0; i <= 100; i++ {
+ if _, _, errno := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, prSetKeepCaps, uintptr(i&1), 0); errno != 0 {
+ t.Fatalf("[%d] failed to set KEEP_CAPS=%d: %v", i, i&1, errno)
+ }
+ }
+
+ select {
+ case <-time.After(10 * time.Millisecond):
+ case <-sig:
+ t.Fatal("unexpected signal")
+ }
+ Stop(sig)
+}
diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go
index 66b8327..db94756 100644
--- a/libgo/go/os/signal/signal_test.go
+++ b/libgo/go/os/signal/signal_test.go
@@ -675,22 +675,68 @@ func TestTime(t *testing.T) {
<-done
}
-func TestNotifyContext(t *testing.T) {
- c, stop := NotifyContext(context.Background(), syscall.SIGINT)
- defer stop()
-
- if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
- t.Errorf("c.String() = %q, want %q", got, want)
- }
+var (
+ checkNotifyContext = flag.Bool("check_notify_ctx", false, "if true, TestNotifyContext will fail if SIGINT is not received.")
+ ctxNotifyTimes = flag.Int("ctx_notify_times", 1, "number of times a SIGINT signal should be received")
+)
- syscall.Kill(syscall.Getpid(), syscall.SIGINT)
- select {
- case <-c.Done():
- if got := c.Err(); got != context.Canceled {
- t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
+func TestNotifyContextNotifications(t *testing.T) {
+ if *checkNotifyContext {
+ ctx, _ := NotifyContext(context.Background(), syscall.SIGINT)
+ // We want to make sure not to be calling Stop() internally on NotifyContext() when processing a received signal.
+ // Being able to wait for a number of received system signals allows us to do so.
+ var wg sync.WaitGroup
+ n := *ctxNotifyTimes
+ wg.Add(n)
+ for i := 0; i < n; i++ {
+ go func() {
+ syscall.Kill(syscall.Getpid(), syscall.SIGINT)
+ wg.Done()
+ }()
}
- case <-time.After(time.Second):
- t.Errorf("timed out waiting for context to be done after SIGINT")
+ wg.Wait()
+ <-ctx.Done()
+ fmt.Print("received SIGINT")
+ // Sleep to give time to simultaneous signals to reach the process.
+ // These signals must be ignored given stop() is not called on this code.
+ // We want to guarantee a SIGINT doesn't cause a premature termination of the program.
+ time.Sleep(settleTime)
+ return
+ }
+
+ t.Parallel()
+ testCases := []struct {
+ name string
+ n int // number of times a SIGINT should be notified.
+ }{
+ {"once", 1},
+ {"multiple", 10},
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ var subTimeout time.Duration
+ if deadline, ok := t.Deadline(); ok {
+ subTimeout := time.Until(deadline)
+ subTimeout -= subTimeout / 10 // Leave 10% headroom for cleaning up subprocess.
+ }
+
+ args := []string{
+ "-test.v",
+ "-test.run=TestNotifyContextNotifications$",
+ "-check_notify_ctx",
+ fmt.Sprintf("-ctx_notify_times=%d", tc.n),
+ }
+ 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 -check_notify_ctx_notification and it failed with %v.\nOutput:\n%s", err, out)
+ }
+ if want := []byte("received SIGINT"); !bytes.Contains(out, want) {
+ t.Errorf("got %q, wanted %q", out, want)
+ }
+ })
}
}
@@ -768,34 +814,6 @@ func TestNotifyContextPrematureCancelParent(t *testing.T) {
}
}
-func TestNotifyContextSimultaneousNotifications(t *testing.T) {
- c, stop := NotifyContext(context.Background(), syscall.SIGINT)
- defer stop()
-
- if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
- t.Errorf("c.String() = %q, want %q", got, want)
- }
-
- var wg sync.WaitGroup
- n := 10
- wg.Add(n)
- for i := 0; i < n; i++ {
- go func() {
- syscall.Kill(syscall.Getpid(), syscall.SIGINT)
- wg.Done()
- }()
- }
- wg.Wait()
- select {
- case <-c.Done():
- if got := c.Err(); got != context.Canceled {
- t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
- }
- case <-time.After(time.Second):
- t.Errorf("expected context to be canceled")
- }
-}
-
func TestNotifyContextSimultaneousStop(t *testing.T) {
c, stop := NotifyContext(context.Background(), syscall.SIGINT)
defer stop()
diff --git a/libgo/go/os/tempfile.go b/libgo/go/os/tempfile.go
index 2728485..1ad44f1 100644
--- a/libgo/go/os/tempfile.go
+++ b/libgo/go/os/tempfile.go
@@ -4,10 +4,7 @@
package os
-import (
- "errors"
- "strings"
-)
+import "errors"
// fastrand provided by runtime.
// We generate random temporary file names so that there's a good
@@ -23,7 +20,7 @@ func nextRandom() string {
// opens the file for reading and writing, and returns the resulting file.
// The filename is generated by taking pattern and adding a random string to the end.
// If pattern includes a "*", the random string replaces the last "*".
-// If dir is the empty string, TempFile uses the default directory for temporary files, as returned by TempDir.
+// If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir.
// Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file.
// The caller can use the file's Name method to find the pathname of the file.
// It is the caller's responsibility to remove the file when it is no longer needed.
@@ -62,7 +59,7 @@ func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
return "", "", errPatternHasSeparator
}
}
- if pos := strings.LastIndex(pattern, "*"); pos != -1 {
+ if pos := lastIndex(pattern, '*'); pos != -1 {
prefix, suffix = pattern[:pos], pattern[pos+1:]
} else {
prefix = pattern
@@ -74,7 +71,7 @@ func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
// and returns the pathname of the new directory.
// The new directory's name is generated by adding a random string to the end of pattern.
// If pattern includes a "*", the random string replaces the last "*" instead.
-// If dir is the empty string, TempFile uses the default directory for temporary files, as returned by TempDir.
+// If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir.
// Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory.
// It is the caller's responsibility to remove the directory when it is no longer needed.
func MkdirTemp(dir, pattern string) (string, error) {
@@ -116,3 +113,13 @@ func joinPath(dir, name string) string {
}
return dir + string(PathSeparator) + name
}
+
+// LastIndexByte from the strings package.
+func lastIndex(s string, sep byte) int {
+ for i := len(s) - 1; i >= 0; i-- {
+ if s[i] == sep {
+ return i
+ }
+ }
+ return -1
+}
diff --git a/libgo/go/os/testdata/dirfs/a b/libgo/go/os/testdata/dirfs/a
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libgo/go/os/testdata/dirfs/a
diff --git a/libgo/go/os/testdata/dirfs/b b/libgo/go/os/testdata/dirfs/b
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libgo/go/os/testdata/dirfs/b
diff --git a/libgo/go/os/testdata/dirfs/dir/x b/libgo/go/os/testdata/dirfs/dir/x
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libgo/go/os/testdata/dirfs/dir/x