aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/chan.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/chan.go')
-rw-r--r--src/runtime/chan.go25
1 files changed, 24 insertions, 1 deletions
diff --git a/src/runtime/chan.go b/src/runtime/chan.go
index b14ebc31d2..ae26be3c42 100644
--- a/src/runtime/chan.go
+++ b/src/runtime/chan.go
@@ -36,6 +36,7 @@ type hchan struct {
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
+ timer *timer // timer feeding this chan
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
@@ -426,12 +427,19 @@ func closechan(c *hchan) {
}
// empty reports whether a read from c would block (that is, the channel is
-// empty). It uses a single atomic read of mutable state.
+// empty). It is atomically correct and sequentially consistent at the moment
+// it returns, but since the channel is unlocked, the channel may become
+// non-empty immediately afterward.
func empty(c *hchan) bool {
// c.dataqsiz is immutable.
if c.dataqsiz == 0 {
return atomic.Loadp(unsafe.Pointer(&c.sendq.first)) == nil
}
+ // c.timer is also immutable (it is set after make(chan) but before any channel operations).
+ // All timer channels have dataqsiz > 0.
+ if c.timer != nil {
+ c.timer.maybeRunChan()
+ }
return atomic.Loaduint(&c.qcount) == 0
}
@@ -470,6 +478,10 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
throw("unreachable")
}
+ if c.timer != nil {
+ c.timer.maybeRunChan()
+ }
+
// Fast path: check for failed non-blocking operation without acquiring the lock.
if !block && empty(c) {
// After observing that the channel is not ready for receiving, we observe whether the
@@ -570,11 +582,16 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
mysg.elem = ep
mysg.waitlink = nil
gp.waiting = mysg
+
mysg.g = gp
mysg.isSelect = false
mysg.c = c
gp.param = nil
c.recvq.enqueue(mysg)
+ if c.timer != nil {
+ blockTimerChan(c)
+ }
+
// Signal to anyone trying to shrink our stack that we're about
// to park on a channel. The window between when this G's status
// changes and when we set gp.activeStackChans is not safe for
@@ -586,6 +603,9 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
if mysg != gp.waiting {
throw("G waiting list is corrupted")
}
+ if c.timer != nil {
+ unblockTimerChan(c)
+ }
gp.waiting = nil
gp.activeStackChans = false
if mysg.releasetime > 0 {
@@ -728,6 +748,9 @@ func chanlen(c *hchan) int {
if c == nil {
return 0
}
+ if c.timer != nil {
+ c.timer.maybeRunChan()
+ }
return int(c.qcount)
}