runtime (chan): fix blocking select on a nil channel

Previously, a blocking select on a nil channel would result in a nil panic inside the channel runtime code.
This change fixes the nil checks so that the select works as intended.
Этот коммит содержится в:
Jaden Weiss 2020-04-12 19:08:44 -04:00 коммит произвёл Ron Evans
родитель 9890c760cf
коммит 9c78f7039d
2 изменённых файлов: 14 добавлений и 3 удалений

Просмотреть файл

@ -85,10 +85,12 @@ func (b *channelBlockedList) detach() {
} }
for i, v := range b.allSelectOps { for i, v := range b.allSelectOps {
// cancel all other channel operations that are part of this select statement // cancel all other channel operations that are part of this select statement
if &b.allSelectOps[i] == b { switch {
case &b.allSelectOps[i] == b:
// This entry is the one that was already detatched.
continue continue
} case v.t == nil:
if v.s.ch == nil { // This entry is not used (nil channel).
continue continue
} }
v.s.ch.blocked = v.s.ch.blocked.remove(&b.allSelectOps[i]) v.s.ch.blocked = v.s.ch.blocked.remove(&b.allSelectOps[i])
@ -512,6 +514,13 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelB
// construct blocked operations // construct blocked operations
for i, v := range states { for i, v := range states {
if v.ch == nil {
// A nil channel receive will never complete.
// A nil channel send would have panicked during tryChanSelect.
ops[i] = channelBlockedList{}
continue
}
ops[i] = channelBlockedList{ ops[i] = channelBlockedList{
next: v.ch.blocked, next: v.ch.blocked,
t: task.Current(), t: task.Current(),

2
testdata/channel.go предоставленный
Просмотреть файл

@ -134,6 +134,8 @@ func main() {
select { select {
case make(chan int) <- 3: case make(chan int) <- 3:
println("unreachable") println("unreachable")
case <-(chan int)(nil):
println("unreachable")
case n := <-ch: case n := <-ch:
println("select n from chan:", n) println("select n from chan:", n)
case n := <-make(chan int): case n := <-make(chan int):