mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/internal/obj: mark split-stack prologue nonpreemptible
When there are both a synchronous preemption request (by clobbering the stack guard) and an asynchronous one (by signal), the running goroutine may observe the synchronous request first in stack bounds check, and go to the path of calling morestack. If the preemption signal arrives at this point before the call to morestack, the goroutine will be asynchronously preempted, entering the scheduler. When it is resumed, the scheduler clears the preemption request, unclobbers the stack guard. But the resumed goroutine will still call morestack, as it is already on its way. morestack will, as there is no preemption request, double the stack unnecessarily. If this happens multiple times, the stack may grow too big, although only a small amount is actually used. To fix this, we mark the stack bounds check and the call to morestack async-nonpreemptible, starting after the memory instruction (mostly a load, on x86 CMP with memory). Not done for Wasm as it does not support async preemption. Fixes #35470. Change-Id: Ibd7f3d935a3649b80f47539116ec9b9556680cf2 Reviewed-on: https://go-review.googlesource.com/c/go/+/207350 Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
c3f149250e
commit
b2482e4817
7 changed files with 94 additions and 40 deletions
|
|
@ -677,6 +677,12 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REG_R1
|
||||
|
||||
// Mark the stack bound check and morestack call async nonpreemptible.
|
||||
// If we get preempted here, when resumed the preemption request is
|
||||
// cleared, but we'll still call morestack, which will double the stack
|
||||
// unnecessarily. See issue #35470.
|
||||
p = c.ctxt.StartUnsafePoint(p, c.newprog)
|
||||
|
||||
var q *obj.Prog
|
||||
if framesize <= objabi.StackSmall {
|
||||
// small stack: SP < stackguard
|
||||
|
|
@ -796,7 +802,7 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
p.Mark |= LABEL
|
||||
}
|
||||
|
||||
p = c.ctxt.EmitEntryLiveness(c.cursym, p, c.newprog)
|
||||
p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
|
||||
|
||||
// JAL runtime.morestack(SB)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
|
@ -812,6 +818,8 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
|||
}
|
||||
p.Mark |= BRANCH
|
||||
|
||||
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
|
||||
|
||||
// JMP start
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue