diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go index e3565a6d33..2786e7e441 100644 --- a/src/runtime/proc1.go +++ b/src/runtime/proc1.go @@ -158,7 +158,15 @@ func ready(gp *g, traceskip int) { // readyExecute marks gp ready to run, preempt the current g, and execute gp. // This is used to start concurrent GC promptly when we reach its trigger. func readyExecute(gp *g, traceskip int) { + // Squirrel away gp so we don't allocate a closure for the + // mcall'd func below. If we allocate a closure, it could go + // away as soon as we put _g_ on the runqueue. + getg().readyg = gp + mcall(func(_g_ *g) { + gp := _g_.readyg + _g_.readyg = nil + if trace.enabled { traceGoUnpark(gp, traceskip) traceGoSched() diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 609c7cf6f6..3230d4e1a8 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -243,6 +243,7 @@ type g struct { startpc uintptr // pc of goroutine function racectx uintptr waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr) + readyg *g // scratch for readyExecute } type mts struct { diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index 7b6fbb0349..50e2a207da 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -33,7 +33,10 @@ func getg() *g // run other goroutines. // // mcall can only be called from g stacks (not g0, not gsignal). -//go:noescape +// +// This must NOT be go:noescape: if fn is a stack-allocated closure, +// fn puts g on a run queue, and g executes before fn returns, the +// closure will be invalidated while it is still executing. func mcall(fn func(*g)) // systemstack runs fn on a system stack.