public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-6308] runtime: add "success" field to sudog
@ 2020-12-22 20:13 Ian Lance Taylor
  0 siblings, 0 replies; only message in thread
From: Ian Lance Taylor @ 2020-12-22 20:13 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:3b2d8145a4c349058d76ce299ea7eea605572004

commit r11-6308-g3b2d8145a4c349058d76ce299ea7eea605572004
Author: Ian Lance Taylor <iant@golang.org>
Date:   Mon Dec 21 21:20:59 2020 -0800

    runtime: add "success" field to sudog
    
    This is the gofrontend version of https://golang.org/cl/245019.
    Original CL description:
    
        The current wakeup protocol for channel communications is that the
        second goroutine sets gp.param to the sudog when a value is
        successfully communicated over the channel, and to nil when the wakeup
        is due to closing the channel.
    
        Setting nil to indicate channel closure works okay for chansend and
        chanrecv, because they're only communicating with one channel, so they
        know it must be the channel that was closed. However, it means
        selectgo has to re-poll all of the channels to figure out which one
        was closed.
    
        This commit adds a "success" field to sudog, and changes the wakeup
        protocol to always set gp.param to sg, and to use sg.success to
        indicate successful communication vs channel closure.
    
        While here, this also reorganizes the chansend code slightly so that
        the sudog is still released to the pool if the send blocks and then is
        awoken because the channel closed.
    
        For golang/go#40410
    
    This is being brought over to gofrontend as a step toward upgrading to
    Go1.16beta1, setting up for more compiler changes related to select handling.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/279734

Diff:
---
 gcc/go/gofrontend/MERGE      |  2 +-
 libgo/go/runtime/chan.go     | 25 +++++++++++++++----------
 libgo/go/runtime/runtime2.go |  6 ++++++
 libgo/go/runtime/select.go   | 19 +++++++------------
 4 files changed, 29 insertions(+), 23 deletions(-)

diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index a7df8433403..bb537f152b9 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-d0e56e82bb298268ec0f306fef99a715c892d4a7
+eca96e39cb895805b617e0e1f184f893ed3e46bb
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
diff --git a/libgo/go/runtime/chan.go b/libgo/go/runtime/chan.go
index 8e104f14140..1b20aff6ea3 100644
--- a/libgo/go/runtime/chan.go
+++ b/libgo/go/runtime/chan.go
@@ -285,18 +285,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
 }
 
@@ -333,6 +334,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 +408,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 +427,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())
 		}
@@ -607,11 +611,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.
@@ -664,6 +668,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()
 	}
diff --git a/libgo/go/runtime/runtime2.go b/libgo/go/runtime/runtime2.go
index bf3fbac14c8..f30d1bcde6e 100644
--- a/libgo/go/runtime/runtime2.go
+++ b/libgo/go/runtime/runtime2.go
@@ -354,6 +354,12 @@ type sudog struct {
 	// g.selectDone must be CAS'd to win the wake-up race.
 	isSelect bool
 
+	// success indicates whether communication over channel c
+	// succeeded. It is true if the goroutine was awoken because a
+	// value was delivered over channel c, and false if awoken
+	// because c was closed.
+	success bool
+
 	parent   *sudog // semaRoot binary tree
 	waitlink *sudog // g.waiting list or semaRoot
 	waittail *sudog // semaRoot
diff --git a/libgo/go/runtime/select.go b/libgo/go/runtime/select.go
index d5087fbc045..c31aa344b55 100644
--- a/libgo/go/runtime/select.go
+++ b/libgo/go/runtime/select.go
@@ -235,10 +235,10 @@ func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
 		nextp  **sudog
 	)
 
-loop:
 	// pass 1 - look for something already waiting
 	var casi int
 	var cas *scase
+	var caseSuccess bool
 	var caseReleaseTime int64 = -1
 	var recvOK bool
 	for _, casei := range pollorder {
@@ -335,6 +335,7 @@ loop:
 	// We singly-linked up the SudoGs in lock order.
 	casi = -1
 	cas = nil
+	caseSuccess = false
 	sglist = gp.waiting
 	// Clear all elem before unlinking from gp.waiting.
 	for sg1 := gp.waiting; sg1 != nil; sg1 = sg1.waitlink {
@@ -350,6 +351,7 @@ loop:
 			// sg has already been dequeued by the G that woke us up.
 			casi = int(casei)
 			cas = k
+			caseSuccess = sglist.success
 			if sglist.releasetime > 0 {
 				caseReleaseTime = sglist.releasetime
 			}
@@ -368,16 +370,7 @@ loop:
 	}
 
 	if cas == nil {
-		// We can wake up with gp.param == nil (so cas == nil)
-		// when a channel involved in the select has been closed.
-		// It is easiest to loop and re-run the operation;
-		// we'll see that it's now closed.
-		// Maybe some day we can signal the close explicitly,
-		// but we'd have to distinguish close-on-reader from close-on-writer.
-		// It's easiest not to duplicate the code and just recheck above.
-		// We know that something closed, and things never un-close,
-		// so we won't block again.
-		goto loop
+		throw("selectgo: bad wakeup")
 	}
 
 	c = cas.c
@@ -387,7 +380,9 @@ loop:
 	}
 
 	if cas.kind == caseRecv {
-		recvOK = true
+		recvOK = caseSuccess
+	} else if cas.kind == caseSend && !caseSuccess {
+		goto sclose
 	}
 
 	selunlock(scases, lockorder)


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-12-22 20:13 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-22 20:13 [gcc r11-6308] runtime: add "success" field to sudog Ian Lance Taylor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).