Commit 84bb9e62 authored by Ian Lance Taylor's avatar Ian Lance Taylor Committed by Andrew Gerrand

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: 's avatarAustin Clements <austin@google.com>
parent e5ff5296
...@@ -593,8 +593,10 @@ func TestSelectStackAdjust(t *testing.T) { ...@@ -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) { ...@@ -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) { ...@@ -622,10 +634,14 @@ func TestSelectStackAdjust(t *testing.T) {
} }
} }
ready <- true ready <- true
}() }
go f(ready1, false)
go f(ready2, true)
// Let the goroutine get into the select. // Let the goroutines get into the select.
<-ready <-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) { ...@@ -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 { ...@@ -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 { ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment