diff options
Diffstat (limited to 'src/runtime/proc.go')
-rw-r--r-- | src/runtime/proc.go | 118 |
1 files changed, 102 insertions, 16 deletions
diff --git a/src/runtime/proc.go b/src/runtime/proc.go index fd892115bf..35aeb2d1ac 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -209,6 +209,10 @@ func main() { main_init_done = make(chan bool) if iscgo { + if _cgo_pthread_key_created == nil { + throw("_cgo_pthread_key_created missing") + } + if _cgo_thread_start == nil { throw("_cgo_thread_start missing") } @@ -223,6 +227,13 @@ func main() { if _cgo_notify_runtime_init_done == nil { throw("_cgo_notify_runtime_init_done missing") } + + // Set the x_crosscall2_ptr C function pointer variable point to crosscall2. + if set_crosscall2 == nil { + throw("set_crosscall2 missing") + } + set_crosscall2() + // Start the template thread in case we enter Go from // a C-created thread and need to create a new thread. startTemplateThread() @@ -1886,11 +1897,15 @@ func allocm(pp *p, fn func(), id int64) *m { // pressed into service as the scheduling stack and current // goroutine for the duration of the cgo callback. // -// When the callback is done with the m, it calls dropm to -// put the m back on the list. +// It calls dropm to put the m back on the list, +// 1. when the callback is done with the m in non-pthread platforms, +// 2. or when the C thread exiting on pthread platforms. +// +// The signal argument indicates whether we're called from a signal +// handler. // //go:nosplit -func needm() { +func needm(signal bool) { if (iscgo || GOOS == "windows") && !cgoHasExtraM { // Can happen if C/C++ code calls Go from a global ctor. // Can also happen on Windows if a global ctor uses a @@ -1936,16 +1951,36 @@ func needm() { osSetupTLS(mp) // Install g (= m->g0) and set the stack bounds - // to match the current stack. We don't actually know + // to match the current stack. If we don't actually know // how big the stack is, like we don't know how big any - // scheduling stack is, but we assume there's at least 32 kB, - // which is more than enough for us. + // scheduling stack is, but we assume there's at least 32 kB. + // If we can get a more accurate stack bound from pthread, + // use that. setg(mp.g0) gp := getg() gp.stack.hi = getcallersp() + 1024 gp.stack.lo = getcallersp() - 32*1024 + if !signal && _cgo_getstackbound != nil { + // Don't adjust if called from the signal handler. + // We are on the signal stack, not the pthread stack. + // (We could get the stack bounds from sigaltstack, but + // we're getting out of the signal handler very soon + // anyway. Not worth it.) + var bounds [2]uintptr + asmcgocall(_cgo_getstackbound, unsafe.Pointer(&bounds)) + // getstackbound is an unsupported no-op on Windows. + if bounds[0] != 0 { + gp.stack.lo = bounds[0] + gp.stack.hi = bounds[1] + } + } gp.stackguard0 = gp.stack.lo + stackGuard + // Should mark we are already in Go now. + // Otherwise, we may call needm again when we get a signal, before cgocallbackg1, + // which means the extram list may be empty, that will cause a deadlock. + mp.isExtraInC = false + // Initialize this thread to use the m. asminit() minit() @@ -1955,6 +1990,17 @@ func needm() { sched.ngsys.Add(-1) } +// Acquire an extra m and bind it to the C thread when a pthread key has been created. +// +//go:nosplit +func needAndBindM() { + needm(false) + + if _cgo_pthread_key_created != nil && *(*uintptr)(_cgo_pthread_key_created) != 0 { + cgoBindM() + } +} + // newextram allocates m's and puts them on the extra list. // It is called with a working local m, so that it can do things // like call schedlock and allocate. @@ -1995,6 +2041,8 @@ func oneNewExtraM() { gp.m = mp mp.curg = gp mp.isextra = true + // mark we are in C by default. + mp.isExtraInC = true mp.lockedInt++ mp.lockedg.set(gp) gp.lockedm.set(mp) @@ -2018,9 +2066,11 @@ func oneNewExtraM() { addExtraM(mp) } +// dropm puts the current m back onto the extra list. +// +// 1. On systems without pthreads, like Windows // dropm is called when a cgo callback has called needm but is now // done with the callback and returning back into the non-Go thread. -// It puts the current m back onto the extra list. // // The main expense here is the call to signalstack to release the // m's signal stack, and then the call to needm on the next callback @@ -2032,15 +2082,18 @@ func oneNewExtraM() { // call. These should typically not be scheduling operations, just a few // atomics, so the cost should be small. // -// TODO(rsc): An alternative would be to allocate a dummy pthread per-thread -// variable using pthread_key_create. Unlike the pthread keys we already use -// on OS X, this dummy key would never be read by Go code. It would exist -// only so that we could register at thread-exit-time destructor. -// That destructor would put the m back onto the extra list. -// This is purely a performance optimization. The current version, -// in which dropm happens on each cgo call, is still correct too. -// We may have to keep the current version on systems with cgo -// but without pthreads, like Windows. +// 2. On systems with pthreads +// dropm is called while a non-Go thread is exiting. +// We allocate a pthread per-thread variable using pthread_key_create, +// to register a thread-exit-time destructor. +// And store the g into a thread-specific value associated with the pthread key, +// when first return back to C. +// So that the destructor would invoke dropm while the non-Go thread is exiting. +// This is much faster since it avoids expensive signal-related syscalls. +// +// NOTE: this always runs without a P, so, nowritebarrierrec required. +// +//go:nowritebarrierrec func dropm() { // Clear m and g, and return m to the extra list. // After the call to setg we can only call nosplit functions @@ -2067,6 +2120,39 @@ func dropm() { msigrestore(sigmask) } +// bindm store the g0 of the current m into a thread-specific value. +// +// We allocate a pthread per-thread variable using pthread_key_create, +// to register a thread-exit-time destructor. +// We are here setting the thread-specific value of the pthread key, to enable the destructor. +// So that the pthread_key_destructor would dropm while the C thread is exiting. +// +// And the saved g will be used in pthread_key_destructor, +// since the g stored in the TLS by Go might be cleared in some platforms, +// before the destructor invoked, so, we restore g by the stored g, before dropm. +// +// We store g0 instead of m, to make the assembly code simpler, +// since we need to restore g0 in runtime.cgocallback. +// +// On systems without pthreads, like Windows, bindm shouldn't be used. +// +// NOTE: this always runs without a P, so, nowritebarrierrec required. +// +//go:nosplit +//go:nowritebarrierrec +func cgoBindM() { + if GOOS == "windows" || GOOS == "plan9" { + fatal("bindm in unexpected GOOS") + } + g := getg() + if g.m.g0 != g { + fatal("the current g is not g0") + } + if _cgo_bindm != nil { + asmcgocall(_cgo_bindm, unsafe.Pointer(g)) + } +} + // A helper function for EnsureDropM. func getm() uintptr { return uintptr(unsafe.Pointer(getg().m)) |