mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
reflect: add Select
R=r, iant, rogpeppe, bradfitz CC=golang-dev https://golang.org/cl/6498078
This commit is contained in:
parent
e8de8b58ca
commit
370ae05545
4 changed files with 654 additions and 8 deletions
|
|
@ -1618,6 +1618,140 @@ func Copy(dst, src Value) int {
|
|||
return n
|
||||
}
|
||||
|
||||
// A runtimeSelect is a single case passed to rselect.
|
||||
// This must match ../runtime/chan.c:/runtimeSelect
|
||||
type runtimeSelect struct {
|
||||
dir uintptr // 0, SendDir, or RecvDir
|
||||
typ *runtimeType // channel type
|
||||
ch iword // interface word for channel
|
||||
val iword // interface word for value (for SendDir)
|
||||
}
|
||||
|
||||
// rselect runs a select. It returns the index of the chosen case,
|
||||
// and if the case was a receive, the interface word of the received
|
||||
// value and the conventional OK bool to indicate whether the receive
|
||||
// corresponds to a sent value.
|
||||
func rselect([]runtimeSelect) (chosen int, recv iword, recvOK bool)
|
||||
|
||||
// A SelectDir describes the communication direction of a select case.
|
||||
type SelectDir int
|
||||
|
||||
// NOTE: These values must match ../runtime/chan.c:/SelectDir.
|
||||
|
||||
const (
|
||||
_ SelectDir = iota
|
||||
SelectSend // case Chan <- Send
|
||||
SelectRecv // case <-Chan:
|
||||
SelectDefault // default
|
||||
)
|
||||
|
||||
// A SelectCase describes a single case in a select operation.
|
||||
// The kind of case depends on Dir, the communication direction.
|
||||
//
|
||||
// If Dir is SelectDefault, the case represents a default case.
|
||||
// Chan and Send must be zero Values.
|
||||
//
|
||||
// If Dir is SelectSend, the case represents a send operation.
|
||||
// Normally Chan's underlying value must be a channel, and Send's underlying value must be
|
||||
// assignable to the channel's element type. As a special case, if Chan is a zero Value,
|
||||
// then the case is ignored, and the field Send will also be ignored and may be either zero
|
||||
// or non-zero.
|
||||
//
|
||||
// If Dir is SelectRecv, the case represents a receive operation.
|
||||
// Normally Chan's underlying value must be a channel and Send must be a zero Value.
|
||||
// If Chan is a zero Value, then the case is ignored, but Send must still be a zero Value.
|
||||
// When a receive operation is selected, the received Value is returned by Select.
|
||||
//
|
||||
type SelectCase struct {
|
||||
Dir SelectDir // direction of case
|
||||
Chan Value // channel to use (for send or receive)
|
||||
Send Value // value to send (for send)
|
||||
}
|
||||
|
||||
// Select executes a select operation described by the list of cases.
|
||||
// Like the Go select statement, it blocks until one of the cases can
|
||||
// proceed and then executes that case. It returns the index of the chosen case
|
||||
// and, if that case was a receive operation, the value received and a
|
||||
// boolean indicating whether the value corresponds to a send on the channel
|
||||
// (as opposed to a zero value received because the channel is closed).
|
||||
func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
|
||||
// NOTE: Do not trust that caller is not modifying cases data underfoot.
|
||||
// The range is safe because the caller cannot modify our copy of the len
|
||||
// and each iteration makes its own copy of the value c.
|
||||
runcases := make([]runtimeSelect, len(cases))
|
||||
haveDefault := false
|
||||
for i, c := range cases {
|
||||
rc := &runcases[i]
|
||||
rc.dir = uintptr(c.Dir)
|
||||
switch c.Dir {
|
||||
default:
|
||||
panic("reflect.Select: invalid Dir")
|
||||
|
||||
case SelectDefault: // default
|
||||
if haveDefault {
|
||||
panic("reflect.Select: multiple default cases")
|
||||
}
|
||||
haveDefault = true
|
||||
if c.Chan.IsValid() {
|
||||
panic("reflect.Select: default case has Chan value")
|
||||
}
|
||||
if c.Send.IsValid() {
|
||||
panic("reflect.Select: default case has Send value")
|
||||
}
|
||||
|
||||
case SelectSend:
|
||||
ch := c.Chan
|
||||
if !ch.IsValid() {
|
||||
break
|
||||
}
|
||||
ch.mustBe(Chan)
|
||||
ch.mustBeExported()
|
||||
tt := (*chanType)(unsafe.Pointer(ch.typ))
|
||||
if ChanDir(tt.dir)&SendDir == 0 {
|
||||
panic("reflect.Select: SendDir case using recv-only channel")
|
||||
}
|
||||
rc.ch = ch.iword()
|
||||
rc.typ = tt.runtimeType()
|
||||
v := c.Send
|
||||
if !v.IsValid() {
|
||||
panic("reflect.Select: SendDir case missing Send value")
|
||||
}
|
||||
v.mustBeExported()
|
||||
v = v.assignTo("reflect.Select", toCommonType(tt.elem), nil)
|
||||
rc.val = v.iword()
|
||||
|
||||
case SelectRecv:
|
||||
if c.Send.IsValid() {
|
||||
panic("reflect.Select: RecvDir case has Send value")
|
||||
}
|
||||
ch := c.Chan
|
||||
if !ch.IsValid() {
|
||||
break
|
||||
}
|
||||
ch.mustBe(Chan)
|
||||
ch.mustBeExported()
|
||||
tt := (*chanType)(unsafe.Pointer(ch.typ))
|
||||
rc.typ = tt.runtimeType()
|
||||
if ChanDir(tt.dir)&RecvDir == 0 {
|
||||
panic("reflect.Select: RecvDir case using send-only channel")
|
||||
}
|
||||
rc.ch = ch.iword()
|
||||
}
|
||||
}
|
||||
|
||||
chosen, word, recvOK := rselect(runcases)
|
||||
if runcases[chosen].dir == uintptr(SelectRecv) {
|
||||
tt := (*chanType)(unsafe.Pointer(toCommonType(runcases[chosen].typ)))
|
||||
typ := toCommonType(tt.elem)
|
||||
fl := flag(typ.Kind()) << flagKindShift
|
||||
if typ.size > ptrSize {
|
||||
fl |= flagIndir
|
||||
}
|
||||
recv = Value{typ, unsafe.Pointer(word), fl}
|
||||
}
|
||||
return chosen, recv, recvOK
|
||||
}
|
||||
|
||||
/*
|
||||
* constructors
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue