Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions runtime/internal/runtime/z_chan.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ func ChanSend(p *Chan, v unsafe.Pointer, eltSize int) bool {
if n == 0 {
for p.getp != chanHasRecv && !p.close {
p.sends++
// A blocked unbuffered send must wake select-based receivers so they can
// retry ChanTryRecv after observing that a sender is now waiting.
notifyOps(p)
p.cond.Wait(&p.mutex)
p.sends--
}
Expand Down
49 changes: 49 additions & 0 deletions test/select_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package test

import (
"testing"
"time"
)

func TestSelectRecvWakesForBlockedUnbufferedSend(t *testing.T) {
for i := 0; i < 50; i++ {
res := make(chan struct{})
done := make(chan struct{})
started := make(chan struct{})
received := make(chan struct{})
sendDone := make(chan struct{})

go func() {
close(started)
select {
case <-res:
close(received)
case <-done:
}
}()

<-started
time.Sleep(time.Millisecond)

go func() {
res <- struct{}{}
close(sendDone)
}()

select {
case <-sendDone:
case <-time.After(200 * time.Millisecond):
close(done)
t.Fatalf("iteration %d: unbuffered send did not wake select receiver", i)
}

select {
case <-received:
case <-time.After(200 * time.Millisecond):
close(done)
t.Fatalf("iteration %d: select receiver did not receive sent value", i)
}

close(done)
}
}
Loading