mirror of
https://github.com/golang/go
synced 2024-11-15 02:10:21 -07:00
211 lines
4.5 KiB
Go
211 lines
4.5 KiB
Go
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
// This package implements Native Client's simple RPC (SRPC).
|
||
|
package srpc
|
||
|
|
||
|
import (
|
||
|
"bytes";
|
||
|
"log";
|
||
|
"os";
|
||
|
"sync";
|
||
|
)
|
||
|
|
||
|
// A Client represents the client side of an SRPC connection.
|
||
|
type Client struct {
|
||
|
fd int; // fd to server
|
||
|
r msgReceiver;
|
||
|
s msgSender;
|
||
|
service map[string]srv; // services by name
|
||
|
out chan *msg; // send to out to write to connection
|
||
|
|
||
|
mu sync.Mutex; // protects pending, idGen
|
||
|
pending map[uint64]*RPC;
|
||
|
idGen uint64; // generator for request IDs
|
||
|
}
|
||
|
|
||
|
// A srv is a single method that the server offers.
|
||
|
type srv struct {
|
||
|
num uint32; // method number
|
||
|
fmt string; // argument format
|
||
|
}
|
||
|
|
||
|
// An RPC represents a single RPC issued by a client.
|
||
|
type RPC struct {
|
||
|
Ret []interface{}; // Return values
|
||
|
Done chan *RPC; // Channel where notification of done arrives
|
||
|
Errno Errno; // Status code
|
||
|
c *Client;
|
||
|
id uint64; // request id
|
||
|
}
|
||
|
|
||
|
// NewClient allocates a new client using the file descriptor fd.
|
||
|
func NewClient(fd int) (c *Client, err os.Error) {
|
||
|
c = new(Client);
|
||
|
c.fd = fd;
|
||
|
c.r.fd = fd;
|
||
|
c.s.fd = fd;
|
||
|
c.service = make(map[string]srv);
|
||
|
c.pending = make(map[uint64]*RPC);
|
||
|
|
||
|
// service discovery request
|
||
|
m := &msg{
|
||
|
protocol: protocol,
|
||
|
isReq: true,
|
||
|
Ret: []interface{}{ []byte(nil) },
|
||
|
Size: []int{ 4000 },
|
||
|
};
|
||
|
m.packRequest();
|
||
|
c.s.send(m);
|
||
|
m, err = c.r.recv();
|
||
|
if err != nil {
|
||
|
return nil, err;
|
||
|
}
|
||
|
m.unpackResponse();
|
||
|
if m.status != OK {
|
||
|
log.Stderrf("NewClient service_discovery: %s", m.status);
|
||
|
return nil, m.status;
|
||
|
}
|
||
|
for n, line := range bytes.Split(m.Ret[0].([]byte), []byte{'\n'}, 0) {
|
||
|
i := bytes.Index(line, []byte{':'});
|
||
|
if i < 0 {
|
||
|
continue;
|
||
|
}
|
||
|
c.service[string(line[0:i])] = srv{uint32(n), string(line[i+1:len(line)])};
|
||
|
}
|
||
|
|
||
|
c.out = make(chan *msg);
|
||
|
go c.input();
|
||
|
go c.output();
|
||
|
return c, nil;
|
||
|
}
|
||
|
|
||
|
func (c *Client) input() {
|
||
|
for {
|
||
|
m, err := c.r.recv();
|
||
|
if err != nil {
|
||
|
log.Exitf("client recv: %s", err);
|
||
|
}
|
||
|
if m.unpackResponse(); m.status != OK {
|
||
|
log.Stderrf("invalid message: %s", m.status);
|
||
|
continue;
|
||
|
}
|
||
|
c.mu.Lock();
|
||
|
rpc, ok := c.pending[m.requestId];
|
||
|
if ok {
|
||
|
c.pending[m.requestId] = nil, false;
|
||
|
}
|
||
|
c.mu.Unlock();
|
||
|
if !ok {
|
||
|
log.Stderrf("unexpected response");
|
||
|
continue;
|
||
|
}
|
||
|
rpc.Ret = m.Ret;
|
||
|
rpc.Done <- rpc;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *Client) output() {
|
||
|
for m := range c.out {
|
||
|
c.s.send(m);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NewRPC creates a new RPC on the client connection.
|
||
|
func (c *Client) NewRPC(done chan *RPC) *RPC {
|
||
|
if done == nil {
|
||
|
done = make(chan *RPC);
|
||
|
}
|
||
|
c.mu.Lock();
|
||
|
id := c.idGen;
|
||
|
c.idGen++;
|
||
|
c.mu.Unlock();
|
||
|
return &RPC{nil, done, OK, c, id};
|
||
|
}
|
||
|
|
||
|
// Start issues an RPC request for method name with the given arguments.
|
||
|
// The RPC r must not be in use for another pending request.
|
||
|
// To wait for the RPC to finish, receive from r.Done and then
|
||
|
// inspect r.Ret and r.Errno.
|
||
|
func (r *RPC) Start(name string, arg []interface{}) {
|
||
|
var m msg;
|
||
|
|
||
|
r.Errno = OK;
|
||
|
r.c.mu.Lock();
|
||
|
srv, ok := r.c.service[name];
|
||
|
if !ok {
|
||
|
r.c.mu.Unlock();
|
||
|
r.Errno = ErrBadRPCNumber;
|
||
|
r.Done <- r;
|
||
|
return;
|
||
|
}
|
||
|
r.c.pending[r.id] = r;
|
||
|
r.c.mu.Unlock();
|
||
|
|
||
|
m.protocol = protocol;
|
||
|
m.requestId = r.id;
|
||
|
m.isReq = true;
|
||
|
m.rpcNumber = srv.num;
|
||
|
m.Arg = arg;
|
||
|
|
||
|
// Fill in the return values and sizes to generate
|
||
|
// the right type chars. We'll take most any size.
|
||
|
|
||
|
// Skip over input arguments.
|
||
|
// We could check them against arg, but the server
|
||
|
// will do that anyway.
|
||
|
i := 0;
|
||
|
for srv.fmt[i] != ':' {
|
||
|
i++;
|
||
|
}
|
||
|
fmt := srv.fmt[i+1:len(srv.fmt)];
|
||
|
|
||
|
// Now the return prototypes.
|
||
|
m.Ret = make([]interface{}, len(fmt) - i);
|
||
|
m.Size = make([]int, len(fmt) - i);
|
||
|
for i := 0; i < len(fmt); i++ {
|
||
|
switch fmt[i] {
|
||
|
default:
|
||
|
log.Exitf("unexpected service type %c", fmt[i]);
|
||
|
case 'b':
|
||
|
m.Ret[i] = false;
|
||
|
case 'C':
|
||
|
m.Ret[i] = []byte(nil);
|
||
|
m.Size[i] = 1<<30;
|
||
|
case 'd':
|
||
|
m.Ret[i] = float64(0);
|
||
|
case 'D':
|
||
|
m.Ret[i] = []float64(nil);
|
||
|
m.Size[i] = 1<<30;
|
||
|
case 'h':
|
||
|
m.Ret[i] = int(-1);
|
||
|
case 'i':
|
||
|
m.Ret[i] = int32(0);
|
||
|
case 'I':
|
||
|
m.Ret[i] = []int32(nil);
|
||
|
m.Size[i] = 1<<30;
|
||
|
case 's':
|
||
|
m.Ret[i] = "";
|
||
|
m.Size[i] = 1<<30;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m.packRequest();
|
||
|
r.c.out <- &m;
|
||
|
}
|
||
|
|
||
|
// Call is a convenient wrapper that starts the RPC request,
|
||
|
// waits for it to finish, and then returns the results.
|
||
|
// Its implementation is:
|
||
|
//
|
||
|
// r.Start(name, arg);
|
||
|
// <-r.Done;
|
||
|
// return r.Ret, r.Errno;
|
||
|
//
|
||
|
func (r *RPC) Call(name string, arg []interface{}) (ret []interface{}, err Errno) {
|
||
|
r.Start(name, arg);
|
||
|
<-r.Done;
|
||
|
return r.Ret, r.Errno;
|
||
|
}
|