aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/runtime/chan.go
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2021-02-02 12:42:10 -0800
committerIan Lance Taylor <iant@golang.org>2021-02-02 12:42:10 -0800
commit8910f1cd79445bbe2da01f8ccf7c37909349529e (patch)
treeba67a346969358fd7cc2b7c12384479de8364cab /libgo/go/runtime/chan.go
parent45c32be1f96ace25b66c34a84818dc5e07e9d516 (diff)
parent8e4a738d2540ab6aff77506d368bf4e3fa6963bd (diff)
downloadgcc-8910f1cd79445bbe2da01f8ccf7c37909349529e.zip
gcc-8910f1cd79445bbe2da01f8ccf7c37909349529e.tar.gz
gcc-8910f1cd79445bbe2da01f8ccf7c37909349529e.tar.bz2
Merge from trunk revision 8e4a738d2540ab6aff77506d368bf4e3fa6963bd.
Diffstat (limited to 'libgo/go/runtime/chan.go')
-rw-r--r--libgo/go/runtime/chan.go79
1 files changed, 56 insertions, 23 deletions
diff --git a/libgo/go/runtime/chan.go b/libgo/go/runtime/chan.go
index 8e104f1..7878a8f 100644
--- a/libgo/go/runtime/chan.go
+++ b/libgo/go/runtime/chan.go
@@ -232,8 +232,7 @@ func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
// Space is available in the channel buffer. Enqueue the element to send.
qp := chanbuf(c, c.sendx)
if raceenabled {
- raceacquire(qp)
- racerelease(qp)
+ racenotify(c, c.sendx, nil)
}
typedmemmove(c.elemtype, qp, ep)
c.sendx++
@@ -285,18 +284,19 @@ func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
}
gp.waiting = nil
gp.activeStackChans = false
- if gp.param == nil {
- if c.closed == 0 {
- throw("chansend: spurious wakeup")
- }
- panic(plainError("send on closed channel"))
- }
+ closed := !mysg.success
gp.param = nil
if mysg.releasetime > 0 {
blockevent(mysg.releasetime-t0, 2)
}
mysg.c = nil
releaseSudog(mysg)
+ if closed {
+ if c.closed == 0 {
+ throw("chansend: spurious wakeup")
+ }
+ panic(plainError("send on closed channel"))
+ }
return true
}
@@ -314,11 +314,8 @@ func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
// Pretend we go through the buffer, even though
// we copy directly. Note that we need to increment
// the head/tail locations only when raceenabled.
- qp := chanbuf(c, c.recvx)
- raceacquire(qp)
- racerelease(qp)
- raceacquireg(sg.g, qp)
- racereleaseg(sg.g, qp)
+ racenotify(c, c.recvx, nil)
+ racenotify(c, c.recvx, sg)
c.recvx++
if c.recvx == c.dataqsiz {
c.recvx = 0
@@ -333,6 +330,7 @@ func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
gp := sg.g
unlockf()
gp.param = unsafe.Pointer(sg)
+ sg.success = true
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
@@ -406,7 +404,8 @@ func closechan(c *hchan) {
sg.releasetime = cputicks()
}
gp := sg.g
- gp.param = nil
+ gp.param = unsafe.Pointer(sg)
+ sg.success = false
if raceenabled {
raceacquireg(gp, c.raceaddr())
}
@@ -424,7 +423,8 @@ func closechan(c *hchan) {
sg.releasetime = cputicks()
}
gp := sg.g
- gp.param = nil
+ gp.param = unsafe.Pointer(sg)
+ sg.success = false
if raceenabled {
raceacquireg(gp, c.raceaddr())
}
@@ -553,8 +553,7 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
// Receive directly from queue
qp := chanbuf(c, c.recvx)
if raceenabled {
- raceacquire(qp)
- racerelease(qp)
+ racenotify(c, c.recvx, nil)
}
if ep != nil {
typedmemmove(c.elemtype, ep, qp)
@@ -607,11 +606,11 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
if mysg.releasetime > 0 {
blockevent(mysg.releasetime-t0, 2)
}
- closed := gp.param == nil
+ success := mysg.success
gp.param = nil
mysg.c = nil
releaseSudog(mysg)
- return true, !closed
+ return true, success
}
// recv processes a receive operation on a full channel c.
@@ -643,10 +642,8 @@ func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
// queue is full, those are both the same slot.
qp := chanbuf(c, c.recvx)
if raceenabled {
- raceacquire(qp)
- racerelease(qp)
- raceacquireg(sg.g, qp)
- racereleaseg(sg.g, qp)
+ racenotify(c, c.recvx, nil)
+ racenotify(c, c.recvx, sg)
}
// copy data from queue to receiver
if ep != nil {
@@ -664,6 +661,7 @@ func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
gp := sg.g
unlockf()
gp.param = unsafe.Pointer(sg)
+ sg.success = true
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
@@ -856,3 +854,38 @@ func racesync(c *hchan, sg *sudog) {
racereleaseg(sg.g, chanbuf(c, 0))
raceacquire(chanbuf(c, 0))
}
+
+// Notify the race detector of a send or receive involving buffer entry idx
+// and a channel c or its communicating partner sg.
+// This function handles the special case of c.elemsize==0.
+func racenotify(c *hchan, idx uint, sg *sudog) {
+ // We could have passed the unsafe.Pointer corresponding to entry idx
+ // instead of idx itself. However, in a future version of this function,
+ // we can use idx to better handle the case of elemsize==0.
+ // A future improvement to the detector is to call TSan with c and idx:
+ // this way, Go will continue to not allocating buffer entries for channels
+ // of elemsize==0, yet the race detector can be made to handle multiple
+ // sync objects underneath the hood (one sync object per idx)
+ qp := chanbuf(c, idx)
+ // When elemsize==0, we don't allocate a full buffer for the channel.
+ // Instead of individual buffer entries, the race detector uses the
+ // c.buf as the only buffer entry. This simplification prevents us from
+ // following the memory model's happens-before rules (rules that are
+ // implemented in racereleaseacquire). Instead, we accumulate happens-before
+ // information in the synchronization object associated with c.buf.
+ if c.elemsize == 0 {
+ if sg == nil {
+ raceacquire(qp)
+ racerelease(qp)
+ } else {
+ raceacquireg(sg.g, qp)
+ racereleaseg(sg.g, qp)
+ }
+ } else {
+ if sg == nil {
+ racereleaseacquire(qp)
+ } else {
+ racereleaseacquireg(sg.g, qp)
+ }
+ }
+}