func main() { var i int var recvDir reflect.ChanDir = reflect.RecvDir var chanOf reflect.Type = reflect.ChanOf(recvDir, reflect.TypeOf(i)) fmt.Println(chanOf.Kind()) // chan fmt.Println(chanOf.ChanDir()) // <-chan fmt.Println(chanOf.String()) // <-chan int var i1 int var recvDir1 reflect.ChanDir = reflect.SendDir var chanOf1 reflect.Type = reflect.ChanOf(recvDir1, reflect.TypeOf(i1)) fmt.Println(chanOf1.Kind(), chanOf1.ChanDir(), chanOf1.String()) // chan chan<- chan<- int var i2 int var recvDir2 reflect.ChanDir = reflect.BothDir var chanOf2 reflect.Type = reflect.ChanOf(recvDir2, reflect.TypeOf(i2)) fmt.Println(chanOf2.Kind(), chanOf2.ChanDir(), chanOf2.String()) // chan chan chan int var i3 string var recvDir3 reflect.ChanDir = reflect.BothDir var chanOf3 reflect.Type = reflect.ChanOf(recvDir3, reflect.TypeOf(i3)) fmt.Println(chanOf3.Kind(), chanOf3.ChanDir(), chanOf3.String()) // chan chan chan string }
func Test_InjectorInvoke(t *testing.T) { injector := inject.New() expect(t, injector == nil, false) dep := "some dependency" injector.Map(dep) dep2 := "another dep" injector.MapTo(dep2, (*SpecialString)(nil)) dep3 := make(chan *SpecialString) dep4 := make(chan *SpecialString) typRecv := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(dep3).Elem()) typSend := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(dep4).Elem()) injector.Set(typRecv, reflect.ValueOf(dep3)) injector.Set(typSend, reflect.ValueOf(dep4)) _, err := injector.Invoke(func(d1 string, d2 SpecialString, d3 <-chan *SpecialString, d4 chan<- *SpecialString) { expect(t, d1, dep) expect(t, d2, dep2) expect(t, reflect.TypeOf(d3).Elem(), reflect.TypeOf(dep3).Elem()) expect(t, reflect.TypeOf(d4).Elem(), reflect.TypeOf(dep4).Elem()) expect(t, reflect.TypeOf(d3).ChanDir(), reflect.RecvDir) expect(t, reflect.TypeOf(d4).ChanDir(), reflect.SendDir) }) expect(t, err, nil) }
// Helper method to map default channels in the context func (c *Connection) mapDefaultChannels(context martini.Context) { // Map the Error Channel to a <-chan error for the next Handler(s) context.Set(reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(c.Error).Elem()), reflect.ValueOf(c.Error)) // Map the Disconnect Channel to a chan<- bool for the next Handler(s) context.Set(reflect.ChanOf(reflect.SendDir, reflect.TypeOf(c.Disconnect).Elem()), reflect.ValueOf(c.Disconnect)) // Map the Done Channel to a <-chan bool for the next Handler(s) context.Set(reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(c.Done).Elem()), reflect.ValueOf(c.Done)) }
func NewChan(typ interface{}) (interface{}, interface{}) { rc := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, reflect.TypeOf(typ)), 0) wc := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, reflect.TypeOf(typ)), 0) go loop(rc, wc) vrc := rc.Convert(reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(typ))) vwc := wc.Convert(reflect.ChanOf(reflect.SendDir, reflect.TypeOf(typ))) return vrc.Interface(), vwc.Interface() }
func Test_Injector_Invoke(t *testing.T) { Convey("Invokes function", t, func() { injector := inject.New() So(injector, ShouldNotBeNil) dep := "some dependency" injector.Map(dep) dep2 := "another dep" injector.MapTo(dep2, (*SpecialString)(nil)) dep3 := make(chan *SpecialString) dep4 := make(chan *SpecialString) typRecv := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(dep3).Elem()) typSend := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(dep4).Elem()) injector.Set(typRecv, reflect.ValueOf(dep3)) injector.Set(typSend, reflect.ValueOf(dep4)) _, err := injector.Invoke(func(d1 string, d2 SpecialString, d3 <-chan *SpecialString, d4 chan<- *SpecialString) { So(d1, ShouldEqual, dep) So(d2, ShouldEqual, dep2) So(reflect.TypeOf(d3).Elem(), ShouldEqual, reflect.TypeOf(dep3).Elem()) So(reflect.TypeOf(d4).Elem(), ShouldEqual, reflect.TypeOf(dep4).Elem()) So(reflect.TypeOf(d3).ChanDir(), ShouldEqual, reflect.RecvDir) So(reflect.TypeOf(d4).ChanDir(), ShouldEqual, reflect.SendDir) }) So(err, ShouldBeNil) _, err = injector.Invoke(myFastInvoker(func(string) {})) So(err, ShouldBeNil) }) Convey("Invokes function with return value", t, func() { injector := inject.New() So(injector, ShouldNotBeNil) dep := "some dependency" injector.Map(dep) dep2 := "another dep" injector.MapTo(dep2, (*SpecialString)(nil)) result, err := injector.Invoke(func(d1 string, d2 SpecialString) string { So(d1, ShouldEqual, dep) So(d2, ShouldEqual, dep2) return "Hello world" }) So(result[0].String(), ShouldEqual, "Hello world") So(err, ShouldBeNil) }) }
func TestGetChannel(t *testing.T) { b := &Broadcaster{RoutineMaxClients: 10} var uids []string var tids []int for i := 0; i < 10; i++ { tuid, ttid := b.AddClient() uids = append(uids, tuid) tids = append(tids, ttid) } ch, err := b.GetChannel(uids[1], tids[1]) if err != nil { t.Errorf("Test encountered the following error: %v", err) } if reflect.TypeOf(ch) != reflect.ChanOf(reflect.BothDir, reflect.TypeOf("")) { t.Errorf("Test expected to retrieve channel, but received %v", reflect.TypeOf(ch)) } ch, err = b.GetChannel("asd", 3) if err == nil { t.Error("An error was expected, but not received") } }
func chanOfUnknown() { // Unknown channel direction: assume all three. // MakeChan only works on the bi-di channel type. t := reflect.ChanOf(unknownDir, reflect.TypeOf(&a)) print(reflect.Zero(t).Interface()) // @types <-chan *int | chan<- *int | chan *int print(reflect.MakeChan(t, 0).Interface()) // @types chan *int }
func chanOfSend() { // MakeChan(chan<-) is a no-op. t := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(&a)) print(reflect.Zero(t).Interface()) // @types <-chan *int | chan<- *int | chan *int print(reflect.MakeChan(t, 0).Interface().(chan<- *int)) // @pointsto print(reflect.MakeChan(t, 0).Interface().(chan *int)) // @pointsto <alloc in reflect.MakeChan> }
func chanOfRecv() { // MakeChan(<-chan) is a no-op. t := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(&a)) print(reflect.Zero(t).Interface()) // @types <-chan *int print(reflect.MakeChan(t, 0).Interface().(<-chan *int)) // @pointsto print(reflect.MakeChan(t, 0).Interface().(chan *int)) // @pointsto }
// executeCmd runs Store.ExecuteCmd in a goroutine. A channel with // element type equal to the reply type is created and returned // immediately. The reply is sent to the channel once the cmd has been // executed by the store. The store is looked up from the store map // if specified by header.Replica; otherwise, the command is being // executed locally, and the replica is determined via lookup of // header.Key in the ranges slice. func (db *LocalDB) executeCmd(method string, header *storage.RequestHeader, args, reply interface{}) interface{} { chanVal := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, reflect.TypeOf(reply)), 1) replyVal := reflect.ValueOf(reply) go func() { // If the replica isn't specified in the header, look it up. var err error var store *storage.Store // If we aren't given a Replica, then a little bending over backwards here. We need to find the Store, but all // we have is the Key. So find its Range locally, and pull out its Replica which we use to find the Store. // This lets us use the same codepath below (store.ExecuteCmd) for both locally and remotely originated // commands. if header.Replica.NodeID == 0 { if repl := db.lookupReplica(header.Key); repl != nil { header.Replica = *repl } else { err = util.Errorf("unable to lookup range replica for key %q", string(header.Key)) } } if err == nil { store, err = db.GetStore(&header.Replica) } if err != nil { reflect.Indirect(replyVal).FieldByName("Error").Set(reflect.ValueOf(err)) } else { store.ExecuteCmd(method, header, args, reply) } chanVal.Send(replyVal) }() return chanVal.Interface() }
// tysubst attempts to substitute all type variables within a single return // type with their corresponding Go type from the type environment. // // tysubst will panic if a type variable is unbound, or if it encounters a // type that cannot be dynamically created. Such types include arrays, // functions and structs. (A limitation of the `reflect` package.) func (rt returnType) tysubst(typ reflect.Type) reflect.Type { if tyname := tyvarName(typ); len(tyname) > 0 { if thetype, ok := rt.tyenv[tyname]; !ok { rt.panic("Unbound type variable %s.", tyname) } else { return thetype } } switch typ.Kind() { case reflect.Array: rt.panic("Cannot dynamically create Array types.") case reflect.Chan: return reflect.ChanOf(typ.ChanDir(), rt.tysubst(typ.Elem())) case reflect.Func: rt.panic("Cannot dynamically create Function types.") case reflect.Interface: rt.panic("TODO") case reflect.Map: return reflect.MapOf(rt.tysubst(typ.Key()), rt.tysubst(typ.Elem())) case reflect.Ptr: return reflect.PtrTo(rt.tysubst(typ.Elem())) case reflect.Slice: return reflect.SliceOf(rt.tysubst(typ.Elem())) case reflect.Struct: rt.panic("Cannot dynamically create Struct types.") case reflect.UnsafePointer: rt.panic("Cannot dynamically create unsafe.Pointer types.") } // We've covered all the composite types, so we're only left with // base types. return typ }
func main() { t := reflect.TypeOf(3) chtype := reflect.ChanOf(reflect.RecvDir, t) inst := *(reflect.New(chtype).Interface().(*<-chan int)) fmt.Println(inst) }
// executeCmd runs Store.ExecuteCmd in a goroutine. A channel with // element type equal to the reply type is created and returned // immediately. The reply is sent to the channel once the cmd has been // executed by the store. The store is looked up from the store map // if specified by header.Replica; otherwise, the command is being // executed locally, and the replica is determined via lookup of // header.Key in the ranges slice. func (db *LocalDB) executeCmd(method string, header *storage.RequestHeader, args, reply interface{}) interface{} { chanVal := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, reflect.TypeOf(reply)), 1) replyVal := reflect.ValueOf(reply) go func() { // If the replica isn't specified in the header, look it up. var err error var store *storage.Store if header.Replica.NodeID == 0 { if repl := db.lookupReplica(header.Key); repl != nil { header.Replica = *repl } else { err = util.Errorf("unable to lookup range replica for key %q", string(header.Key)) } } if err == nil { store, err = db.GetStore(&header.Replica) } if err != nil { reflect.Indirect(replyVal).FieldByName("Error").Set(reflect.ValueOf(err)) } else { store.ExecuteCmd(method, header, args, reply) } chanVal.Send(replyVal) }() return chanVal.Interface() }
// FlattenChan is of type: func(input chan []T) chan T. // Takes a chan of arrays, and concatenates them together, putting each element // onto the output chan. After input is closed, output is also closed. If input // is chan T instead of type chan []T, then this is a no-op. func FlattenChan(input interface{}) interface{} { inputValue := reflect.ValueOf(input) if inputValue.Kind() != reflect.Chan { panic(fmt.Sprintf("FlattenChan called on invalid type: %s", inputValue.Type())) } elemType := inputValue.Type().Elem() if elemType.Kind() != reflect.Array && elemType.Kind() != reflect.Slice { return input } outputType := reflect.ChanOf(reflect.BothDir, elemType.Elem()) output := reflect.MakeChan(outputType, 0) go func() { for { value, ok := inputValue.Recv() if !ok { break } for i := 0; i < value.Len(); i++ { output.Send(value.Index(i)) } } output.Close() }() return output.Interface() }
// makeWorkerChans makes a buffered channel of the specified type func makeWorkerChans(t reflect.Type) (chan []reflect.Value, reflect.Value) { // display(reflect.TypeOf([]reflect.Value{})) // job := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, reflect.TypeOf(&channeller{})), 100) // job := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, reflect.TypeOf([]reflect.Value{})), 100) job := make(chan []reflect.Value) res := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, t), 100) return job, res }
func main() { c := reflect.ChanOf(reflect.SendDir|reflect.RecvDir, Int) fmt.Println(c) t := reflect.TypeOf(make(chan int)).Elem() fmt.Println(t) }
func chanOf(elem interface{}) interface{} { if t, ok := elem.(qlang.GoTyper); ok { tchan := reflect.ChanOf(reflect.BothDir, t.GoType()) return qlang.NewType(tchan) } panic(fmt.Sprintf("invalid chan T: `%v` isn't a qlang type", elem)) }
func Test_InjectorSet(t *testing.T) { injector := inject.New() typ := reflect.TypeOf("string") typSend := reflect.ChanOf(reflect.SendDir, typ) typRecv := reflect.ChanOf(reflect.RecvDir, typ) // instantiating unidirectional channels is not possible using reflect // http://golang.org/src/pkg/reflect/value.go?s=60463:60504#L2064 chanRecv := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0) chanSend := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0) injector.Set(typSend, chanSend) injector.Set(typRecv, chanRecv) expect(t, injector.Get(typSend).IsValid(), true) expect(t, injector.Get(typRecv).IsValid(), true) expect(t, injector.Get(chanSend.Type()).IsValid(), false) }
// Make makes a channel from the parameters in ChannelDescriptor struct. func (c *ChannelDescriptor) Make(both bool) reflect.Value { dir := c.Dir if both { dir = reflect.BothDir } elem := types.MakeNew(c.TypeName, c.Size) typ := reflect.ChanOf(dir, elem.Type()) return reflect.MakeChan(typ, c.Size) }
func Mkchan(typ interface{}, buffer ...int) *qlang.Chan { n := 0 if len(buffer) > 0 { n = buffer[0] } t := reflect.ChanOf(reflect.BothDir, types.Reflect(typ)) return &qlang.Chan{Data: reflect.MakeChan(t, n)} }
// JSON returns a websocket handling middleware. It can only be used // in handlers for HTTP GET. // IMPORTANT: The last handler in your handler chain must block in order for the // connection to be kept alive. // It accepts an empty struct it will copy and try to populate // with data received from the client using the JSON Marshaler, as well // as it will serialize your structs to JSON and send them to the client. // For the following, it is assumed you passed a struct named Message // to the handler. // It maps four channels for you to use in the follow-up Handler(s): // - A receiving string channel (<-chan *Message) on which you will // receive all incoming structs from the client // - A sending string channel (chan<- *Message) on which you will be // able to send structs to the client. // - A receiving error channel (<-chan error) on which you will receive // errors occurring while sending & receiving // - A receiving disconnect channel (<-chan bool) on which you will receive // a message only if the connection is about to be closed following an // error or a client disconnect. // - A sending done channel (chan<- bool) on which you can send as soon as you wish // to disconnect the connection. // The middleware handles the following for you: // - Checking the request for cross origin access // - Doing the websocket handshake // - Setting sensible options for the Gorilla websocket connection // - Starting and terminating the necessary goroutines // An optional sockets.Options object can be passed to Messages to overwrite // default options mentioned in the documentation of the Options object. func JSON(bindStruct interface{}, options ...*Options) martini.Handler { o := newOptions(options) return func(context martini.Context, resp http.ResponseWriter, req *http.Request) { // Check the request for cross origin access or HTTP methods other than GET status, err := checkRequest(req, o) if err != nil { resp.WriteHeader(status) resp.Write([]byte(err.Error())) return } // Do handshake with the client and upgrade the connection to a websocket ws, err := doHandshake(resp, req, o) if err != nil { resp.WriteHeader(http.StatusBadRequest) resp.Write([]byte(err.Error())) return } // Set up the JSON connection c := newJSONConnection(bindStruct, ws, o) // Set the options for the gorilla websocket package c.setSocketOptions() // Map the Sender to a chan<- *Message for the next Handler(s) context.Set(reflect.ChanOf(reflect.SendDir, c.typ), c.Sender) // Map the Receiver to a <-chan *Message for the next Handler(s) context.Set(reflect.ChanOf(reflect.RecvDir, c.typ), c.Receiver) // Map the Channels <-chan error, <-chan bool and chan<- bool c.mapDefaultChannels(context) // start the send and receive goroutines go c.send() go c.recv() go waitForDisconnect(c) // call the next handler, which must block context.Next() } }
func chanOfBoth() { t := reflect.ChanOf(reflect.BothDir, reflect.TypeOf(&a)) print(reflect.Zero(t).Interface()) // @types <-chan *int | chan<- *int | chan *int ch := reflect.MakeChan(t, 0) print(ch.Interface().(chan *int)) // @pointsto <alloc in reflect.MakeChan> ch.Send(reflect.ValueOf(&b)) ch.Interface().(chan *int) <- &a r, _ := ch.Recv() print(r.Interface().(*int)) // @pointsto main.a | main.b print(<-ch.Interface().(chan *int)) // @pointsto main.a | main.b }
// invokeMethod sends the specified RPC asynchronously and returns a // channel which receives the reply struct when the call is // complete. Returns a channel of the same type as "reply". func (db *LocalDB) invokeMethod(method string, args, reply interface{}) interface{} { chanVal := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, reflect.TypeOf(reply)), 1) replyVal := reflect.ValueOf(reply) reflect.ValueOf(db.rng).MethodByName(method).Call([]reflect.Value{ reflect.ValueOf(args), replyVal, }) chanVal.Send(replyVal) return chanVal.Interface() }
func sendSlice(slice interface{}) (channel interface{}) { sliceValue := reflect.ValueOf(slice) chanType := reflect.ChanOf(reflect.BothDir, sliceValue.Type().Elem()) chanValue := reflect.MakeChan(chanType, 0) go func() { for i := 0; i < sliceValue.Len(); i++ { chanValue.Send(sliceValue.Index(i)) } chanValue.Close() }() return chanValue.Interface() }
func (d *Dataset) SetupShard(n int) { ctype := reflect.ChanOf(reflect.BothDir, d.Type) for i := 0; i < n; i++ { ds := &DatasetShard{ Id: i, Parent: d, WriteChan: reflect.MakeChan(ctype, 0), } // println("created shard", ds.Name()) d.Shards = append(d.Shards, ds) } }
func Test_Injector_Set(t *testing.T) { Convey("Set and get type", t, func() { injector := inject.New() So(injector, ShouldNotBeNil) typ := reflect.TypeOf("string") typSend := reflect.ChanOf(reflect.SendDir, typ) typRecv := reflect.ChanOf(reflect.RecvDir, typ) // instantiating unidirectional channels is not possible using reflect // http://golang.org/src/pkg/reflect/value.go?s=60463:60504#L2064 chanRecv := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0) chanSend := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0) injector.Set(typSend, chanSend) injector.Set(typRecv, chanRecv) So(injector.GetVal(typSend).IsValid(), ShouldBeTrue) So(injector.GetVal(typRecv).IsValid(), ShouldBeTrue) So(injector.GetVal(chanSend.Type()).IsValid(), ShouldBeFalse) }) }
func pMapChan(dataChan interface{}, f interface{}) (resultChan interface{}) { fType := reflect.TypeOf(f) fRetType := fType.Out(0) dataChanType := reflect.TypeOf(dataChan) dataChanElemType := dataChanType.Elem() inChans := make([]reflect.Value, cpus) outChans := make([]reflect.Value, cpus) for i := 0; i < cpus; i++ { inChans[i] = reflect.MakeChan(reflect.ChanOf(reflect.BothDir, dataChanElemType), cpus) outChans[i] = reflect.MakeChan(reflect.ChanOf(reflect.BothDir, fRetType), cpus) go pMapChanInt(inChans[i], f, outChans[i]) } go pMapFeedInChans(dataChan, inChans) resultChanValue := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, fRetType), 0) resultChan = resultChanValue.Interface() go pMapDrainOutChans(outChans, resultChanValue) return }
// Invoke calls the method's handler with the given input. Each encoded output // from the method is passed to out, and the final result is returned. Invoke // will panic if it is unable to encode an output from the handler. // // Returns ErrNoSuchMethod if m == nil. func (m *Method) Invoke(ctx Context, in []byte, out func([]byte)) error { if m == nil { return ErrNoSuchMethod } inValue, err := decode(in, m.input) if err != nil { return &protocol.Error{ Code: protocol.ErrorInvalidParams, Message: fmt.Sprintf("unable to decode params: %v", err), } } if m.Stream { outChan := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, m.output), 0) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() for { v, ok := outChan.Recv() if !ok { break } bits, err := json.Marshal(v.Interface()) if err != nil { panic(err) // Not expected to occur } out(bits) } }() err = m.callStream(ctx, inValue, outChan) outChan.Close() wg.Wait() return err } // Non-streaming case v, err := m.call(ctx, inValue) if err != nil { return err } bits, err := json.Marshal(v) if err != nil { panic(err) // Not expected to occur } out(bits) return nil }
// Slice accepts a slice and send values to tasks via Channel() func (fc *FlowContext) Slice(slice interface{}) (ret *Dataset) { sliceValue, sliceType := reflect.ValueOf(slice), reflect.TypeOf(slice) sliceLen := sliceValue.Len() chType := reflect.ChanOf(reflect.BothDir, sliceType.Elem()) chValue := reflect.MakeChan(chType, 16) go func() { for i := 0; i < sliceLen; i++ { chValue.Send(sliceValue.Index(i)) } chValue.Close() }() return fc.doChannel(chValue, chType) }
// routeRPC verifies permissions and looks up the appropriate range // based on the supplied key and sends the RPC according to the // specified options. routeRPC sends asynchronously and returns a // channel which receives the reply struct when the call is // complete. Returns a channel of the same type as "reply". func (db *DistDB) routeRPC(method string, header *storage.RequestHeader, args, reply interface{}) interface{} { chanVal := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, reflect.TypeOf(reply)), 1) // Verify permissions. if err := db.verifyPermissions(method, header); err != nil { replyVal := reflect.ValueOf(reply) reflect.Indirect(replyVal).FieldByName("Error").Set(reflect.ValueOf(err)) chanVal.Send(replyVal) return chanVal.Interface() } // Retry logic for lookup of range by key and RPCs to range replicas. go func() { retryOpts := util.RetryOptions{ Tag: fmt.Sprintf("routing %s rpc", method), Backoff: retryBackoff, MaxBackoff: maxRetryBackoff, Constant: 2, MaxAttempts: 0, // retry indefinitely } err := util.RetryWithBackoff(retryOpts, func() (bool, error) { rangeMeta, err := db.rangeCache.LookupRangeMetadata(header.Key) if err == nil { err = db.sendRPC(rangeMeta.Replicas, method, args, chanVal.Interface()) } if err != nil { // Range metadata might be out of date - evict it. db.rangeCache.EvictCachedRangeMetadata(header.Key) // If retryable, allow outer loop to retry. if retryErr, ok := err.(util.Retryable); ok && retryErr.CanRetry() { glog.Warningf("failed to invoke %s: %v", method, err) return false, nil } // TODO(mtracy): Make sure that errors that clearly result from // a stale metadata cache are retryable. } return true, err }) if err != nil { replyVal := reflect.ValueOf(reply) reflect.Indirect(replyVal).FieldByName("Error").Set(reflect.ValueOf(err)) chanVal.Send(replyVal) } }() return chanVal.Interface() }