select {
case <- c:
case <- c:
}
In this case, c.recvq lists two SudoGs which have the same G.
So we can't use the G as the key to dequeue the correct SudoG,
as that key is ambiguous. Dequeueing the wrong SudoG ends up
freeing a SudoG that is still in c.recvq.
The fix is to use the actual SudoG pointer as the key.
LGTM=dvyukov
R=rsc, bradfitz, dvyukov, khr
CC=austin, golang-codereviews
https://golang.org/cl/
159040043
<-done
}
+func TestSelectDuplicateChannel(t *testing.T) {
+ // This test makes sure we can queue a G on
+ // the same channel multiple times.
+ c := make(chan int)
+ d := make(chan int)
+ e := make(chan int)
+
+ // goroutine A
+ go func() {
+ select {
+ case <-c:
+ case <-c:
+ case <-d:
+ }
+ e <- 9
+ }()
+ time.Sleep(time.Millisecond) // make sure goroutine A gets qeueued first on c
+
+ // goroutine B
+ go func() {
+ <-c
+ }()
+ time.Sleep(time.Millisecond) // make sure goroutine B gets queued on c before continuing
+
+ d <- 7 // wake up A, it dequeues itself from c. This operation used to corrupt c.recvq.
+ <-e // A tells us it's done
+ c <- 8 // wake up B. This operation used to fail because c.recvq was corrupted (it tries to wake up an already running G instead of B)
+}
+
func BenchmarkChanNonblocking(b *testing.B) {
myc := make(chan int)
b.RunParallel(func(pb *testing.PB) {
} else {
c = k._chan
if k.kind == _CaseSend {
- c.sendq.dequeueg(gp)
+ c.sendq.dequeueSudoG(sglist)
} else {
- c.recvq.dequeueg(gp)
+ c.recvq.dequeueSudoG(sglist)
}
}
sgnext = sglist.waitlink
return
}
-func (q *waitq) dequeueg(gp *g) {
+func (q *waitq) dequeueSudoG(s *sudog) {
var prevsgp *sudog
l := &q.first
for {
if sgp == nil {
return
}
- if sgp.g == gp {
+ if sgp == s {
*l = sgp.next
if q.last == sgp {
q.last = prevsgp