func writeReturn(conn n.Conn, msg interface{}) ([]interface{}, error) { if err := conn.Write(msg); err != nil { return nil, err } reply, err := conn.Read() if err != nil { return nil, err } retrn, ok := reply.(*returnMsg) if !ok { return nil, NewError("foreign return type") } if retrn.Err != nil { return nil, err } return retrn.Out, nil }
func (r *Runtime) readGotPtrPtr(ptrPtr []*ptrPtrMsg, conn n.Conn) error { p := make(map[circuit.HandleID]struct{}) for _, pp := range ptrPtr { p[pp.ID] = struct{}{} } for len(p) > 0 { m_, err := conn.Read() if err != nil { return err } m, ok := m_.(*gotPtrMsg) if !ok { return NewError("gotPtrMsg expected") } _, present := p[m.ID] if !present { return NewError("ack'ing unsent ptrPtrMsg") } delete(p, m.ID) } return nil }
func (r *Runtime) serveDial(req *dialMsg, conn n.Conn) { // Go guarantees the defer runs even if panic occurs defer conn.Close() expDial, _ := r.exportValues([]interface{}{PermRef(r.srv.Get(req.Service))}, conn.Addr()) conn.Write(&returnMsg{Out: expDial}) // Waiting for export acks not necessary since expDial is always a permptr. }
func (r *Runtime) serveGetPtr(req *getPtrMsg, conn n.Conn) { defer conn.Close() h := r.exp.Lookup(req.ID) if h == nil { if err := conn.Write(&returnMsg{Err: NewError("getPtr: no exp handle")}); err != nil { // See comment in serveCall. if strings.HasPrefix(err.Error(), "gob") { panic(err) } } return } expReply, _ := r.exportValues([]interface{}{r.Ref(h.Value.Interface())}, conn.Addr()) conn.Write(&returnMsg{Out: expReply}) }
func (r *Runtime) serveCall(req *callMsg, conn n.Conn) { // Go guarantees the defer runs even if panic occurs defer conn.Close() h := r.exp.Lookup(req.ReceiverID) if h == nil { log.Printf("exported handle %v not found", req.ReceiverID.String()) if err := conn.Write(&returnMsg{Err: NewError("reply: no exp handle")}); err != nil { // We need to distinguish between I/O errors and encoding errors. // An encoding error implies bad code (e.g. forgot to register a // type) and therefore is best handled by a panic. An I/O error is // an expected runtime condition, and thus we ignore it (as we are // on the server side). // // XXX: It should be Conn's responsibility to panic on encoding // errors. For extra safety and convenience, we do something hacky // here in trying to guess if we got an encoding error, in case // Conn didn't throw a panic. if strings.HasPrefix(err.Error(), "gob") { panic(err) } } return } fn := h.Type.Func[req.FuncID] if fn == nil { conn.Write(&returnMsg{Err: NewError("no func")}) return } in, err := r.importValues(req.In, fn.InTypes, conn.Addr(), true, nil) if err != nil { conn.Write(&returnMsg{Err: err}) return } reply, err := call(h.Value, h.Type, req.FuncID, in) if err != nil { conn.Write(&returnMsg{Err: err}) return } expReply, ptrPtr := r.exportValues(reply, conn.Addr()) if err = conn.Write(&returnMsg{Out: expReply}); err != nil { // Gob encoding errors will often be the cause of this log.Printf("write error (%s)", err) } r.readGotPtrPtr(ptrPtr, conn) }
func (r *Runtime) serveDropPtr(q *dropPtrMsg, conn n.Conn) { // Go guarantees the defer runs even if panic occurs defer conn.Close() r.exp.Remove(q.ID, conn.Addr()) }
func (r *Runtime) serveGo(req *goMsg, conn n.Conn) { log.Println("Cross-call start") // Go guarantees the defer runs even if panic occurs var exit string defer func() { defer func() { recover() log.Println("Exit: ", exit) os.Exit(0) }() conn.Close() // Potentially unnecessary hack to ensure that last message sent to // caller is received before we die time.Sleep(time.Second) }() exit = "lookup" t := types.FuncTabl.TypeWithID(req.TypeID) if t == nil { conn.Write(&returnMsg{Err: NewError("reply: no func type")}) return } // No need to acknowledge acquisition of re-exported ptrs since, // the caller is waiting for a return message anyway exit = "import" mainID := t.MainID() in, err := r.importValues(req.In, t.Func[mainID].InTypes, conn.Addr(), true, nil) if err != nil { conn.Write(&returnMsg{Err: err}) return } exit = "call" reply, err := call(t.Zero(), t, mainID, in) if err != nil { conn.Write(&returnMsg{Err: err}) return } expReply, ptrPtr := r.exportValues(reply, conn.Addr()) err = conn.Write(&returnMsg{Out: expReply}) r.readGotPtrPtr(ptrPtr, conn) // Wait for any daemonized goroutines to complete r.dwg.Wait() exit = "ok" }