aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/chan.go
diff options
context:
space:
mode:
authorDaniel Morsing <daniel.morsing@gmail.com>2017-08-02 19:01:17 +0100
committerDaniel Morsing <daniel.morsing@gmail.com>2017-08-15 19:18:00 +0000
commit32b94f13cf35e619a0dae6e1e381adc7e182d283 (patch)
tree64d58aca0d055ac7ced3b9a611d5ed36e9980d6c /src/runtime/chan.go
parent89d74f54168619cf1f36b6868626fbb1237c1deb (diff)
downloadgo-32b94f13cf35e619a0dae6e1e381adc7e182d283.tar.gz
go-32b94f13cf35e619a0dae6e1e381adc7e182d283.zip
runtime: move selectdone into g
Writing to selectdone on the stack of another goroutine meant a pretty subtle dance between the select code and the stack copying code. Instead move the selectdone variable into the g struct. Change-Id: Id246aaf18077c625adef7ca2d62794afef1bdd1b Reviewed-on: https://go-review.googlesource.com/53390 Reviewed-by: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/runtime/chan.go')
-rw-r--r--src/runtime/chan.go18
1 files changed, 12 insertions, 6 deletions
diff --git a/src/runtime/chan.go b/src/runtime/chan.go
index b34333e605..872f17bb84 100644
--- a/src/runtime/chan.go
+++ b/src/runtime/chan.go
@@ -222,7 +222,7 @@ func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
mysg.elem = ep
mysg.waitlink = nil
mysg.g = gp
- mysg.selectdone = nil
+ mysg.isSelect = false
mysg.c = c
gp.waiting = mysg
gp.param = nil
@@ -507,7 +507,7 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
mysg.waitlink = nil
gp.waiting = mysg
mysg.g = gp
- mysg.selectdone = nil
+ mysg.isSelect = false
mysg.c = c
gp.param = nil
c.recvq.enqueue(mysg)
@@ -711,10 +711,16 @@ func (q *waitq) dequeue() *sudog {
sgp.next = nil // mark as removed (see dequeueSudog)
}
- // if sgp participates in a select and is already signaled, ignore it
- if sgp.selectdone != nil {
- // claim the right to signal
- if *sgp.selectdone != 0 || !atomic.Cas(sgp.selectdone, 0, 1) {
+ // if a goroutine was put on this queue because of a
+ // select, there is a small window between the goroutine
+ // being woken up by a different case and it grabbing the
+ // channel locks. Once it has the lock
+ // it removes itself from the queue, so we won't see it after that.
+ // We use a flag in the G struct to tell us when someone
+ // else has won the race to signal this goroutine but the goroutine
+ // hasn't removed itself from the queue yet.
+ if sgp.isSelect {
+ if !atomic.Cas(&sgp.g.selectDone, 0, 1) {
continue
}
}