aboutsummaryrefslogtreecommitdiff
path: root/libgo
diff options
context:
space:
mode:
Diffstat (limited to 'libgo')
-rw-r--r--libgo/go/reflect/value.go8
-rw-r--r--libgo/go/runtime/chan.go43
-rw-r--r--libgo/go/runtime/select.go135
3 files changed, 78 insertions, 108 deletions
diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go
index 208bb2f..8f6a93b 100644
--- a/libgo/go/reflect/value.go
+++ b/libgo/go/reflect/value.go
@@ -1160,7 +1160,7 @@ func (v Value) recv(nb bool) (val Value, ok bool) {
} else {
p = unsafe.Pointer(&val.ptr)
}
- selected, ok := chanrecv(v.typ, v.pointer(), nb, p)
+ selected, ok := chanrecv(v.pointer(), nb, p)
if !selected {
val = Value{}
}
@@ -1191,7 +1191,7 @@ func (v Value) send(x Value, nb bool) (selected bool) {
} else {
p = unsafe.Pointer(&x.ptr)
}
- return chansend(v.typ, v.pointer(), p, nb)
+ return chansend(v.pointer(), p, nb)
}
// Set assigns x to the value v.
@@ -2327,10 +2327,10 @@ func chanlen(ch unsafe.Pointer) int
// (due to the escapes() call in ValueOf).
//go:noescape
-func chanrecv(t *rtype, ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool)
+func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool)
//go:noescape
-func chansend(t *rtype, ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool
+func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool
func makechan(typ *rtype, size uint64) (ch unsafe.Pointer)
func makemap(t *rtype) (m unsafe.Pointer)
diff --git a/libgo/go/runtime/chan.go b/libgo/go/runtime/chan.go
index a9574dd..d2470bd 100644
--- a/libgo/go/runtime/chan.go
+++ b/libgo/go/runtime/chan.go
@@ -118,8 +118,8 @@ func chanbuf(c *hchan, i uint) unsafe.Pointer {
// entry point for c <- x from compiled code
//go:nosplit
-func chansend1(t *chantype, c *hchan, elem unsafe.Pointer) {
- chansend(t, c, elem, true, getcallerpc(unsafe.Pointer(&t)))
+func chansend1(c *hchan, elem unsafe.Pointer) {
+ chansend(c, elem, true, getcallerpc(unsafe.Pointer(&c)))
}
/*
@@ -134,14 +134,7 @@ func chansend1(t *chantype, c *hchan, elem unsafe.Pointer) {
* been closed. it is easiest to loop and re-run
* the operation; we'll see that it's now closed.
*/
-func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
- if raceenabled {
- raceReadObjectPC(t.elem, ep, callerpc, funcPC(chansend))
- }
- if msanenabled {
- msanread(ep, t.elem.size)
- }
-
+func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
if c == nil {
if !block {
return false
@@ -400,13 +393,13 @@ func closechan(c *hchan) {
// entry points for <- c from compiled code
//go:nosplit
-func chanrecv1(t *chantype, c *hchan, elem unsafe.Pointer) {
- chanrecv(t, c, elem, true)
+func chanrecv1(c *hchan, elem unsafe.Pointer) {
+ chanrecv(c, elem, true)
}
//go:nosplit
-func chanrecv2(t *chantype, c *hchan, elem unsafe.Pointer) (received bool) {
- _, received = chanrecv(t, c, elem, true)
+func chanrecv2(c *hchan, elem unsafe.Pointer) (received bool) {
+ _, received = chanrecv(c, elem, true)
return
}
@@ -416,7 +409,7 @@ func chanrecv2(t *chantype, c *hchan, elem unsafe.Pointer) (received bool) {
// Otherwise, if c is closed, zeros *ep and returns (true, false).
// Otherwise, fills in *ep with an element and returns (true, true).
// A non-nil ep must point to the heap or the caller's stack.
-func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
+func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
// raceenabled: don't need to check ep, as it is always on the stack
// or is new memory allocated by reflect.
@@ -609,8 +602,8 @@ func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func()) {
// ... bar
// }
//
-func selectnbsend(t *chantype, c *hchan, elem unsafe.Pointer) (selected bool) {
- return chansend(t, c, elem, false, getcallerpc(unsafe.Pointer(&t)))
+func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {
+ return chansend(c, elem, false, getcallerpc(unsafe.Pointer(&c)))
}
// compiler implements
@@ -630,8 +623,8 @@ func selectnbsend(t *chantype, c *hchan, elem unsafe.Pointer) (selected bool) {
// ... bar
// }
//
-func selectnbrecv(t *chantype, elem unsafe.Pointer, c *hchan) (selected bool) {
- selected, _ = chanrecv(t, c, elem, false)
+func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected bool) {
+ selected, _ = chanrecv(c, elem, false)
return
}
@@ -652,20 +645,20 @@ func selectnbrecv(t *chantype, elem unsafe.Pointer, c *hchan) (selected bool) {
// ... bar
// }
//
-func selectnbrecv2(t *chantype, elem unsafe.Pointer, received *bool, c *hchan) (selected bool) {
+func selectnbrecv2(elem unsafe.Pointer, received *bool, c *hchan) (selected bool) {
// TODO(khr): just return 2 values from this function, now that it is in Go.
- selected, *received = chanrecv(t, c, elem, false)
+ selected, *received = chanrecv(c, elem, false)
return
}
//go:linkname reflect_chansend reflect.chansend
-func reflect_chansend(t *chantype, c *hchan, elem unsafe.Pointer, nb bool) (selected bool) {
- return chansend(t, c, elem, !nb, getcallerpc(unsafe.Pointer(&t)))
+func reflect_chansend(c *hchan, elem unsafe.Pointer, nb bool) (selected bool) {
+ return chansend(c, elem, !nb, getcallerpc(unsafe.Pointer(&c)))
}
//go:linkname reflect_chanrecv reflect.chanrecv
-func reflect_chanrecv(t *chantype, c *hchan, nb bool, elem unsafe.Pointer) (selected bool, received bool) {
- return chanrecv(t, c, elem, !nb)
+func reflect_chanrecv(c *hchan, nb bool, elem unsafe.Pointer) (selected bool, received bool) {
+ return chanrecv(c, elem, !nb)
}
//go:linkname reflect_chanlen reflect.chanlen
diff --git a/libgo/go/runtime/select.go b/libgo/go/runtime/select.go
index 62c4049..f0cad1b 100644
--- a/libgo/go/runtime/select.go
+++ b/libgo/go/runtime/select.go
@@ -18,14 +18,14 @@ import (
//go:linkname selectdefault runtime.selectdefault
//go:linkname selectsend runtime.selectsend
//go:linkname selectrecv runtime.selectrecv
-//go:linkname selectrecv2 runtime.selectrecv2
//go:linkname selectgo runtime.selectgo
-const (
- debugSelect = false
+const debugSelect = false
+const (
// scase.kind
- caseRecv = iota
+ caseNil = iota
+ caseRecv
caseSend
caseDefault
)
@@ -47,10 +47,9 @@ type hselect struct {
type scase struct {
elem unsafe.Pointer // data element
c *hchan // chan
- pc uintptr // return pc
+ pc uintptr // return pc (for race detector / msan)
kind uint16
- index uint16 // case index
- receivedp *bool // pointer to received bool (recv2)
+ receivedp *bool // pointer to received bool, if any
releasetime int64
}
@@ -88,88 +87,64 @@ func newselect(sel *hselect, selsize int64, size int32) {
}
}
-func selectsend(sel *hselect, c *hchan, elem unsafe.Pointer, index int32) {
- // nil cases do not compete
- if c != nil {
- selectsendImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, index)
- }
- return
-}
-
-// cut in half to give stack a chance to split
-func selectsendImpl(sel *hselect, c *hchan, pc uintptr, elem unsafe.Pointer, index int32) {
+func selectsend(sel *hselect, c *hchan, elem unsafe.Pointer) {
+ pc := getcallerpc(unsafe.Pointer(&sel))
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.index = uint16(index)
cas.kind = caseSend
cas.elem = elem
if debugSelect {
- print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, " index=", cas.index, "\n")
- }
-}
-
-func selectrecv(sel *hselect, c *hchan, elem unsafe.Pointer, index int32) {
- // nil cases do not compete
- if c != nil {
- selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, nil, index)
+ print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, "\n")
}
- return
}
-func selectrecv2(sel *hselect, c *hchan, elem unsafe.Pointer, received *bool, index int32) {
- // nil cases do not compete
- if c != nil {
- selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, received, index)
- }
- return
-}
-
-func selectrecvImpl(sel *hselect, c *hchan, pc uintptr, elem unsafe.Pointer, received *bool, index int32) {
+func selectrecv(sel *hselect, c *hchan, elem unsafe.Pointer, received *bool) {
+ pc := getcallerpc(unsafe.Pointer(&sel))
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.index = uint16(index)
cas.kind = caseRecv
cas.elem = elem
cas.receivedp = received
if debugSelect {
- print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, " index=", cas.index, "\n")
+ print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, "\n")
}
}
-func selectdefault(sel *hselect, index int32) {
- selectdefaultImpl(sel, getcallerpc(unsafe.Pointer(&sel)), index)
- return
-}
-
-func selectdefaultImpl(sel *hselect, callerpc uintptr, index int32) {
+func selectdefault(sel *hselect) {
+ pc := getcallerpc(unsafe.Pointer(&sel))
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 = callerpc
+ cas.pc = pc
cas.c = nil
- cas.index = uint16(index)
cas.kind = caseDefault
if debugSelect {
- print("selectdefault s=", sel, " pc=", hex(cas.pc), " index=", cas.index, "\n")
+ print("selectdefault s=", sel, " pc=", hex(cas.pc), "\n")
}
}
@@ -193,14 +168,11 @@ func selunlock(scases []scase, lockorder []uint16) {
// the G that calls select runnable again and schedules it for execution.
// When the G runs on another M, it locks all the locks and frees sel.
// Now if the first M touches sel, it will access freed memory.
- n := len(scases)
- r := 0
- // skip the default case
- if n > 0 && scases[lockorder[0]].c == nil {
- r = 1
- }
- for i := n - 1; i >= r; i-- {
+ for i := len(scases) - 1; i >= 0; i-- {
c := scases[lockorder[i]].c
+ if c == nil {
+ break
+ }
if i > 0 && c == scases[lockorder[i-1]].c {
continue // will unlock it on the next iteration
}
@@ -241,20 +213,15 @@ func block() {
// *sel is on the current goroutine's stack (regardless of any
// escaping in selectgo).
//
-// selectgo does not return. Instead, it overwrites its return PC and
-// returns directly to the triggered select case. Because of this, it
-// cannot appear at the top of a split stack.
-func selectgo(sel *hselect) int32 {
- _, index := selectgoImpl(sel)
- return int32(index)
-}
-
-// selectgoImpl returns scase.pc and scase.so for the select
-// case which fired.
-func selectgoImpl(sel *hselect) (uintptr, uint16) {
+// 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 {
if debugSelect {
print("select: sel=", sel, "\n")
}
+ if sel.ncase != sel.tcase {
+ throw("selectgo: case count mismatch")
+ }
scaseslice := slice{unsafe.Pointer(&sel.scase), int(sel.ncase), int(sel.ncase)}
scases := *(*[]scase)(unsafe.Pointer(&scaseslice))
@@ -347,13 +314,19 @@ func selectgoImpl(sel *hselect) (uintptr, uint16) {
loop:
// pass 1 - look for something already waiting
+ var dfli int
var dfl *scase
+ var casi int
var cas *scase
for i := 0; i < int(sel.ncase); i++ {
- cas = &scases[pollorder[i]]
+ casi = int(pollorder[i])
+ cas = &scases[casi]
c = cas.c
switch cas.kind {
+ case caseNil:
+ continue
+
case caseRecv:
sg = c.sendq.dequeue()
if sg != nil {
@@ -382,12 +355,14 @@ loop:
}
case caseDefault:
+ dfli = casi
dfl = cas
}
}
if dfl != nil {
selunlock(scases, lockorder)
+ casi = dfli
cas = dfl
goto retc
}
@@ -400,7 +375,11 @@ loop:
}
nextp = &gp.waiting
for _, casei := range lockorder {
- cas = &scases[casei]
+ casi = int(casei)
+ cas = &scases[casi]
+ if cas.kind == caseNil {
+ continue
+ }
c = cas.c
sg := acquireSudog()
sg.g = gp
@@ -494,6 +473,7 @@ loop:
// otherwise they stack up on quiet channels
// record the successful case, if any.
// We singly-linked up the SudoGs in lock order.
+ casi = -1
cas = nil
sglist = gp.waiting
// Clear all elem before unlinking from gp.waiting.
@@ -506,11 +486,15 @@ loop:
for _, casei := range lockorder {
k = &scases[casei]
+ if k.kind == caseNil {
+ continue
+ }
if sglist.releasetime > 0 {
k.releasetime = sglist.releasetime
}
if sg == sglist {
// sg has already been dequeued by the G that woke us up.
+ casi = int(casei)
cas = k
} else {
c = k.c
@@ -659,7 +643,7 @@ retc:
if cas.releasetime > 0 {
blockevent(cas.releasetime-t0, 2)
}
- return cas.pc, cas.index
+ return casi
sclose:
// send on closed channel
@@ -703,22 +687,15 @@ func reflect_rselect(cases []runtimeSelect) (chosen int, recvOK bool) {
rc := &cases[i]
switch rc.dir {
case selectDefault:
- selectdefaultImpl(sel, uintptr(i), 0)
+ selectdefault(sel)
case selectSend:
- if rc.ch == nil {
- break
- }
- selectsendImpl(sel, rc.ch, uintptr(i), rc.val, 0)
+ selectsend(sel, rc.ch, rc.val)
case selectRecv:
- if rc.ch == nil {
- break
- }
- selectrecvImpl(sel, rc.ch, uintptr(i), rc.val, r, 0)
+ selectrecv(sel, rc.ch, rc.val, r)
}
}
- pc, _ := selectgoImpl(sel)
- chosen = int(pc)
+ chosen = selectgo(sel)
recvOK = *r
return
}