diff options
author | Ian Lance Taylor <iant@golang.org> | 2021-01-27 17:55:50 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2021-01-29 11:04:55 -0800 |
commit | 726b7aa004d6885388a76521222602b8552a41ee (patch) | |
tree | 5179037ef840a43dcea0f3be4e07dbcbcfcb2c4a /libgo/go/os | |
parent | 91a95ad2ae0e0f2fa953fafe55ff2ec32c8277d5 (diff) | |
download | gcc-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.go | 10 | ||||
-rw-r--r-- | libgo/go/os/file_unix.go | 8 | ||||
-rw-r--r-- | libgo/go/os/os_test.go | 45 | ||||
-rw-r--r-- | libgo/go/os/os_windows_test.go | 11 | ||||
-rw-r--r-- | libgo/go/os/signal/signal_linux_test.go | 42 | ||||
-rw-r--r-- | libgo/go/os/signal/signal_test.go | 102 | ||||
-rw-r--r-- | libgo/go/os/tempfile.go | 21 | ||||
-rw-r--r-- | libgo/go/os/testdata/dirfs/a | 0 | ||||
-rw-r--r-- | libgo/go/os/testdata/dirfs/b | 0 | ||||
-rw-r--r-- | libgo/go/os/testdata/dirfs/dir/x | 0 |
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 |