This libgo patch adds a success field to the sudog structure used to
manage select statements.  This is a step toward enabling some
compiler changes as part of upgrading to the Go 1.16beta1 release.
Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian
3b2d8145a4c349058d76ce299ea7eea605572004
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)

Reply via email to