sigsave(&sigmask)
sigblock(false)
- // nilokay=false is safe here because of the invariant above,
+ // getExtraM is safe here because of the invariant above,
// that the extra list always contains or will soon contain
// at least one m.
- mp, last := getExtraM(false)
+ mp, last := getExtraM()
// Set needextram when we've just emptied the list,
// so that the eventual call into cgocallbackg will
continue
}
if extraM.CompareAndSwap(old, locked) {
- extraMInUse.Add(1)
return (*m)(unsafe.Pointer(old))
}
osyield_no_g()
// Return an M from the extra M list. Returns last == true if the list becomes
// empty because of this call.
//
+// Spins waiting for an extra M, so caller must ensure that the list always
+// contains or will soon contain at least one M.
+//
//go:nosplit
-func getExtraM(nilokay bool) (mp *m, last bool) {
- mp = lockextra(nilokay)
- if mp == nil {
- unlockextra(nil, 0)
- return nil, true
- }
+func getExtraM() (mp *m, last bool) {
+ mp = lockextra(false)
+ extraMInUse.Add(1)
unlockextra(mp.schedlink.ptr(), -1)
return mp, mp.schedlink.ptr() == nil
}
"fmt"
"os"
"runtime"
+ "sync/atomic"
+ _ "unsafe" // for go:linkname
)
func init() {
//export go_callback
func go_callback() {
+ if e := extraMInUse.Load(); e == 0 {
+ fmt.Printf("in callback extraMInUse got %d want >0\n", e)
+ os.Exit(1)
+ }
+
runtime.GC()
grow()
runtime.GC()
if os.Getenv("RUNTIME_TEST_SHORT") != "" {
P = 10
}
+
+ if e := extraMInUse.Load(); e != 0 {
+ fmt.Printf("before testing extraMInUse got %d want 0\n", e)
+ os.Exit(1)
+ }
+
done := make(chan bool)
// allocate a bunch of stack frames and spray them with pointers
for i := 0; i < P; i++ {
for i := 0; i < P; i++ {
<-done
}
+
+ if e := extraMInUse.Load(); e != 0 {
+ fmt.Printf("after testing extraMInUse got %d want 0\n", e)
+ os.Exit(1)
+ }
+
fmt.Printf("OK\n")
}
+
+//go:linkname extraMInUse runtime.extraMInUse
+var extraMInUse atomic.Uint32