Index: src/pkg/reflect/value.go |
=================================================================== |
--- a/src/pkg/reflect/value.go |
+++ b/src/pkg/reflect/value.go |
@@ -1618,6 +1618,140 @@ |
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 |
*/ |