package runtime
import (
)
const debugSelect = false
type scase struct {
c *hchan
elem unsafe.Pointer
}
var (
chansendpc = abi.FuncPCABIInternal(chansend)
chanrecvpc = abi.FuncPCABIInternal(chanrecv)
)
func ( *uintptr) {
* = sys.GetCallerPC()
}
func ( []scase, []uint16) {
var *hchan
for , := range {
:= [].c
if != {
=
lock(&.lock)
}
}
}
func ( []scase, []uint16) {
for := len() - 1; >= 0; -- {
:= [[]].c
if > 0 && == [[-1]].c {
continue
}
unlock(&.lock)
}
}
func ( *g, unsafe.Pointer) bool {
.activeStackChans = true
.parkingOnChan.Store(false)
var *hchan
for := .waiting; != nil; = .waitlink {
if .c.get() != && != nil {
unlock(&.lock)
}
= .c.get()
}
if != nil {
unlock(&.lock)
}
return true
}
func () {
gopark(nil, nil, waitReasonSelectNoCases, traceBlockForever, 1)
}
func ( *scase, *uint16, *uintptr, , int, bool) (int, bool) {
:= getg()
if debugSelect {
print("select: cas0=", , "\n")
}
:= (*[1 << 16]scase)(unsafe.Pointer())
:= (*[1 << 17]uint16)(unsafe.Pointer())
:= +
:= [::]
:= [::]
:= [:][::]
var []uintptr
if raceenabled && != nil {
:= (*[1 << 16]uintptr)(unsafe.Pointer())
= [::]
}
:= func( int) uintptr {
if == nil {
return 0
}
return []
}
var int64
if blockprofilerate > 0 {
= cputicks()
}
:= 0
:= true
for := range {
:= &[]
if .c == nil {
.elem = nil
continue
}
if .c.bubble != nil {
if getg().bubble != .c.bubble {
fatal("select on synctest channel from outside bubble")
}
} else {
= false
}
if .c.timer != nil {
.c.timer.maybeRunChan(.c)
}
:= cheaprandn(uint32( + 1))
[] = []
[] = uint16()
++
}
= [:]
= [:]
:= waitReasonSelect
if .bubble != nil && {
= waitReasonSynctestSelect
}
for := range {
:=
:= [[]].c
for > 0 && [[(-1)/2]].c.sortkey() < .sortkey() {
:= ( - 1) / 2
[] = []
=
}
[] = []
}
for := len() - 1; >= 0; -- {
:= []
:= [].c
[] = [0]
:= 0
for {
:= *2 + 1
if >= {
break
}
if +1 < && [[]].c.sortkey() < [[+1]].c.sortkey() {
++
}
if .sortkey() < [[]].c.sortkey() {
[] = []
=
continue
}
break
}
[] =
}
if debugSelect {
for := 0; +1 < len(); ++ {
if [[]].c.sortkey() > [[+1]].c.sortkey() {
print("i=", , " x=", [], " y=", [+1], "\n")
throw("select: broken sort")
}
}
}
sellock(, )
var (
*sudog
*hchan
*scase
*sudog
*sudog
unsafe.Pointer
**sudog
)
var int
var *scase
var bool
var int64 = -1
var bool
for , := range {
= int()
= &[]
= .c
if >= {
= .sendq.dequeue()
if != nil {
goto
}
if .qcount > 0 {
goto
}
if .closed != 0 {
goto
}
} else {
if raceenabled {
racereadpc(.raceaddr(), (), chansendpc)
}
if .closed != 0 {
goto
}
= .recvq.dequeue()
if != nil {
goto
}
if .qcount < .dataqsiz {
goto
}
}
}
if ! {
selunlock(, )
= -1
goto
}
if .waiting != nil {
throw("gp.waiting != nil")
}
= &.waiting
for , := range {
= int()
= &[]
= .c
:= acquireSudog()
.g =
.isSelect = true
.elem.set(.elem)
.releasetime = 0
if != 0 {
.releasetime = -1
}
.c.set()
* =
= &.waitlink
if < {
.sendq.enqueue()
} else {
.recvq.enqueue()
}
if .timer != nil {
blockTimerChan()
}
}
.param = nil
.parkingOnChan.Store(true)
gopark(selparkcommit, nil, , traceBlockSelect, 1)
.activeStackChans = false
sellock(, )
.selectDone.Store(0)
= (*sudog)(.param)
.param = nil
= -1
= nil
= false
= .waiting
for := .waiting; != nil; = .waitlink {
.isSelect = false
.elem.set(nil)
.c.set(nil)
}
.waiting = nil
for , := range {
= &[]
if .c.timer != nil {
unblockTimerChan(.c)
}
if == {
= int()
=
= .success
if .releasetime > 0 {
= .releasetime
}
} else {
= .c
if int() < {
.sendq.dequeueSudoG()
} else {
.recvq.dequeueSudoG()
}
}
= .waitlink
.waitlink = nil
releaseSudog()
=
}
if == nil {
throw("selectgo: bad wakeup")
}
= .c
if debugSelect {
print("wait-return: cas0=", , " c=", , " cas=", , " send=", < , "\n")
}
if < {
if ! {
goto
}
} else {
=
}
if raceenabled {
if < {
raceReadObjectPC(.elemtype, .elem, (), chansendpc)
} else if .elem != nil {
raceWriteObjectPC(.elemtype, .elem, (), chanrecvpc)
}
}
if msanenabled {
if < {
msanread(.elem, .elemtype.Size_)
} else if .elem != nil {
msanwrite(.elem, .elemtype.Size_)
}
}
if asanenabled {
if < {
asanread(.elem, .elemtype.Size_)
} else if .elem != nil {
asanwrite(.elem, .elemtype.Size_)
}
}
selunlock(, )
goto
:
if raceenabled {
if .elem != nil {
raceWriteObjectPC(.elemtype, .elem, (), chanrecvpc)
}
racenotify(, .recvx, nil)
}
if msanenabled && .elem != nil {
msanwrite(.elem, .elemtype.Size_)
}
if asanenabled && .elem != nil {
asanwrite(.elem, .elemtype.Size_)
}
= true
= chanbuf(, .recvx)
if .elem != nil {
typedmemmove(.elemtype, .elem, )
}
typedmemclr(.elemtype, )
.recvx++
if .recvx == .dataqsiz {
.recvx = 0
}
.qcount--
selunlock(, )
goto
:
if raceenabled {
racenotify(, .sendx, nil)
raceReadObjectPC(.elemtype, .elem, (), chansendpc)
}
if msanenabled {
msanread(.elem, .elemtype.Size_)
}
if asanenabled {
asanread(.elem, .elemtype.Size_)
}
typedmemmove(.elemtype, chanbuf(, .sendx), .elem)
.sendx++
if .sendx == .dataqsiz {
.sendx = 0
}
.qcount++
selunlock(, )
goto
:
recv(, , .elem, func() { selunlock(, ) }, 2)
if debugSelect {
print("syncrecv: cas0=", , " c=", , "\n")
}
= true
goto
:
selunlock(, )
= false
if .elem != nil {
typedmemclr(.elemtype, .elem)
}
if raceenabled {
raceacquire(.raceaddr())
}
goto
:
if raceenabled {
raceReadObjectPC(.elemtype, .elem, (), chansendpc)
}
if msanenabled {
msanread(.elem, .elemtype.Size_)
}
if asanenabled {
asanread(.elem, .elemtype.Size_)
}
send(, , .elem, func() { selunlock(, ) }, 2)
if debugSelect {
print("syncsend: cas0=", , " c=", , "\n")
}
goto
:
if > 0 {
blockevent(-, 1)
}
return ,
:
selunlock(, )
panic(plainError("send on closed channel"))
}
func ( *hchan) () uintptr {
return uintptr(unsafe.Pointer())
}
type runtimeSelect struct {
dir selectDir
typ unsafe.Pointer
ch *hchan
val unsafe.Pointer
}
type selectDir int
const (
_ selectDir = iota
selectSend
selectRecv
selectDefault
)
func ( []runtimeSelect) (int, bool) {
if len() == 0 {
block()
}
:= make([]scase, len())
:= make([]int, len())
, := 0, 0
:= -1
for , := range {
var int
switch .dir {
case selectDefault:
=
continue
case selectSend:
=
++
case selectRecv:
++
= len() -
}
[] = scase{c: .ch, elem: .val}
[] =
}
if + == 0 {
return , false
}
if + < len() {
copy([:], [len()-:])
copy([:], [len()-:])
}
:= make([]uint16, 2*(+))
var *uintptr
if raceenabled {
:= make([]uintptr, +)
for := range {
selectsetpc(&[])
}
= &[0]
}
, := selectgo(&[0], &[0], , , , == -1)
if < 0 {
=
} else {
= []
}
return ,
}
func ( *waitq) ( *sudog) {
:= .prev
:= .next
if != nil {
if != nil {
.next =
.prev =
.next = nil
.prev = nil
return
}
.next = nil
.last =
.prev = nil
return
}
if != nil {
.prev = nil
.first =
.next = nil
return
}
if .first == {
.first = nil
.last = nil
}
}