mirror of
https://github.com/golang/go
synced 2024-09-29 01:24:34 -06:00
runtime: retry thread creation on EAGAIN
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>
This commit is contained in:
parent
d96eb826cb
commit
14018c8bec
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -162,7 +162,10 @@ func newosproc(mp *m) {
|
||||
}
|
||||
|
||||
// TODO: Check for error.
|
||||
lwp_create(¶ms)
|
||||
retryOnEAGAIN(func() int32 {
|
||||
lwp_create(¶ms)
|
||||
return 0
|
||||
})
|
||||
sigprocmask(_SIG_SETMASK, &oset, nil)
|
||||
}
|
||||
|
||||
|
@ -213,10 +213,14 @@ func newosproc(mp *m) {
|
||||
|
||||
var oset sigset
|
||||
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
|
||||
ret := thr_new(¶m, int32(unsafe.Sizeof(param)))
|
||||
ret := retryOnEAGAIN(func() int32 {
|
||||
errno := thr_new(¶m, 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")
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -34,12 +34,16 @@ func newosproc(mp *m) {
|
||||
|
||||
var oset sigset
|
||||
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
|
||||
ret := tfork(¶m, unsafe.Sizeof(param), mp, mp.g0, abi.FuncPCABI0(mstart))
|
||||
ret := retryOnEAGAIN(func() int32 {
|
||||
errno := tfork(¶m, 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")
|
||||
|
23
src/runtime/retry.go
Normal file
23
src/runtime/retry.go
Normal file
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user