]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: retry thread creation on EAGAIN
authorIan Lance Taylor <iant@golang.org>
Tue, 1 Nov 2022 23:04:50 +0000 (16:04 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 10 Nov 2022 20:44:45 +0000 (20:44 +0000)
This copies the logic we use in runtime/cgo, when calling pthread_create,
into runtime proper, when calling newosproc.

We only do this in newosproc, not newosproc0, because in newosproc0 we
need a nosplit function literal, and we need to pass arguments to it through
newosproc, which is a pain. Also newosproc0 is only called at process
startup, when thread creation is less likely to fail anyhow.

Fixes #49438

Change-Id: Ia26813952fdbae8aaad5904c9102269900a07ba9
Reviewed-on: https://go-review.googlesource.com/c/go/+/447175
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
src/runtime/os3_solaris.go
src/runtime/os_aix.go
src/runtime/os_darwin.go
src/runtime/os_dragonfly.go
src/runtime/os_freebsd.go
src/runtime/os_linux.go
src/runtime/os_netbsd.go
src/runtime/os_openbsd_libc.go
src/runtime/os_openbsd_syscall.go
src/runtime/retry.go [new file with mode: 0644]

index 3bc23e17befcf97e06ec6d26a0a4932f47fe73a2..ffac4b6492b87cc51ded27da824f05373e1adba0 100644 (file)
@@ -172,11 +172,13 @@ func newosproc(mp *m) {
        // Disable signals during create, so that the new thread starts
        // with signals disabled. It will enable them in minit.
        sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
-       ret = pthread_create(&tid, &attr, abi.FuncPCABI0(tstart_sysvicall), unsafe.Pointer(mp))
+       ret = retryOnEAGAIN(func() int32 {
+               return pthread_create(&tid, &attr, abi.FuncPCABI0(tstart_sysvicall), unsafe.Pointer(mp))
+       })
        sigprocmask(_SIG_SETMASK, &oset, nil)
        if ret != 0 {
                print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", ret, ")\n")
-               if ret == -_EAGAIN {
+               if ret == _EAGAIN {
                        println("runtime: may need to increase max user processes (ulimit -u)")
                }
                throw("newosproc")
index 1a534db0e4e70c073dd176408c0150b9cf05224f..7c5947d06f5294a661d2214da0c9a4da21084744 100644 (file)
@@ -211,16 +211,9 @@ func newosproc(mp *m) {
        // Disable signals during create, so that the new thread starts
        // with signals disabled. It will enable them in minit.
        sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
-       var ret int32
-       for tries := 0; tries < 20; tries++ {
-               // pthread_create can fail with EAGAIN for no reasons
-               // but it will be ok if it retries.
-               ret = pthread_create(&tid, &attr, &tstart, unsafe.Pointer(mp))
-               if ret != _EAGAIN {
-                       break
-               }
-               usleep(uint32(tries+1) * 1000) // Milliseconds.
-       }
+       ret := retryOnEAGAIN(func() int32 {
+               return pthread_create(&tid, &attr, &tstart, unsafe.Pointer(mp))
+       })
        sigprocmask(_SIG_SETMASK, &oset, nil)
        if ret != 0 {
                print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", ret, ")\n")
index 45032c782fdc22a5e17ad0e874527a05142c2289..af5c18c30195c1b70c4aa173c79972b709d34789 100644 (file)
@@ -230,7 +230,9 @@ func newosproc(mp *m) {
        // setup and then calls mstart.
        var oset sigset
        sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
-       err = pthread_create(&attr, abi.FuncPCABI0(mstart_stub), unsafe.Pointer(mp))
+       err = retryOnEAGAIN(func() int32 {
+               return pthread_create(&attr, abi.FuncPCABI0(mstart_stub), unsafe.Pointer(mp))
+       })
        sigprocmask(_SIG_SETMASK, &oset, nil)
        if err != 0 {
                writeErrStr(failthreadcreate)
index 979a7d1642ed7cfcb559b39489d6d5fb40092f76..e467578c32a8c0e4c5958663c33dbca1ef807777 100644 (file)
@@ -162,7 +162,10 @@ func newosproc(mp *m) {
        }
 
        // TODO: Check for error.
-       lwp_create(&params)
+       retryOnEAGAIN(func() int32 {
+               lwp_create(&params)
+               return 0
+       })
        sigprocmask(_SIG_SETMASK, &oset, nil)
 }
 
index 3b7ae2a0f6dfc10078f170a44a21bf413ff6bd28..f53cb115a157df9d83ec737344313c84c5c485a6 100644 (file)
@@ -213,10 +213,14 @@ func newosproc(mp *m) {
 
        var oset sigset
        sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
-       ret := thr_new(&param, int32(unsafe.Sizeof(param)))
+       ret := retryOnEAGAIN(func() int32 {
+               errno := thr_new(&param, int32(unsafe.Sizeof(param)))
+               // thr_new returns negative errno
+               return -errno
+       })
        sigprocmask(_SIG_SETMASK, &oset, nil)
-       if ret < 0 {
-               print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n")
+       if ret != 0 {
+               print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", ret, ")\n")
                throw("newosproc")
        }
 }
index 8e30ee338e0c90d76b3cb66c71ed5474934d6503..3ad1e3b8fcd1d15e35c28129b46b16eb51bbb4b4 100644 (file)
@@ -176,12 +176,20 @@ func newosproc(mp *m) {
        // with signals disabled. It will enable them in minit.
        var oset sigset
        sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
-       ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(abi.FuncPCABI0(mstart)))
+       ret := retryOnEAGAIN(func() int32 {
+               r := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(abi.FuncPCABI0(mstart)))
+               // clone returns positive TID, negative errno.
+               // We don't care about the TID.
+               if r >= 0 {
+                       return 0
+               }
+               return -r
+       })
        sigprocmask(_SIG_SETMASK, &oset, nil)
 
-       if ret < 0 {
-               print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n")
-               if ret == -_EAGAIN {
+       if ret != 0 {
+               print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", ret, ")\n")
+               if ret == _EAGAIN {
                        println("runtime: may need to increase max user processes (ulimit -u)")
                }
                throw("newosproc")
index 0273d33c0598971aca591b20c0898bbd7cadb3a7..ce59618af4adb581ae21379e60fe8e9137b4100e 100644 (file)
@@ -227,11 +227,15 @@ func newosproc(mp *m) {
 
        lwp_mcontext_init(&uc.uc_mcontext, stk, mp, mp.g0, abi.FuncPCABI0(netbsdMstart))
 
-       ret := lwp_create(unsafe.Pointer(&uc), _LWP_DETACHED, unsafe.Pointer(&mp.procid))
+       ret := retryOnEAGAIN(func() int32 {
+               errno := lwp_create(unsafe.Pointer(&uc), _LWP_DETACHED, unsafe.Pointer(&mp.procid))
+               // lwp_create returns negative errno
+               return -errno
+       })
        sigprocmask(_SIG_SETMASK, &oset, nil)
-       if ret < 0 {
-               print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n")
-               if ret == -_EAGAIN {
+       if ret != 0 {
+               print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", ret, ")\n")
+               if ret == _EAGAIN {
                        println("runtime: may need to increase max user processes (ulimit -p)")
                }
                throw("runtime.newosproc")
index f0478b0936c6346de7adf5dc79e8a48a84d13ca3..201f1629d9b78b7b6dd72ed3a703949db8ca5719 100644 (file)
@@ -47,7 +47,9 @@ func newosproc(mp *m) {
        // setup and then calls mstart.
        var oset sigset
        sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
-       err := pthread_create(&attr, abi.FuncPCABI0(mstart_stub), unsafe.Pointer(mp))
+       err := retryOnEAGAIN(func() int32 {
+               return pthread_create(&attr, abi.FuncPCABI0(mstart_stub), unsafe.Pointer(mp))
+       })
        sigprocmask(_SIG_SETMASK, &oset, nil)
        if err != 0 {
                writeErrStr(failthreadcreate)
index 9d67a7ebbdaec4882cbb5e47fe7f88be57b184ec..d784f764750ebdca0580046fb933a6216aab0473 100644 (file)
@@ -34,12 +34,16 @@ func newosproc(mp *m) {
 
        var oset sigset
        sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
-       ret := tfork(&param, unsafe.Sizeof(param), mp, mp.g0, abi.FuncPCABI0(mstart))
+       ret := retryOnEAGAIN(func() int32 {
+               errno := tfork(&param, unsafe.Sizeof(param), mp, mp.g0, abi.FuncPCABI0(mstart))
+               // tfork returns negative errno
+               return -errno
+       })
        sigprocmask(_SIG_SETMASK, &oset, nil)
 
-       if ret < 0 {
-               print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n")
-               if ret == -_EAGAIN {
+       if ret != 0 {
+               print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", ret, ")\n")
+               if ret == _EAGAIN {
                        println("runtime: may need to increase max user processes (ulimit -p)")
                }
                throw("runtime.newosproc")
diff --git a/src/runtime/retry.go b/src/runtime/retry.go
new file mode 100644 (file)
index 0000000..2e2f813
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2022 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 unix
+
+package runtime
+
+// retryOnEAGAIN retries a function until it does not return EAGAIN.
+// It will use an increasing delay between calls, and retry up to 20 times.
+// The function argument is expected to return an errno value,
+// and retryOnEAGAIN will return any errno value other than EAGAIN.
+// If all retries return EAGAIN, then retryOnEAGAIN will return EAGAIN.
+func retryOnEAGAIN(fn func() int32) int32 {
+       for tries := 0; tries < 20; tries++ {
+               errno := fn()
+               if errno != _EAGAIN {
+                       return errno
+               }
+               usleep_no_g(uint32(tries+1) * 1000) // milliseconds
+       }
+       return _EAGAIN
+}