aboutsummaryrefslogtreecommitdiff
path: root/libgo
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2018-09-13 21:32:24 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-09-13 21:32:24 +0000
commit84cdf51de42f823b4ed0d65ef20ab0142607917b (patch)
treeb7d94851b891d2529f5f7eb23386a86f07563097 /libgo
parent283b9cafbd73b92b106e3f23b07f59af6761c78b (diff)
downloadgcc-84cdf51de42f823b4ed0d65ef20ab0142607917b.zip
gcc-84cdf51de42f823b4ed0d65ef20ab0142607917b.tar.gz
gcc-84cdf51de42f823b4ed0d65ef20ab0142607917b.tar.bz2
compiler, runtime: open code select
This is the gofrontend version of https://golang.org/cl/37933, https://golang.org/cl/37934, and https://golang.org/cl/37935. Open code the initialization of select cases. This is a step toward updating libgo to the 1.11 release. Reviewed-on: https://go-review.googlesource.com/135000 From-SVN: r264290
Diffstat (limited to 'libgo')
-rw-r--r--libgo/go/runtime/select.go266
1 files changed, 61 insertions, 205 deletions
diff --git a/libgo/go/runtime/select.go b/libgo/go/runtime/select.go
index 1c5124b..9dab052 100644
--- a/libgo/go/runtime/select.go
+++ b/libgo/go/runtime/select.go
@@ -7,146 +7,36 @@ package runtime
// This file contains the implementation of Go select statements.
import (
- "runtime/internal/sys"
"unsafe"
)
// For gccgo, use go:linkname to rename compiler-called functions to
// themselves, so that the compiler will export them.
//
-//go:linkname newselect runtime.newselect
-//go:linkname selectdefault runtime.selectdefault
-//go:linkname selectsend runtime.selectsend
-//go:linkname selectrecv runtime.selectrecv
//go:linkname selectgo runtime.selectgo
const debugSelect = false
+// scase.kind values.
+// Known to compiler.
+// Changes here must also be made in src/cmd/compile/internal/gc/select.go's walkselect.
const (
- // scase.kind
caseNil = iota
caseRecv
caseSend
caseDefault
)
-// Select statement header.
-// Known to compiler.
-// Changes here must also be made in src/cmd/internal/gc/select.go's selecttype.
-type hselect struct {
- tcase uint16 // total count of scase[]
- ncase uint16 // currently filled scase[]
- pollorder *uint16 // case poll order
- lockorder *uint16 // channel lock order
- scase [1]scase // one per case (in order of appearance)
-}
-
// Select case descriptor.
// Known to compiler.
-// Changes here must also be made in src/cmd/internal/gc/select.go's selecttype.
+// Changes here must also be made in src/cmd/internal/gc/select.go's scasetype.
type scase struct {
- elem unsafe.Pointer // data element
c *hchan // chan
- pc uintptr // return pc (for race detector / msan)
+ elem unsafe.Pointer // data element
kind uint16
- receivedp *bool // pointer to received bool, if any
releasetime int64
}
-var (
- chansendpc = funcPC(chansend)
- chanrecvpc = funcPC(chanrecv)
-)
-
-func selectsize(size uintptr) uintptr {
- selsize := unsafe.Sizeof(hselect{}) +
- (size-1)*unsafe.Sizeof(hselect{}.scase[0]) +
- size*unsafe.Sizeof(*hselect{}.lockorder) +
- size*unsafe.Sizeof(*hselect{}.pollorder)
- return round(selsize, sys.Int64Align)
-}
-
-func newselect(sel *hselect, selsize int64, size int32) {
- if selsize != int64(selectsize(uintptr(size))) {
- print("runtime: bad select size ", selsize, ", want ", selectsize(uintptr(size)), "\n")
- throw("bad select size")
- }
- if size != int32(uint16(size)) {
- throw("select size too large")
- }
- sel.tcase = uint16(size)
- sel.ncase = 0
- sel.lockorder = (*uint16)(add(unsafe.Pointer(&sel.scase), uintptr(size)*unsafe.Sizeof(hselect{}.scase[0])))
- sel.pollorder = (*uint16)(add(unsafe.Pointer(sel.lockorder), uintptr(size)*unsafe.Sizeof(*hselect{}.lockorder)))
-
- // For gccgo the temporary variable will not have been zeroed.
- memclrNoHeapPointers(unsafe.Pointer(&sel.scase), uintptr(size)*unsafe.Sizeof(hselect{}.scase[0])+uintptr(size)*unsafe.Sizeof(*hselect{}.lockorder)+uintptr(size)*unsafe.Sizeof(*hselect{}.pollorder))
-
- if debugSelect {
- print("newselect s=", sel, " size=", size, "\n")
- }
-}
-
-func selectsend(sel *hselect, c *hchan, elem unsafe.Pointer) {
- pc := getcallerpc()
- i := sel.ncase
- if i >= sel.tcase {
- throw("selectsend: too many cases")
- }
- sel.ncase = i + 1
- if c == nil {
- return
- }
- cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
- cas.pc = pc
- cas.c = c
- cas.kind = caseSend
- cas.elem = elem
-
- if debugSelect {
- print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, "\n")
- }
-}
-
-func selectrecv(sel *hselect, c *hchan, elem unsafe.Pointer, received *bool) {
- pc := getcallerpc()
- i := sel.ncase
- if i >= sel.tcase {
- throw("selectrecv: too many cases")
- }
- sel.ncase = i + 1
- if c == nil {
- return
- }
- cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
- cas.pc = pc
- cas.c = c
- cas.kind = caseRecv
- cas.elem = elem
- cas.receivedp = received
-
- if debugSelect {
- print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, "\n")
- }
-}
-
-func selectdefault(sel *hselect) {
- pc := getcallerpc()
- i := sel.ncase
- if i >= sel.tcase {
- throw("selectdefault: too many cases")
- }
- sel.ncase = i + 1
- cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
- cas.pc = pc
- cas.c = nil
- cas.kind = caseDefault
-
- if debugSelect {
- print("selectdefault s=", sel, " pc=", hex(cas.pc), "\n")
- }
-}
-
func sellock(scases []scase, lockorder []uint16) {
var c *hchan
for _, o := range lockorder {
@@ -209,26 +99,39 @@ func block() {
// selectgo implements the select statement.
//
-// *sel is on the current goroutine's stack (regardless of any
-// escaping in selectgo).
+// cas0 points to an array of type [ncases]scase, and order0 points to
+// an array of type [2*ncases]uint16. Both reside on the goroutine's
+// stack (regardless of any escaping in selectgo).
//
// selectgo returns the index of the chosen scase, which matches the
// ordinal position of its respective select{recv,send,default} call.
-func selectgo(sel *hselect) int {
+// Also, if the chosen scase was a receive operation, it returns whether
+// a value was received.
+func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
if debugSelect {
- print("select: sel=", sel, "\n")
- }
- if sel.ncase != sel.tcase {
- throw("selectgo: case count mismatch")
+ print("select: cas0=", cas0, "\n")
}
- scaseslice := slice{unsafe.Pointer(&sel.scase), int(sel.ncase), int(sel.ncase)}
- scases := *(*[]scase)(unsafe.Pointer(&scaseslice))
+ cas1 := (*[1 << 16]scase)(unsafe.Pointer(cas0))
+ order1 := (*[1 << 17]uint16)(unsafe.Pointer(order0))
+
+ scases := cas1[:ncases:ncases]
+ pollorder := order1[:ncases:ncases]
+ lockorder := order1[ncases:][:ncases:ncases]
+
+ // Replace send/receive cases involving nil channels with
+ // caseNil so logic below can assume non-nil channel.
+ for i := range scases {
+ cas := &scases[i]
+ if cas.c == nil && cas.kind != caseDefault {
+ *cas = scase{}
+ }
+ }
var t0 int64
if blockprofilerate > 0 {
t0 = cputicks()
- for i := 0; i < int(sel.ncase); i++ {
+ for i := 0; i < ncases; i++ {
scases[i].releasetime = -1
}
}
@@ -241,10 +144,13 @@ func selectgo(sel *hselect) int {
// cases correctly, and they are rare enough not to bother
// optimizing (and needing to test).
+ // needed for gccgo, which doesn't zero pollorder
+ if ncases > 0 {
+ pollorder[0] = 0
+ }
+
// generate permuted order
- pollslice := slice{unsafe.Pointer(sel.pollorder), int(sel.ncase), int(sel.ncase)}
- pollorder := *(*[]uint16)(unsafe.Pointer(&pollslice))
- for i := 1; i < int(sel.ncase); i++ {
+ for i := 1; i < ncases; i++ {
j := fastrandn(uint32(i + 1))
pollorder[i] = pollorder[j]
pollorder[j] = uint16(i)
@@ -252,9 +158,7 @@ func selectgo(sel *hselect) int {
// sort the cases by Hchan address to get the locking order.
// simple heap sort, to guarantee n log n time and constant stack footprint.
- lockslice := slice{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)}
- lockorder := *(*[]uint16)(unsafe.Pointer(&lockslice))
- for i := 0; i < int(sel.ncase); i++ {
+ for i := 0; i < ncases; i++ {
j := i
// Start with the pollorder to permute cases on the same channel.
c := scases[pollorder[i]].c
@@ -265,7 +169,7 @@ func selectgo(sel *hselect) int {
}
lockorder[j] = pollorder[i]
}
- for i := int(sel.ncase) - 1; i >= 0; i-- {
+ for i := ncases - 1; i >= 0; i-- {
o := lockorder[i]
c := scases[o].c
lockorder[i] = lockorder[0]
@@ -287,14 +191,15 @@ func selectgo(sel *hselect) int {
}
lockorder[j] = o
}
- /*
- for i := 0; i+1 < int(sel.ncase); i++ {
+
+ if debugSelect {
+ for i := 0; i+1 < ncases; i++ {
if scases[lockorder[i]].c.sortkey() > scases[lockorder[i+1]].c.sortkey() {
print("i=", i, " x=", lockorder[i], " y=", lockorder[i+1], "\n")
throw("select: broken sort")
}
}
- */
+ }
// lock all the channels involved in the select
sellock(scases, lockorder)
@@ -316,7 +221,8 @@ loop:
var dfl *scase
var casi int
var cas *scase
- for i := 0; i < int(sel.ncase); i++ {
+ var recvOK bool
+ for i := 0; i < ncases; i++ {
casi = int(pollorder[i])
cas = &scases[casi]
c = cas.c
@@ -338,9 +244,6 @@ loop:
}
case caseSend:
- if raceenabled {
- racereadpc(unsafe.Pointer(c), cas.pc, chansendpc)
- }
if c.closed != 0 {
goto sclose
}
@@ -469,26 +372,11 @@ loop:
c = cas.c
if debugSelect {
- print("wait-return: sel=", sel, " c=", c, " cas=", cas, " kind=", cas.kind, "\n")
+ print("wait-return: cas0=", cas0, " c=", c, " cas=", cas, " kind=", cas.kind, "\n")
}
- if cas.kind == caseRecv && cas.receivedp != nil {
- *cas.receivedp = true
- }
-
- if raceenabled {
- if cas.kind == caseRecv && cas.elem != nil {
- raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
- } else if cas.kind == caseSend {
- raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
- }
- }
- if msanenabled {
- if cas.kind == caseRecv && cas.elem != nil {
- msanwrite(cas.elem, c.elemtype.size)
- } else if cas.kind == caseSend {
- msanread(cas.elem, c.elemtype.size)
- }
+ if cas.kind == caseRecv {
+ recvOK = true
}
selunlock(scases, lockorder)
@@ -496,19 +384,7 @@ loop:
bufrecv:
// can receive from buffer
- if raceenabled {
- if cas.elem != nil {
- raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
- }
- raceacquire(chanbuf(c, c.recvx))
- racerelease(chanbuf(c, c.recvx))
- }
- if msanenabled && cas.elem != nil {
- msanwrite(cas.elem, c.elemtype.size)
- }
- if cas.receivedp != nil {
- *cas.receivedp = true
- }
+ recvOK = true
qp = chanbuf(c, c.recvx)
if cas.elem != nil {
typedmemmove(c.elemtype, cas.elem, qp)
@@ -524,14 +400,6 @@ bufrecv:
bufsend:
// can send to buffer
- if raceenabled {
- raceacquire(chanbuf(c, c.sendx))
- racerelease(chanbuf(c, c.sendx))
- raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
- }
- if msanenabled {
- msanread(cas.elem, c.elemtype.size)
- }
typedmemmove(c.elemtype, chanbuf(c, c.sendx), cas.elem)
c.sendx++
if c.sendx == c.dataqsiz {
@@ -545,19 +413,15 @@ recv:
// can receive from sleeping sender (sg)
recv(c, sg, cas.elem, func() { selunlock(scases, lockorder) }, 2)
if debugSelect {
- print("syncrecv: sel=", sel, " c=", c, "\n")
- }
- if cas.receivedp != nil {
- *cas.receivedp = true
+ print("syncrecv: cas0=", cas0, " c=", c, "\n")
}
+ recvOK = true
goto retc
rclose:
// read at end of closed channel
selunlock(scases, lockorder)
- if cas.receivedp != nil {
- *cas.receivedp = false
- }
+ recvOK = false
if cas.elem != nil {
typedmemclr(c.elemtype, cas.elem)
}
@@ -568,15 +432,9 @@ rclose:
send:
// can send to a sleeping receiver (sg)
- if raceenabled {
- raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
- }
- if msanenabled {
- msanread(cas.elem, c.elemtype.size)
- }
send(c, sg, cas.elem, func() { selunlock(scases, lockorder) }, 2)
if debugSelect {
- print("syncsend: sel=", sel, " c=", c, "\n")
+ print("syncsend: cas0=", cas0, " c=", c, "\n")
}
goto retc
@@ -591,7 +449,7 @@ retc:
checkPreempt()
}
- return casi
+ return casi, recvOK
sclose:
// send on closed channel
@@ -625,27 +483,25 @@ const (
)
//go:linkname reflect_rselect reflect.rselect
-func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) {
- // flagNoScan is safe here, because all objects are also referenced from cases.
- size := selectsize(uintptr(len(cases)))
- sel := (*hselect)(mallocgc(size, nil, true))
- newselect(sel, int64(size), int32(len(cases)))
- r := new(bool)
+func reflect_rselect(cases []runtimeSelect) (int, bool) {
+ if len(cases) == 0 {
+ block()
+ }
+ sel := make([]scase, len(cases))
+ order := make([]uint16, 2*len(cases))
for i := range cases {
rc := &cases[i]
switch rc.dir {
case selectDefault:
- selectdefault(sel)
+ sel[i] = scase{kind: caseDefault}
case selectSend:
- selectsend(sel, rc.ch, rc.val)
+ sel[i] = scase{kind: caseSend, c: rc.ch, elem: rc.val}
case selectRecv:
- selectrecv(sel, rc.ch, rc.val, r)
+ sel[i] = scase{kind: caseRecv, c: rc.ch, elem: rc.val}
}
}
- chosen = selectgo(sel)
- recvOK = *r
- return
+ return selectgo(&sel[0], &order[0], len(cases))
}
func (q *waitq) dequeueSudoG(sgp *sudog) {