// 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. //go:build !windows // +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)) } }