mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: handle selects with duplicate channels in shrinkstack
The shrinkstack code locks all the channels a goroutine is waiting for, but didn't handle the case of the same channel appearing in the list multiple times. This led to a deadlock. The channels are sorted so it's easy to avoid locking the same channel twice. Fixes #16286. Change-Id: Ie514805d0532f61c942e85af5b7b8ac405e2ff65 Reviewed-on: https://go-review.googlesource.com/24815 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
e5ff529679
commit
84bb9e62f0
2 changed files with 35 additions and 10 deletions
|
|
@ -593,8 +593,10 @@ func TestSelectStackAdjust(t *testing.T) {
|
|||
// pointers are adjusted correctly by stack shrinking.
|
||||
c := make(chan *int)
|
||||
d := make(chan *int)
|
||||
ready := make(chan bool)
|
||||
go func() {
|
||||
ready1 := make(chan bool)
|
||||
ready2 := make(chan bool)
|
||||
|
||||
f := func(ready chan bool, dup bool) {
|
||||
// Temporarily grow the stack to 10K.
|
||||
stackGrowthRecursive((10 << 10) / (128 * 8))
|
||||
|
||||
|
|
@ -604,10 +606,20 @@ func TestSelectStackAdjust(t *testing.T) {
|
|||
val := 42
|
||||
var cx *int
|
||||
cx = &val
|
||||
|
||||
var c2 chan *int
|
||||
var d2 chan *int
|
||||
if dup {
|
||||
c2 = c
|
||||
d2 = d
|
||||
}
|
||||
|
||||
// Receive from d. cx won't be affected.
|
||||
select {
|
||||
case cx = <-c:
|
||||
case <-c2:
|
||||
case <-d:
|
||||
case <-d2:
|
||||
}
|
||||
|
||||
// Check that pointer in cx was adjusted correctly.
|
||||
|
|
@ -622,10 +634,14 @@ func TestSelectStackAdjust(t *testing.T) {
|
|||
}
|
||||
}
|
||||
ready <- true
|
||||
}()
|
||||
}
|
||||
|
||||
// Let the goroutine get into the select.
|
||||
<-ready
|
||||
go f(ready1, false)
|
||||
go f(ready2, true)
|
||||
|
||||
// Let the goroutines get into the select.
|
||||
<-ready1
|
||||
<-ready2
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Force concurrent GC a few times.
|
||||
|
|
@ -642,9 +658,10 @@ func TestSelectStackAdjust(t *testing.T) {
|
|||
done:
|
||||
selectSink = nil
|
||||
|
||||
// Wake select.
|
||||
d <- nil
|
||||
<-ready
|
||||
// Wake selects.
|
||||
close(d)
|
||||
<-ready1
|
||||
<-ready2
|
||||
}
|
||||
|
||||
func BenchmarkChanNonblocking(b *testing.B) {
|
||||
|
|
|
|||
|
|
@ -784,9 +784,13 @@ func syncadjustsudogs(gp *g, used uintptr, adjinfo *adjustinfo) uintptr {
|
|||
// copystack; otherwise, gp may be in the middle of
|
||||
// putting itself on wait queues and this would
|
||||
// self-deadlock.
|
||||
var lastc *hchan
|
||||
for sg := gp.waiting; sg != nil; sg = sg.waitlink {
|
||||
if sg.c != lastc {
|
||||
lock(&sg.c.lock)
|
||||
}
|
||||
lastc = sg.c
|
||||
}
|
||||
|
||||
// Adjust sudogs.
|
||||
adjustsudogs(gp, adjinfo)
|
||||
|
|
@ -803,9 +807,13 @@ func syncadjustsudogs(gp *g, used uintptr, adjinfo *adjustinfo) uintptr {
|
|||
}
|
||||
|
||||
// Unlock channels.
|
||||
lastc = nil
|
||||
for sg := gp.waiting; sg != nil; sg = sg.waitlink {
|
||||
if sg.c != lastc {
|
||||
unlock(&sg.c.lock)
|
||||
}
|
||||
lastc = sg.c
|
||||
}
|
||||
|
||||
return sgsize
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue