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:
Ian Lance Taylor 2016-07-07 17:43:08 -07:00 committed by Andrew Gerrand
parent e5ff529679
commit 84bb9e62f0
2 changed files with 35 additions and 10 deletions

View file

@ -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.
@ -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