| // Copyright 2016 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 !windows | |
| // Issue 18146: pthread_create failure during syscall.Exec. | |
| package cgotest | |
| import ( | |
| "bytes" | |
| "crypto/md5" | |
| "os" | |
| "os/exec" | |
| "runtime" | |
| "syscall" | |
| "testing" | |
| "time" | |
| ) | |
| func test18146(t *testing.T) { | |
| if testing.Short() { | |
| t.Skip("skipping in short mode") | |
| } | |
| if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { | |
| t.Skipf("skipping flaky test on %s; see golang.org/issue/18202", runtime.GOOS) | |
| } | |
| if runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" { | |
| t.Skipf("skipping on %s", runtime.GOARCH) | |
| } | |
| attempts := 1000 | |
| threads := 4 | |
| // Restrict the number of attempts based on RLIMIT_NPROC. | |
| // Tediously, RLIMIT_NPROC was left out of the syscall package, | |
| // probably because it is not in POSIX.1, so we define it here. | |
| // It is not defined on Solaris. | |
| var nproc int | |
| setNproc := true | |
| switch runtime.GOOS { | |
| default: | |
| setNproc = false | |
| case "aix": | |
| nproc = 9 | |
| case "linux": | |
| nproc = 6 | |
| case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd": | |
| nproc = 7 | |
| } | |
| if setNproc { | |
| var rlim syscall.Rlimit | |
| if syscall.Getrlimit(nproc, &rlim) == nil { | |
| max := int(rlim.Cur) / (threads + 5) | |
| if attempts > max { | |
| t.Logf("lowering attempts from %d to %d for RLIMIT_NPROC", attempts, max) | |
| attempts = max | |
| } | |
| } | |
| } | |
| if os.Getenv("test18146") == "exec" { | |
| runtime.GOMAXPROCS(1) | |
| for n := threads; n > 0; n-- { | |
| go func() { | |
| for { | |
| _ = md5.Sum([]byte("Hello, !")) | |
| } | |
| }() | |
| } | |
| runtime.GOMAXPROCS(threads) | |
| argv := append(os.Args, "-test.run=NoSuchTestExists") | |
| if err := syscall.Exec(os.Args[0], argv, os.Environ()); err != nil { | |
| t.Fatal(err) | |
| } | |
| } | |
| var cmds []*exec.Cmd | |
| defer func() { | |
| for _, cmd := range cmds { | |
| cmd.Process.Kill() | |
| } | |
| }() | |
| args := append(append([]string(nil), os.Args[1:]...), "-test.run=Test18146") | |
| for n := attempts; n > 0; n-- { | |
| cmd := exec.Command(os.Args[0], args...) | |
| cmd.Env = append(os.Environ(), "test18146=exec") | |
| buf := bytes.NewBuffer(nil) | |
| cmd.Stdout = buf | |
| cmd.Stderr = buf | |
| if err := cmd.Start(); err != nil { | |
| // We are starting so many processes that on | |
| // some systems (problem seen on Darwin, | |
| // Dragonfly, OpenBSD) the fork call will fail | |
| // with EAGAIN. | |
| if pe, ok := err.(*os.PathError); ok { | |
| err = pe.Err | |
| } | |
| if se, ok := err.(syscall.Errno); ok && (se == syscall.EAGAIN || se == syscall.EMFILE) { | |
| time.Sleep(time.Millisecond) | |
| continue | |
| } | |
| t.Error(err) | |
| return | |
| } | |
| cmds = append(cmds, cmd) | |
| } | |
| failures := 0 | |
| for _, cmd := range cmds { | |
| err := cmd.Wait() | |
| if err == nil { | |
| continue | |
| } | |
| t.Errorf("syscall.Exec failed: %v\n%s", err, cmd.Stdout) | |
| failures++ | |
| } | |
| if failures > 0 { | |
| t.Logf("Failed %v of %v attempts.", failures, len(cmds)) | |
| } | |
| } |