1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build !windows 6 7 // Issue 18146: pthread_create failure during syscall.Exec. 8 9 package cgotest 10 11 import "C" 12 13 import ( 14 "bytes" 15 "crypto/md5" 16 "os" 17 "os/exec" 18 "runtime" 19 "syscall" 20 "testing" 21 "time" 22 ) 23 24 func test18146(t *testing.T) { 25 if runtime.GOOS == "darwin" { 26 t.Skipf("skipping flaky test on %s; see golang.org/issue/18202", runtime.GOOS) 27 } 28 29 if runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" { 30 t.Skipf("skipping on %s", runtime.GOARCH) 31 } 32 33 attempts := 1000 34 threads := 4 35 36 if testing.Short() { 37 attempts = 100 38 } 39 40 // Restrict the number of attempts based on RLIMIT_NPROC. 41 // Tediously, RLIMIT_NPROC was left out of the syscall package, 42 // probably because it is not in POSIX.1, so we define it here. 43 // It is not defined on Solaris. 44 var nproc int 45 setNproc := true 46 switch runtime.GOOS { 47 default: 48 setNproc = false 49 case "linux": 50 nproc = 6 51 case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd": 52 nproc = 7 53 } 54 if setNproc { 55 var rlim syscall.Rlimit 56 if syscall.Getrlimit(nproc, &rlim) == nil { 57 max := int(rlim.Cur) / (threads + 5) 58 if attempts > max { 59 t.Logf("lowering attempts from %d to %d for RLIMIT_NPROC", attempts, max) 60 attempts = max 61 } 62 } 63 } 64 65 if os.Getenv("test18146") == "exec" { 66 runtime.GOMAXPROCS(1) 67 for n := threads; n > 0; n-- { 68 go func() { 69 for { 70 _ = md5.Sum([]byte("Hello, !")) 71 } 72 }() 73 } 74 runtime.GOMAXPROCS(threads) 75 argv := append(os.Args, "-test.run=NoSuchTestExists") 76 if err := syscall.Exec(os.Args[0], argv, os.Environ()); err != nil { 77 t.Fatal(err) 78 } 79 } 80 81 var cmds []*exec.Cmd 82 defer func() { 83 for _, cmd := range cmds { 84 cmd.Process.Kill() 85 } 86 }() 87 88 args := append(append([]string(nil), os.Args[1:]...), "-test.run=Test18146") 89 for n := attempts; n > 0; n-- { 90 cmd := exec.Command(os.Args[0], args...) 91 cmd.Env = append(os.Environ(), "test18146=exec") 92 buf := bytes.NewBuffer(nil) 93 cmd.Stdout = buf 94 cmd.Stderr = buf 95 if err := cmd.Start(); err != nil { 96 // We are starting so many processes that on 97 // some systems (problem seen on Darwin, 98 // Dragonfly, OpenBSD) the fork call will fail 99 // with EAGAIN. 100 if pe, ok := err.(*os.PathError); ok { 101 err = pe.Err 102 } 103 if se, ok := err.(syscall.Errno); ok && (se == syscall.EAGAIN || se == syscall.EMFILE) { 104 time.Sleep(time.Millisecond) 105 continue 106 } 107 108 t.Error(err) 109 return 110 } 111 cmds = append(cmds, cmd) 112 } 113 114 failures := 0 115 for _, cmd := range cmds { 116 err := cmd.Wait() 117 if err == nil { 118 continue 119 } 120 121 t.Errorf("syscall.Exec failed: %v\n%s", err, cmd.Stdout) 122 failures++ 123 } 124 125 if failures > 0 { 126 t.Logf("Failed %v of %v attempts.", failures, len(cmds)) 127 } 128 } 129