1
0
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:
Ian Lance Taylor 2022-11-01 16:04:50 -07:00 committed by Gopher Robot
parent d96eb826cb
commit 14018c8bec
10 changed files with 75 additions and 30 deletions

View 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")

View 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")

View 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)

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

View 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")
}
}

View 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")

View 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")

View 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)

View 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")

23
src/runtime/retry.go Normal file
View 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
}