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.
|
// pointers are adjusted correctly by stack shrinking.
|
||||||
c := make(chan *int)
|
c := make(chan *int)
|
||||||
d := make(chan *int)
|
d := make(chan *int)
|
||||||
ready := make(chan bool)
|
ready1 := make(chan bool)
|
||||||
go func() {
|
ready2 := make(chan bool)
|
||||||
|
|
||||||
|
f := func(ready chan bool, dup bool) {
|
||||||
// Temporarily grow the stack to 10K.
|
// Temporarily grow the stack to 10K.
|
||||||
stackGrowthRecursive((10 << 10) / (128 * 8))
|
stackGrowthRecursive((10 << 10) / (128 * 8))
|
||||||
|
|
||||||
|
|
@ -604,10 +606,20 @@ func TestSelectStackAdjust(t *testing.T) {
|
||||||
val := 42
|
val := 42
|
||||||
var cx *int
|
var cx *int
|
||||||
cx = &val
|
cx = &val
|
||||||
|
|
||||||
|
var c2 chan *int
|
||||||
|
var d2 chan *int
|
||||||
|
if dup {
|
||||||
|
c2 = c
|
||||||
|
d2 = d
|
||||||
|
}
|
||||||
|
|
||||||
// Receive from d. cx won't be affected.
|
// Receive from d. cx won't be affected.
|
||||||
select {
|
select {
|
||||||
case cx = <-c:
|
case cx = <-c:
|
||||||
|
case <-c2:
|
||||||
case <-d:
|
case <-d:
|
||||||
|
case <-d2:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that pointer in cx was adjusted correctly.
|
// Check that pointer in cx was adjusted correctly.
|
||||||
|
|
@ -622,10 +634,14 @@ func TestSelectStackAdjust(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ready <- true
|
ready <- true
|
||||||
}()
|
}
|
||||||
|
|
||||||
// Let the goroutine get into the select.
|
go f(ready1, false)
|
||||||
<-ready
|
go f(ready2, true)
|
||||||
|
|
||||||
|
// Let the goroutines get into the select.
|
||||||
|
<-ready1
|
||||||
|
<-ready2
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
|
||||||
// Force concurrent GC a few times.
|
// Force concurrent GC a few times.
|
||||||
|
|
@ -642,9 +658,10 @@ func TestSelectStackAdjust(t *testing.T) {
|
||||||
done:
|
done:
|
||||||
selectSink = nil
|
selectSink = nil
|
||||||
|
|
||||||
// Wake select.
|
// Wake selects.
|
||||||
d <- nil
|
close(d)
|
||||||
<-ready
|
<-ready1
|
||||||
|
<-ready2
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkChanNonblocking(b *testing.B) {
|
func BenchmarkChanNonblocking(b *testing.B) {
|
||||||
|
|
|
||||||
|
|
@ -784,8 +784,12 @@ func syncadjustsudogs(gp *g, used uintptr, adjinfo *adjustinfo) uintptr {
|
||||||
// copystack; otherwise, gp may be in the middle of
|
// copystack; otherwise, gp may be in the middle of
|
||||||
// putting itself on wait queues and this would
|
// putting itself on wait queues and this would
|
||||||
// self-deadlock.
|
// self-deadlock.
|
||||||
|
var lastc *hchan
|
||||||
for sg := gp.waiting; sg != nil; sg = sg.waitlink {
|
for sg := gp.waiting; sg != nil; sg = sg.waitlink {
|
||||||
lock(&sg.c.lock)
|
if sg.c != lastc {
|
||||||
|
lock(&sg.c.lock)
|
||||||
|
}
|
||||||
|
lastc = sg.c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust sudogs.
|
// Adjust sudogs.
|
||||||
|
|
@ -803,8 +807,12 @@ func syncadjustsudogs(gp *g, used uintptr, adjinfo *adjustinfo) uintptr {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock channels.
|
// Unlock channels.
|
||||||
|
lastc = nil
|
||||||
for sg := gp.waiting; sg != nil; sg = sg.waitlink {
|
for sg := gp.waiting; sg != nil; sg = sg.waitlink {
|
||||||
unlock(&sg.c.lock)
|
if sg.c != lastc {
|
||||||
|
unlock(&sg.c.lock)
|
||||||
|
}
|
||||||
|
lastc = sg.c
|
||||||
}
|
}
|
||||||
|
|
||||||
return sgsize
|
return sgsize
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue