diff options
author | Daniel Morsing <daniel.morsing@gmail.com> | 2017-08-02 19:01:17 +0100 |
---|---|---|
committer | Daniel Morsing <daniel.morsing@gmail.com> | 2017-08-15 19:18:00 +0000 |
commit | 32b94f13cf35e619a0dae6e1e381adc7e182d283 (patch) | |
tree | 64d58aca0d055ac7ced3b9a611d5ed36e9980d6c /src/runtime/chan.go | |
parent | 89d74f54168619cf1f36b6868626fbb1237c1deb (diff) | |
download | go-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.go | 18 |
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 } } |