Skip to content

Commit

Permalink
runtime: handle selects with duplicate channels in shrinkstack
Browse files Browse the repository at this point in the history
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 <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Austin Clements <[email protected]>
  • Loading branch information
ianlancetaylor authored and adg committed Jul 8, 2016
1 parent e5ff529 commit 84bb9e6
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 10 deletions.
33 changes: 25 additions & 8 deletions src/runtime/chan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand All @@ -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.
Expand All @@ -622,10 +634,14 @@ func TestSelectStackAdjust(t *testing.T) {
}
}
ready <- true
}()
}

go f(ready1, false)
go f(ready2, true)

// Let the goroutine get into the select.
<-ready
// Let the goroutines get into the select.
<-ready1
<-ready2
time.Sleep(10 * time.Millisecond)

// Force concurrent GC a few times.
Expand All @@ -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) {
Expand Down
12 changes: 10 additions & 2 deletions src/runtime/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -784,8 +784,12 @@ 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 {
lock(&sg.c.lock)
if sg.c != lastc {
lock(&sg.c.lock)
}
lastc = sg.c
}

// Adjust sudogs.
Expand All @@ -803,8 +807,12 @@ func syncadjustsudogs(gp *g, used uintptr, adjinfo *adjustinfo) uintptr {
}

// Unlock channels.
lastc = nil
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
Expand Down

0 comments on commit 84bb9e6

Please sign in to comment.