|  | // 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)) | 
|  | } | 
|  | } |