Commit 370ae055 authored by Russ Cox's avatar Russ Cox

reflect: add Select

R=r, iant, rogpeppe, bradfitz
CC=golang-dev
https://golang.org/cl/6498078
parent e8de8b58
This diff is collapsed.
......@@ -186,6 +186,12 @@ type Type interface {
uncommon() *uncommonType
}
/*
* These data structures are known to the compiler (../../cmd/gc/reflect.c).
* A few are known to ../runtime/type.go to convey to debuggers.
* They are also known to ../runtime/type.h.
*/
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint
......@@ -220,11 +226,6 @@ const (
UnsafePointer
)
/*
* These data structures are known to the compiler (../../cmd/gc/reflect.c).
* A few are known to ../runtime/type.go to convey to debuggers.
*/
// The compiler can only construct empty interface values at
// compile time; non-empty interface values get created
// during initialization. Type is an empty interface
......
......@@ -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
*/
......
......@@ -999,11 +999,17 @@ syncsend:
runtime·ready(gp);
retc:
// return to pc corresponding to chosen case
// return pc corresponding to chosen case.
// Set boolean passed during select creation
// (at offset selp + cas->so) to true.
// If cas->so == 0, this is a reflect-driven select and we
// don't need to update the boolean.
pc = cas->pc;
as = (byte*)selp + cas->so;
if(cas->so > 0) {
as = (byte*)selp + cas->so;
*as = true;
}
runtime·free(sel);
*as = true;
return pc;
sclose:
......@@ -1013,6 +1019,87 @@ sclose:
return nil; // not reached
}
// This struct must match ../reflect/value.go:/runtimeSelect.
typedef struct runtimeSelect runtimeSelect;
struct runtimeSelect
{
uintptr dir;
ChanType *typ;
Hchan *ch;
uintptr val;
};
// This enum must match ../reflect/value.go:/SelectDir.
enum SelectDir {
SelectSend = 1,
SelectRecv,
SelectDefault,
};
// func rselect(cases []runtimeSelect) (chosen int, word uintptr, recvOK bool)
void
reflect·rselect(Slice cases, int32 chosen, uintptr word, bool recvOK)
{
int32 i;
Select *sel;
runtimeSelect* rcase, *rc;
void *elem;
void *recvptr;
uintptr maxsize;
chosen = -1;
word = 0;
recvOK = false;
maxsize = 0;
rcase = (runtimeSelect*)cases.array;
for(i=0; i<cases.len; i++) {
rc = &rcase[i];
if(rc->dir == SelectRecv && rc->ch != nil && maxsize < rc->typ->elem->size)
maxsize = rc->typ->elem->size;
}
recvptr = nil;
if(maxsize > sizeof(void*))
recvptr = runtime·mal(maxsize);
newselect(cases.len, &sel);
for(i=0; i<cases.len; i++) {
rc = &rcase[i];
switch(rc->dir) {
case SelectDefault:
selectdefault(sel, (void*)i, 0);
break;
case SelectSend:
if(rc->ch == nil)
break;
if(rc->typ->elem->size > sizeof(void*))
elem = (void*)rc->val;
else
elem = (void*)&rc->val;
selectsend(sel, rc->ch, (void*)i, elem, 0);
break;
case SelectRecv:
if(rc->ch == nil)
break;
if(rc->typ->elem->size > sizeof(void*))
elem = recvptr;
else
elem = &word;
selectrecv(sel, rc->ch, (void*)i, elem, &recvOK, 0);
break;
}
}
chosen = (int32)(uintptr)selectgo(&sel);
if(rcase[chosen].dir == SelectRecv && rcase[chosen].typ->elem->size > sizeof(void*))
word = (uintptr)recvptr;
FLUSH(&chosen);
FLUSH(&word);
FLUSH(&recvOK);
}
// closechan(sel *byte);
void
runtime·closechan(Hchan *c)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment