2014-09-02 15:13:29 -06:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package runtime
|
|
|
|
|
|
|
|
// This file contains the implementation of Go select statements.
|
|
|
|
|
2015-11-11 10:39:30 -07:00
|
|
|
import (
|
|
|
|
"runtime/internal/sys"
|
|
|
|
"unsafe"
|
|
|
|
)
|
2014-09-02 15:13:29 -06:00
|
|
|
|
|
|
|
const (
|
|
|
|
debugSelect = false
|
2015-02-14 06:42:51 -07:00
|
|
|
|
|
|
|
// scase.kind
|
|
|
|
caseRecv = iota
|
|
|
|
caseSend
|
|
|
|
caseDefault
|
2014-09-02 15:13:29 -06:00
|
|
|
)
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
// 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 **hchan // 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.
|
|
|
|
type scase struct {
|
|
|
|
elem unsafe.Pointer // data element
|
|
|
|
c *hchan // chan
|
|
|
|
pc uintptr // return pc
|
|
|
|
kind uint16
|
|
|
|
so uint16 // vararg of selected bool
|
|
|
|
receivedp *bool // pointer to received bool (recv2)
|
|
|
|
releasetime int64
|
|
|
|
}
|
|
|
|
|
2014-09-02 15:13:29 -06:00
|
|
|
var (
|
2014-09-03 09:10:38 -06:00
|
|
|
chansendpc = funcPC(chansend)
|
|
|
|
chanrecvpc = funcPC(chanrecv)
|
2014-09-02 15:13:29 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
func selectsize(size uintptr) uintptr {
|
2015-02-14 06:42:51 -07:00
|
|
|
selsize := unsafe.Sizeof(hselect{}) +
|
|
|
|
(size-1)*unsafe.Sizeof(hselect{}.scase[0]) +
|
|
|
|
size*unsafe.Sizeof(*hselect{}.lockorder) +
|
|
|
|
size*unsafe.Sizeof(*hselect{}.pollorder)
|
2015-11-11 10:39:30 -07:00
|
|
|
return round(selsize, sys.Int64Align)
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
func newselect(sel *hselect, selsize int64, size int32) {
|
2014-09-02 15:13:29 -06:00
|
|
|
if selsize != int64(selectsize(uintptr(size))) {
|
|
|
|
print("runtime: bad select size ", selsize, ", want ", selectsize(uintptr(size)), "\n")
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("bad select size")
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
sel.tcase = uint16(size)
|
|
|
|
sel.ncase = 0
|
2015-02-14 06:42:51 -07:00
|
|
|
sel.lockorder = (**hchan)(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)))
|
2014-09-02 15:13:29 -06:00
|
|
|
|
|
|
|
if debugSelect {
|
|
|
|
print("newselect s=", sel, " size=", size, "\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:nosplit
|
2015-02-14 06:42:51 -07:00
|
|
|
func selectsend(sel *hselect, c *hchan, elem unsafe.Pointer) (selected bool) {
|
2014-09-02 15:13:29 -06:00
|
|
|
// nil cases do not compete
|
|
|
|
if c != nil {
|
|
|
|
selectsendImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// cut in half to give stack a chance to split
|
2015-02-14 06:42:51 -07:00
|
|
|
func selectsendImpl(sel *hselect, c *hchan, pc uintptr, elem unsafe.Pointer, so uintptr) {
|
2014-09-02 15:13:29 -06:00
|
|
|
i := sel.ncase
|
|
|
|
if i >= sel.tcase {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("selectsend: too many cases")
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
sel.ncase = i + 1
|
|
|
|
cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
|
|
|
|
|
|
|
|
cas.pc = pc
|
2015-02-14 06:42:51 -07:00
|
|
|
cas.c = c
|
2014-09-02 15:13:29 -06:00
|
|
|
cas.so = uint16(so)
|
2015-02-14 06:42:51 -07:00
|
|
|
cas.kind = caseSend
|
2014-09-02 15:13:29 -06:00
|
|
|
cas.elem = elem
|
|
|
|
|
|
|
|
if debugSelect {
|
2015-02-14 06:42:51 -07:00
|
|
|
print("selectsend s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, " so=", cas.so, "\n")
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:nosplit
|
2015-02-14 06:42:51 -07:00
|
|
|
func selectrecv(sel *hselect, c *hchan, elem unsafe.Pointer) (selected bool) {
|
2014-09-02 15:13:29 -06:00
|
|
|
// nil cases do not compete
|
|
|
|
if c != nil {
|
|
|
|
selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, nil, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:nosplit
|
2015-02-14 06:42:51 -07:00
|
|
|
func selectrecv2(sel *hselect, c *hchan, elem unsafe.Pointer, received *bool) (selected bool) {
|
2014-09-02 15:13:29 -06:00
|
|
|
// nil cases do not compete
|
|
|
|
if c != nil {
|
|
|
|
selectrecvImpl(sel, c, getcallerpc(unsafe.Pointer(&sel)), elem, received, uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
func selectrecvImpl(sel *hselect, c *hchan, pc uintptr, elem unsafe.Pointer, received *bool, so uintptr) {
|
2014-09-02 15:13:29 -06:00
|
|
|
i := sel.ncase
|
|
|
|
if i >= sel.tcase {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("selectrecv: too many cases")
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
sel.ncase = i + 1
|
|
|
|
cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
|
|
|
|
cas.pc = pc
|
2015-02-14 06:42:51 -07:00
|
|
|
cas.c = c
|
2014-09-02 15:13:29 -06:00
|
|
|
cas.so = uint16(so)
|
2015-02-14 06:42:51 -07:00
|
|
|
cas.kind = caseRecv
|
2014-09-02 15:13:29 -06:00
|
|
|
cas.elem = elem
|
|
|
|
cas.receivedp = received
|
|
|
|
|
|
|
|
if debugSelect {
|
2015-02-14 06:42:51 -07:00
|
|
|
print("selectrecv s=", sel, " pc=", hex(cas.pc), " chan=", cas.c, " so=", cas.so, "\n")
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:nosplit
|
2015-02-14 06:42:51 -07:00
|
|
|
func selectdefault(sel *hselect) (selected bool) {
|
2014-09-02 15:13:29 -06:00
|
|
|
selectdefaultImpl(sel, getcallerpc(unsafe.Pointer(&sel)), uintptr(unsafe.Pointer(&selected))-uintptr(unsafe.Pointer(&sel)))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
func selectdefaultImpl(sel *hselect, callerpc uintptr, so uintptr) {
|
2014-09-02 15:13:29 -06:00
|
|
|
i := sel.ncase
|
|
|
|
if i >= sel.tcase {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("selectdefault: too many cases")
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
sel.ncase = i + 1
|
|
|
|
cas := (*scase)(add(unsafe.Pointer(&sel.scase), uintptr(i)*unsafe.Sizeof(sel.scase[0])))
|
|
|
|
cas.pc = callerpc
|
2015-02-14 06:42:51 -07:00
|
|
|
cas.c = nil
|
2014-09-02 15:13:29 -06:00
|
|
|
cas.so = uint16(so)
|
2015-02-14 06:42:51 -07:00
|
|
|
cas.kind = caseDefault
|
2014-09-02 15:13:29 -06:00
|
|
|
|
|
|
|
if debugSelect {
|
|
|
|
print("selectdefault s=", sel, " pc=", hex(cas.pc), " so=", cas.so, "\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
func sellock(sel *hselect) {
|
2015-04-10 16:01:54 -06:00
|
|
|
lockslice := slice{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)}
|
2014-09-02 15:13:29 -06:00
|
|
|
lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
|
|
|
|
var c *hchan
|
|
|
|
for _, c0 := range lockorder {
|
|
|
|
if c0 != nil && c0 != c {
|
|
|
|
c = c0
|
|
|
|
lock(&c.lock)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
func selunlock(sel *hselect) {
|
2014-09-02 15:13:29 -06:00
|
|
|
// We must be very careful here to not touch sel after we have unlocked
|
|
|
|
// the last lock, because sel can be freed right after the last unlock.
|
|
|
|
// Consider the following situation.
|
|
|
|
// First M calls runtime·park() in runtime·selectgo() passing the sel.
|
|
|
|
// Once runtime·park() has unlocked the last lock, another M makes
|
|
|
|
// 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 := int(sel.ncase)
|
|
|
|
r := 0
|
2015-04-10 16:01:54 -06:00
|
|
|
lockslice := slice{unsafe.Pointer(sel.lockorder), n, n}
|
2014-09-02 15:13:29 -06:00
|
|
|
lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
|
|
|
|
// skip the default case
|
|
|
|
if n > 0 && lockorder[0] == nil {
|
|
|
|
r = 1
|
|
|
|
}
|
|
|
|
for i := n - 1; i >= r; i-- {
|
|
|
|
c := lockorder[i]
|
|
|
|
if i > 0 && c == lockorder[i-1] {
|
|
|
|
continue // will unlock it on the next iteration
|
|
|
|
}
|
|
|
|
unlock(&c.lock)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-11 15:08:33 -07:00
|
|
|
func selparkcommit(gp *g, sel unsafe.Pointer) bool {
|
2015-02-14 06:42:51 -07:00
|
|
|
selunlock((*hselect)(sel))
|
2014-09-02 15:13:29 -06:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func block() {
|
2015-02-21 11:01:40 -07:00
|
|
|
gopark(nil, nil, "select (no cases)", traceEvGoStop, 1) // forever
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// overwrites return pc on stack to signal which case of the select
|
|
|
|
// to run, so cannot appear at the top of a split stack.
|
|
|
|
//go:nosplit
|
2015-02-14 06:42:51 -07:00
|
|
|
func selectgo(sel *hselect) {
|
2014-09-02 15:13:29 -06:00
|
|
|
pc, offset := selectgoImpl(sel)
|
|
|
|
*(*bool)(add(unsafe.Pointer(&sel), uintptr(offset))) = true
|
|
|
|
setcallerpc(unsafe.Pointer(&sel), pc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// selectgoImpl returns scase.pc and scase.so for the select
|
|
|
|
// case which fired.
|
2015-02-14 06:42:51 -07:00
|
|
|
func selectgoImpl(sel *hselect) (uintptr, uint16) {
|
2014-09-02 15:13:29 -06:00
|
|
|
if debugSelect {
|
|
|
|
print("select: sel=", sel, "\n")
|
|
|
|
}
|
|
|
|
|
2015-04-10 16:01:54 -06:00
|
|
|
scaseslice := slice{unsafe.Pointer(&sel.scase), int(sel.ncase), int(sel.ncase)}
|
2014-09-02 15:13:29 -06:00
|
|
|
scases := *(*[]scase)(unsafe.Pointer(&scaseslice))
|
|
|
|
|
|
|
|
var t0 int64
|
|
|
|
if blockprofilerate > 0 {
|
|
|
|
t0 = cputicks()
|
|
|
|
for i := 0; i < int(sel.ncase); i++ {
|
|
|
|
scases[i].releasetime = -1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The compiler rewrites selects that statically have
|
|
|
|
// only 0 or 1 cases plus default into simpler constructs.
|
|
|
|
// The only way we can end up with such small sel.ncase
|
|
|
|
// values here is for a larger select in which most channels
|
|
|
|
// have been nilled out. The general code handles those
|
|
|
|
// cases correctly, and they are rare enough not to bother
|
|
|
|
// optimizing (and needing to test).
|
|
|
|
|
|
|
|
// generate permuted order
|
2015-04-10 16:01:54 -06:00
|
|
|
pollslice := slice{unsafe.Pointer(sel.pollorder), int(sel.ncase), int(sel.ncase)}
|
2014-09-02 15:13:29 -06:00
|
|
|
pollorder := *(*[]uint16)(unsafe.Pointer(&pollslice))
|
2015-04-27 11:46:02 -06:00
|
|
|
for i := 1; i < int(sel.ncase); i++ {
|
2014-09-02 15:33:33 -06:00
|
|
|
j := int(fastrand1()) % (i + 1)
|
2014-09-02 15:13:29 -06:00
|
|
|
pollorder[i] = pollorder[j]
|
2014-12-18 12:48:39 -07:00
|
|
|
pollorder[j] = uint16(i)
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// sort the cases by Hchan address to get the locking order.
|
|
|
|
// simple heap sort, to guarantee n log n time and constant stack footprint.
|
2015-04-10 16:01:54 -06:00
|
|
|
lockslice := slice{unsafe.Pointer(sel.lockorder), int(sel.ncase), int(sel.ncase)}
|
2014-09-02 15:13:29 -06:00
|
|
|
lockorder := *(*[]*hchan)(unsafe.Pointer(&lockslice))
|
|
|
|
for i := 0; i < int(sel.ncase); i++ {
|
|
|
|
j := i
|
2015-02-14 06:42:51 -07:00
|
|
|
c := scases[j].c
|
2014-09-02 15:13:29 -06:00
|
|
|
for j > 0 && lockorder[(j-1)/2].sortkey() < c.sortkey() {
|
|
|
|
k := (j - 1) / 2
|
|
|
|
lockorder[j] = lockorder[k]
|
|
|
|
j = k
|
|
|
|
}
|
|
|
|
lockorder[j] = c
|
|
|
|
}
|
|
|
|
for i := int(sel.ncase) - 1; i >= 0; i-- {
|
|
|
|
c := lockorder[i]
|
|
|
|
lockorder[i] = lockorder[0]
|
|
|
|
j := 0
|
|
|
|
for {
|
|
|
|
k := j*2 + 1
|
|
|
|
if k >= i {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if k+1 < i && lockorder[k].sortkey() < lockorder[k+1].sortkey() {
|
|
|
|
k++
|
|
|
|
}
|
|
|
|
if c.sortkey() < lockorder[k].sortkey() {
|
|
|
|
lockorder[j] = lockorder[k]
|
|
|
|
j = k
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
lockorder[j] = c
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
for i := 0; i+1 < int(sel.ncase); i++ {
|
|
|
|
if lockorder[i].sortkey() > lockorder[i+1].sortkey() {
|
|
|
|
print("i=", i, " x=", lockorder[i], " y=", lockorder[i+1], "\n")
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("select: broken sort")
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
// lock all the channels involved in the select
|
|
|
|
sellock(sel)
|
|
|
|
|
|
|
|
var (
|
|
|
|
gp *g
|
|
|
|
done uint32
|
|
|
|
sg *sudog
|
|
|
|
c *hchan
|
|
|
|
k *scase
|
|
|
|
sglist *sudog
|
|
|
|
sgnext *sudog
|
2015-11-07 22:28:21 -07:00
|
|
|
qp unsafe.Pointer
|
2014-09-02 15:13:29 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
loop:
|
|
|
|
// pass 1 - look for something already waiting
|
|
|
|
var dfl *scase
|
|
|
|
var cas *scase
|
|
|
|
for i := 0; i < int(sel.ncase); i++ {
|
|
|
|
cas = &scases[pollorder[i]]
|
2015-02-14 06:42:51 -07:00
|
|
|
c = cas.c
|
2014-09-02 15:13:29 -06:00
|
|
|
|
|
|
|
switch cas.kind {
|
2015-02-14 06:42:51 -07:00
|
|
|
case caseRecv:
|
2015-11-07 22:28:21 -07:00
|
|
|
sg = c.sendq.dequeue()
|
|
|
|
if sg != nil {
|
|
|
|
goto recv
|
|
|
|
}
|
|
|
|
if c.qcount > 0 {
|
|
|
|
goto bufrecv
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
if c.closed != 0 {
|
|
|
|
goto rclose
|
|
|
|
}
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
case caseSend:
|
2014-09-02 15:13:29 -06:00
|
|
|
if raceenabled {
|
|
|
|
racereadpc(unsafe.Pointer(c), cas.pc, chansendpc)
|
|
|
|
}
|
|
|
|
if c.closed != 0 {
|
|
|
|
goto sclose
|
|
|
|
}
|
2015-11-07 22:28:21 -07:00
|
|
|
sg = c.recvq.dequeue()
|
|
|
|
if sg != nil {
|
|
|
|
goto send
|
|
|
|
}
|
|
|
|
if c.qcount < c.dataqsiz {
|
|
|
|
goto bufsend
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
case caseDefault:
|
2014-09-02 15:13:29 -06:00
|
|
|
dfl = cas
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if dfl != nil {
|
|
|
|
selunlock(sel)
|
|
|
|
cas = dfl
|
|
|
|
goto retc
|
|
|
|
}
|
|
|
|
|
|
|
|
// pass 2 - enqueue on all chans
|
|
|
|
gp = getg()
|
|
|
|
done = 0
|
2015-11-07 22:28:21 -07:00
|
|
|
if gp.waiting != nil {
|
|
|
|
throw("gp.waiting != nil")
|
|
|
|
}
|
2014-09-02 15:13:29 -06:00
|
|
|
for i := 0; i < int(sel.ncase); i++ {
|
|
|
|
cas = &scases[pollorder[i]]
|
2015-02-14 06:42:51 -07:00
|
|
|
c = cas.c
|
2014-09-02 15:13:29 -06:00
|
|
|
sg := acquireSudog()
|
|
|
|
sg.g = gp
|
2015-03-11 13:58:47 -06:00
|
|
|
// Note: selectdone is adjusted for stack copies in stack1.go:adjustsudogs
|
2014-09-02 15:13:29 -06:00
|
|
|
sg.selectdone = (*uint32)(noescape(unsafe.Pointer(&done)))
|
|
|
|
sg.elem = cas.elem
|
|
|
|
sg.releasetime = 0
|
|
|
|
if t0 != 0 {
|
|
|
|
sg.releasetime = -1
|
|
|
|
}
|
|
|
|
sg.waitlink = gp.waiting
|
|
|
|
gp.waiting = sg
|
|
|
|
|
|
|
|
switch cas.kind {
|
2015-02-14 06:42:51 -07:00
|
|
|
case caseRecv:
|
2014-09-02 15:13:29 -06:00
|
|
|
c.recvq.enqueue(sg)
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
case caseSend:
|
2014-09-02 15:13:29 -06:00
|
|
|
c.sendq.enqueue(sg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// wait for someone to wake us up
|
|
|
|
gp.param = nil
|
2015-11-07 22:28:21 -07:00
|
|
|
gopark(selparkcommit, unsafe.Pointer(sel), "select", traceEvGoBlockSelect, 2)
|
2014-09-02 15:13:29 -06:00
|
|
|
|
|
|
|
// someone woke us up
|
|
|
|
sellock(sel)
|
|
|
|
sg = (*sudog)(gp.param)
|
2014-10-02 14:49:11 -06:00
|
|
|
gp.param = nil
|
2014-09-02 15:13:29 -06:00
|
|
|
|
|
|
|
// pass 3 - dequeue from unsuccessful chans
|
|
|
|
// otherwise they stack up on quiet channels
|
|
|
|
// record the successful case, if any.
|
|
|
|
// We singly-linked up the SudoGs in case order, so when
|
|
|
|
// iterating through the linked list they are in reverse order.
|
|
|
|
cas = nil
|
|
|
|
sglist = gp.waiting
|
2014-10-02 14:49:11 -06:00
|
|
|
// Clear all elem before unlinking from gp.waiting.
|
|
|
|
for sg1 := gp.waiting; sg1 != nil; sg1 = sg1.waitlink {
|
2014-10-03 13:33:29 -06:00
|
|
|
sg1.selectdone = nil
|
2014-10-02 14:49:11 -06:00
|
|
|
sg1.elem = nil
|
|
|
|
}
|
2014-09-02 15:13:29 -06:00
|
|
|
gp.waiting = nil
|
|
|
|
for i := int(sel.ncase) - 1; i >= 0; i-- {
|
|
|
|
k = &scases[pollorder[i]]
|
|
|
|
if sglist.releasetime > 0 {
|
|
|
|
k.releasetime = sglist.releasetime
|
|
|
|
}
|
|
|
|
if sg == sglist {
|
2014-12-08 11:11:08 -07:00
|
|
|
// sg has already been dequeued by the G that woke us up.
|
2014-09-02 15:13:29 -06:00
|
|
|
cas = k
|
|
|
|
} else {
|
2015-02-14 06:42:51 -07:00
|
|
|
c = k.c
|
|
|
|
if k.kind == caseSend {
|
2014-10-18 22:02:49 -06:00
|
|
|
c.sendq.dequeueSudoG(sglist)
|
2014-09-02 15:13:29 -06:00
|
|
|
} else {
|
2014-10-18 22:02:49 -06:00
|
|
|
c.recvq.dequeueSudoG(sglist)
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
sgnext = sglist.waitlink
|
runtime: fix sudog leak
The SudoG used to sit on the stack, so it was cheap to allocated
and didn't need to be cleaned up when finished.
For the conversion to Go, we had to move sudog off the stack
for a few reasons, so we added a cache of recently used sudogs
to keep allocation cheap. But we didn't add any of the necessary
cleanup before adding a SudoG to the new cache, and so the cached
SudoGs had stale pointers inside them that have caused all sorts
of awful, hard to debug problems.
CL 155760043 made sure SudoG.elem is cleaned up.
CL 150520043 made sure SudoG.selectdone is cleaned up.
This CL makes sure SudoG.next, SudoG.prev, and SudoG.waitlink
are cleaned up. I should have done this when I did the other two
fields; instead I wasted a week tracking down a leak they caused.
A dangling SudoG.waitlink can point into a sudogcache list that
has been "forgotten" in order to let the GC collect it, but that
dangling .waitlink keeps the list from being collected.
And then the list holding the SudoG with the dangling waitlink
can find itself in the same situation, and so on. We end up
with lists of lists of unusable SudoGs that are still linked into
the object graph and never collected (given the right mix of
non-trivial selects and non-channel synchronization).
More details in golang.org/issue/9110.
Fixes #9110.
LGTM=r
R=r
CC=dvyukov, golang-codereviews, iant, khr
https://golang.org/cl/177870043
2014-11-16 14:44:45 -07:00
|
|
|
sglist.waitlink = nil
|
2014-09-02 15:13:29 -06:00
|
|
|
releaseSudog(sglist)
|
|
|
|
sglist = sgnext
|
|
|
|
}
|
|
|
|
|
|
|
|
if cas == nil {
|
2015-11-07 22:28:21 -07:00
|
|
|
// This can happen if we were woken up by a close().
|
|
|
|
// TODO: figure that out explicitly so we don't need this loop.
|
2014-09-02 15:13:29 -06:00
|
|
|
goto loop
|
|
|
|
}
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
c = cas.c
|
2014-09-02 15:13:29 -06:00
|
|
|
|
|
|
|
if debugSelect {
|
|
|
|
print("wait-return: sel=", sel, " c=", c, " cas=", cas, " kind=", cas.kind, "\n")
|
|
|
|
}
|
|
|
|
|
2015-02-14 06:42:51 -07:00
|
|
|
if cas.kind == caseRecv {
|
2014-09-02 15:13:29 -06:00
|
|
|
if cas.receivedp != nil {
|
|
|
|
*cas.receivedp = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if raceenabled {
|
2015-02-14 06:42:51 -07:00
|
|
|
if cas.kind == caseRecv && cas.elem != nil {
|
2014-09-02 15:13:29 -06:00
|
|
|
raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
|
2015-02-14 06:42:51 -07:00
|
|
|
} else if cas.kind == caseSend {
|
2014-09-02 15:13:29 -06:00
|
|
|
raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
|
|
|
|
}
|
|
|
|
}
|
2015-10-21 12:04:42 -06:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2014-09-02 15:13:29 -06:00
|
|
|
|
|
|
|
selunlock(sel)
|
|
|
|
goto retc
|
|
|
|
|
2015-11-07 22:28:21 -07:00
|
|
|
bufrecv:
|
2014-09-02 15:13:29 -06:00
|
|
|
// 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))
|
|
|
|
}
|
2015-10-21 12:04:42 -06:00
|
|
|
if msanenabled && cas.elem != nil {
|
|
|
|
msanwrite(cas.elem, c.elemtype.size)
|
|
|
|
}
|
2014-09-02 15:13:29 -06:00
|
|
|
if cas.receivedp != nil {
|
|
|
|
*cas.receivedp = true
|
|
|
|
}
|
2015-11-07 22:28:21 -07:00
|
|
|
qp = chanbuf(c, c.recvx)
|
2014-09-02 15:13:29 -06:00
|
|
|
if cas.elem != nil {
|
2015-11-07 22:28:21 -07:00
|
|
|
typedmemmove(c.elemtype, cas.elem, qp)
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
2015-11-07 22:28:21 -07:00
|
|
|
memclr(qp, uintptr(c.elemsize))
|
2014-09-02 15:13:29 -06:00
|
|
|
c.recvx++
|
|
|
|
if c.recvx == c.dataqsiz {
|
|
|
|
c.recvx = 0
|
|
|
|
}
|
|
|
|
c.qcount--
|
2015-11-07 22:28:21 -07:00
|
|
|
selunlock(sel)
|
2014-09-02 15:13:29 -06:00
|
|
|
goto retc
|
|
|
|
|
2015-11-07 22:28:21 -07:00
|
|
|
bufsend:
|
2014-09-02 15:13:29 -06:00
|
|
|
// can send to buffer
|
|
|
|
if raceenabled {
|
|
|
|
raceacquire(chanbuf(c, c.sendx))
|
|
|
|
racerelease(chanbuf(c, c.sendx))
|
|
|
|
raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
|
|
|
|
}
|
2015-10-21 12:04:42 -06:00
|
|
|
if msanenabled {
|
|
|
|
msanread(cas.elem, c.elemtype.size)
|
|
|
|
}
|
2014-12-29 08:07:47 -07:00
|
|
|
typedmemmove(c.elemtype, chanbuf(c, c.sendx), cas.elem)
|
2014-09-02 15:13:29 -06:00
|
|
|
c.sendx++
|
|
|
|
if c.sendx == c.dataqsiz {
|
|
|
|
c.sendx = 0
|
|
|
|
}
|
|
|
|
c.qcount++
|
2015-11-07 22:28:21 -07:00
|
|
|
selunlock(sel)
|
2014-09-02 15:13:29 -06:00
|
|
|
goto retc
|
|
|
|
|
2015-11-07 22:28:21 -07:00
|
|
|
recv:
|
2014-09-02 15:13:29 -06:00
|
|
|
// can receive from sleeping sender (sg)
|
2015-11-07 22:28:21 -07:00
|
|
|
recv(c, sg, cas.elem, func() { selunlock(sel) })
|
2014-09-02 15:13:29 -06:00
|
|
|
if debugSelect {
|
|
|
|
print("syncrecv: sel=", sel, " c=", c, "\n")
|
|
|
|
}
|
|
|
|
if cas.receivedp != nil {
|
|
|
|
*cas.receivedp = true
|
|
|
|
}
|
|
|
|
goto retc
|
|
|
|
|
|
|
|
rclose:
|
|
|
|
// read at end of closed channel
|
|
|
|
selunlock(sel)
|
|
|
|
if cas.receivedp != nil {
|
|
|
|
*cas.receivedp = false
|
|
|
|
}
|
|
|
|
if cas.elem != nil {
|
|
|
|
memclr(cas.elem, uintptr(c.elemsize))
|
|
|
|
}
|
|
|
|
if raceenabled {
|
|
|
|
raceacquire(unsafe.Pointer(c))
|
|
|
|
}
|
|
|
|
goto retc
|
|
|
|
|
2015-11-07 22:28:21 -07:00
|
|
|
send:
|
|
|
|
// can send to a sleeping receiver (sg)
|
2014-09-02 15:13:29 -06:00
|
|
|
if raceenabled {
|
|
|
|
raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
|
|
|
|
}
|
2015-10-21 12:04:42 -06:00
|
|
|
if msanenabled {
|
|
|
|
msanread(cas.elem, c.elemtype.size)
|
|
|
|
}
|
2015-11-07 22:28:21 -07:00
|
|
|
send(c, sg, cas.elem, func() { selunlock(sel) })
|
2014-09-02 15:13:29 -06:00
|
|
|
if debugSelect {
|
|
|
|
print("syncsend: sel=", sel, " c=", c, "\n")
|
|
|
|
}
|
2015-11-07 22:28:21 -07:00
|
|
|
goto retc
|
2014-09-02 15:13:29 -06:00
|
|
|
|
|
|
|
retc:
|
|
|
|
if cas.releasetime > 0 {
|
|
|
|
blockevent(cas.releasetime-t0, 2)
|
|
|
|
}
|
|
|
|
return cas.pc, cas.so
|
|
|
|
|
|
|
|
sclose:
|
|
|
|
// send on closed channel
|
|
|
|
selunlock(sel)
|
|
|
|
panic("send on closed channel")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *hchan) sortkey() uintptr {
|
|
|
|
// TODO(khr): if we have a moving garbage collector, we'll need to
|
|
|
|
// change this function.
|
|
|
|
return uintptr(unsafe.Pointer(c))
|
|
|
|
}
|
|
|
|
|
|
|
|
// A runtimeSelect is a single case passed to rselect.
|
|
|
|
// This must match ../reflect/value.go:/runtimeSelect
|
|
|
|
type runtimeSelect struct {
|
|
|
|
dir selectDir
|
|
|
|
typ unsafe.Pointer // channel type (not used here)
|
|
|
|
ch *hchan // channel
|
|
|
|
val unsafe.Pointer // ptr to data (SendDir) or ptr to receive buffer (RecvDir)
|
|
|
|
}
|
|
|
|
|
|
|
|
// These values must match ../reflect/value.go:/SelectDir.
|
|
|
|
type selectDir int
|
|
|
|
|
|
|
|
const (
|
|
|
|
_ selectDir = iota
|
|
|
|
selectSend // case Chan <- Send
|
|
|
|
selectRecv // case <-Chan:
|
|
|
|
selectDefault // default
|
|
|
|
)
|
|
|
|
|
2014-12-22 11:27:53 -07:00
|
|
|
//go:linkname reflect_rselect reflect.rselect
|
2014-09-02 15:13:29 -06:00
|
|
|
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)))
|
2015-02-14 06:42:51 -07:00
|
|
|
sel := (*hselect)(mallocgc(size, nil, flagNoScan))
|
2014-09-02 15:13:29 -06:00
|
|
|
newselect(sel, int64(size), int32(len(cases)))
|
|
|
|
r := new(bool)
|
|
|
|
for i := range cases {
|
|
|
|
rc := &cases[i]
|
|
|
|
switch rc.dir {
|
|
|
|
case selectDefault:
|
|
|
|
selectdefaultImpl(sel, uintptr(i), 0)
|
|
|
|
case selectSend:
|
|
|
|
if rc.ch == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
selectsendImpl(sel, rc.ch, uintptr(i), rc.val, 0)
|
|
|
|
case selectRecv:
|
|
|
|
if rc.ch == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
selectrecvImpl(sel, rc.ch, uintptr(i), rc.val, r, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pc, _ := selectgoImpl(sel)
|
|
|
|
chosen = int(pc)
|
|
|
|
recvOK = *r
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-12-08 11:11:08 -07:00
|
|
|
func (q *waitq) dequeueSudoG(sgp *sudog) {
|
|
|
|
x := sgp.prev
|
|
|
|
y := sgp.next
|
|
|
|
if x != nil {
|
|
|
|
if y != nil {
|
|
|
|
// middle of queue
|
|
|
|
x.next = y
|
|
|
|
y.prev = x
|
|
|
|
sgp.next = nil
|
|
|
|
sgp.prev = nil
|
2014-09-02 15:13:29 -06:00
|
|
|
return
|
|
|
|
}
|
2014-12-08 11:11:08 -07:00
|
|
|
// end of queue
|
|
|
|
x.next = nil
|
|
|
|
q.last = x
|
|
|
|
sgp.prev = nil
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if y != nil {
|
|
|
|
// start of queue
|
|
|
|
y.prev = nil
|
|
|
|
q.first = y
|
|
|
|
sgp.next = nil
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// x==y==nil. Either sgp is the only element in the queue,
|
|
|
|
// or it has already been removed. Use q.first to disambiguate.
|
|
|
|
if q.first == sgp {
|
|
|
|
q.first = nil
|
|
|
|
q.last = nil
|
2014-09-02 15:13:29 -06:00
|
|
|
}
|
|
|
|
}
|