issue18146.go 2.8 KB

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