mirror of https://github.com/golang/go.git
Native Client SRPC (simple RPC), both server and client.
R=r DELTA=958 (958 added, 0 deleted, 0 changed) OCL=35096 CL=35106
This commit is contained in:
parent
5c2c57e5db
commit
8f8b735295
|
|
@ -0,0 +1,13 @@
|
|||
# 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.
|
||||
|
||||
include $(GOROOT)/src/Make.$(GOARCH)
|
||||
|
||||
TARG=nacl/srpc
|
||||
GOFILES=\
|
||||
client.go\
|
||||
msg.go\
|
||||
server.go\
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
// 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,532 @@
|
|||
// 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.
|
||||
|
||||
// SRPC constants, data structures, and parsing.
|
||||
|
||||
package srpc
|
||||
|
||||
import (
|
||||
"bytes";
|
||||
"math";
|
||||
"os";
|
||||
"strconv";
|
||||
"syscall";
|
||||
"unsafe";
|
||||
)
|
||||
|
||||
// An Errno is an SRPC status code.
|
||||
type Errno uint32
|
||||
const (
|
||||
OK Errno = 256 + iota;
|
||||
ErrBreak;
|
||||
ErrMessageTruncated;
|
||||
ErrNoMemory;
|
||||
ErrProtocolMismatch;
|
||||
ErrBadRPCNumber;
|
||||
ErrBadArgType;
|
||||
ErrTooFewArgs;
|
||||
ErrTooManyArgs;
|
||||
ErrInArgTypeMismatch;
|
||||
ErrOutArgTypeMismatch;
|
||||
ErrInternalError;
|
||||
ErrAppError;
|
||||
)
|
||||
|
||||
var errstr = [...]string {
|
||||
OK-OK: "ok",
|
||||
ErrBreak-OK: "break",
|
||||
ErrMessageTruncated-OK: "message truncated",
|
||||
ErrNoMemory-OK: "out of memory",
|
||||
ErrProtocolMismatch-OK: "protocol mismatch",
|
||||
ErrBadRPCNumber-OK: "invalid RPC method number",
|
||||
ErrBadArgType-OK: "unexpected argument type",
|
||||
ErrTooFewArgs-OK: "too few arguments",
|
||||
ErrTooManyArgs-OK: "too many arguments",
|
||||
ErrInArgTypeMismatch-OK: "input argument type mismatch",
|
||||
ErrOutArgTypeMismatch-OK: "output argument type mismatch",
|
||||
ErrInternalError-OK: "internal error",
|
||||
ErrAppError-OK: "application error",
|
||||
}
|
||||
|
||||
func (e Errno) String() string {
|
||||
if e < OK || int(e-OK) >= len(errstr) {
|
||||
return "Errno(" + strconv.Itoa64(int64(e)) + ")"
|
||||
}
|
||||
return errstr[e - OK];
|
||||
}
|
||||
|
||||
// A *msgHdr is the data argument to the imc_recvmsg
|
||||
// and imc_sendmsg system calls. Because it contains unchecked
|
||||
// counts trusted by the system calls, the data structure is unsafe
|
||||
// to expose to package clients.
|
||||
type msgHdr struct {
|
||||
iov *iov;
|
||||
niov int32;
|
||||
desc *int32;
|
||||
ndesc int32;
|
||||
flags uint32;
|
||||
}
|
||||
|
||||
// A single region for I/O. Just as unsafe as msgHdr.
|
||||
type iov struct {
|
||||
base *byte;
|
||||
len int32;
|
||||
}
|
||||
|
||||
// A msg is the Go representation of a message.
|
||||
type msg struct {
|
||||
rdata []byte; // data being consumed during message parsing
|
||||
rdesc []int32; // file descriptors being consumed during message parsing
|
||||
wdata []byte; // data being generated when replying
|
||||
|
||||
// parsed version of message
|
||||
protocol uint32;
|
||||
requestId uint64;
|
||||
isReq bool;
|
||||
rpcNumber uint32;
|
||||
gotHeader bool;
|
||||
status Errno; // error code sent in response
|
||||
Arg []interface{}; // method arguments
|
||||
Ret []interface{}; // method results
|
||||
Size []int; // max sizes for arrays in method results
|
||||
fmt string; // accumulated format string of arg+":"+ret
|
||||
}
|
||||
|
||||
// A msgReceiver receives messages from a file descriptor.
|
||||
type msgReceiver struct {
|
||||
fd int;
|
||||
data [128*1024]byte;
|
||||
desc [8]int32;
|
||||
hdr msgHdr;
|
||||
iov iov;
|
||||
}
|
||||
|
||||
func (r *msgReceiver) recv() (*msg, os.Error) {
|
||||
// Init pointers to buffers where syscall recvmsg can write.
|
||||
r.iov.base = &r.data[0];
|
||||
r.iov.len = int32(len(r.data));
|
||||
r.hdr.iov = &r.iov;
|
||||
r.hdr.niov = 1;
|
||||
r.hdr.desc = &r.desc[0];
|
||||
r.hdr.ndesc = int32(len(r.desc));
|
||||
n, _, e := syscall.Syscall(syscall.SYS_IMC_RECVMSG, uintptr(r.fd), uintptr(unsafe.Pointer(&r.hdr)), 0);
|
||||
if e != 0 {
|
||||
return nil, os.NewSyscallError("imc_recvmsg", int(e));
|
||||
}
|
||||
|
||||
// Make a copy of the data so that the next recvmsg doesn't
|
||||
// smash it. The system call did not update r.iov.len. Instead it
|
||||
// returned the total byte count as n.
|
||||
m := new(msg);
|
||||
m.rdata = make([]byte, n);
|
||||
bytes.Copy(m.rdata, &r.data);
|
||||
|
||||
// Make a copy of the desc too.
|
||||
// The system call *did* update r.hdr.ndesc.
|
||||
if r.hdr.ndesc > 0 {
|
||||
m.rdesc = make([]int32, r.hdr.ndesc);
|
||||
for i := range m.rdesc {
|
||||
m.rdesc[i] = r.desc[i];
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil;
|
||||
}
|
||||
|
||||
// A msgSender sends messages on a file descriptor.
|
||||
type msgSender struct {
|
||||
fd int;
|
||||
hdr msgHdr;
|
||||
iov iov;
|
||||
|
||||
}
|
||||
|
||||
func (s *msgSender) send(m *msg) os.Error {
|
||||
if len(m.wdata) > 0 {
|
||||
s.iov.base = &m.wdata[0];
|
||||
}
|
||||
s.iov.len = int32(len(m.wdata));
|
||||
s.hdr.iov = &s.iov;
|
||||
s.hdr.niov = 1;
|
||||
s.hdr.desc = nil;
|
||||
s.hdr.ndesc = 0;
|
||||
_, _, e := syscall.Syscall(syscall.SYS_IMC_SENDMSG, uintptr(s.fd), uintptr(unsafe.Pointer(&s.hdr)), 0);
|
||||
if e != 0 {
|
||||
return os.NewSyscallError("imc_sendmsg", int(e));
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Reading from msg.rdata.
|
||||
func (m *msg) uint8() uint8 {
|
||||
if m.status != OK {
|
||||
return 0;
|
||||
}
|
||||
if len(m.rdata) < 1 {
|
||||
m.status = ErrMessageTruncated;
|
||||
return 0;
|
||||
}
|
||||
x := m.rdata[0];
|
||||
m.rdata = m.rdata[1:len(m.rdata)];
|
||||
return x;
|
||||
}
|
||||
|
||||
func (m *msg) uint32() uint32 {
|
||||
if m.status != OK {
|
||||
return 0;
|
||||
}
|
||||
if len(m.rdata) < 4 {
|
||||
m.status = ErrMessageTruncated;
|
||||
return 0;
|
||||
}
|
||||
b := m.rdata[0:4];
|
||||
x := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24;
|
||||
m.rdata = m.rdata[4:len(m.rdata)];
|
||||
return x;
|
||||
}
|
||||
|
||||
func (m *msg) uint64() uint64 {
|
||||
if m.status != OK {
|
||||
return 0;
|
||||
}
|
||||
if len(m.rdata) < 8 {
|
||||
m.status = ErrMessageTruncated;
|
||||
return 0;
|
||||
}
|
||||
b := m.rdata[0:8];
|
||||
x := uint64(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24);
|
||||
x |= uint64(uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24)<<32;
|
||||
m.rdata = m.rdata[8:len(m.rdata)];
|
||||
return x;
|
||||
}
|
||||
|
||||
func (m *msg) bytes(n int) []byte {
|
||||
if m.status != OK {
|
||||
return nil;
|
||||
}
|
||||
if len(m.rdata) < n {
|
||||
m.status = ErrMessageTruncated;
|
||||
return nil;
|
||||
}
|
||||
x := m.rdata[0:n];
|
||||
m.rdata = m.rdata[n:len(m.rdata)];
|
||||
return x;
|
||||
}
|
||||
|
||||
// Writing to msg.wdata.
|
||||
func (m *msg) grow(n int) []byte {
|
||||
i := len(m.wdata);
|
||||
if i+n > cap(m.wdata) {
|
||||
a := make([]byte, i, (i+n)*2);
|
||||
bytes.Copy(a, m.wdata);
|
||||
m.wdata = a;
|
||||
}
|
||||
m.wdata = m.wdata[0:i+n];
|
||||
return m.wdata[i:i+n];
|
||||
}
|
||||
|
||||
func (m *msg) wuint8(x uint8) {
|
||||
m.grow(1)[0] = x;
|
||||
}
|
||||
|
||||
func (m *msg) wuint32(x uint32) {
|
||||
b := m.grow(4);
|
||||
b[0] = byte(x);
|
||||
b[1] = byte(x>>8);
|
||||
b[2] = byte(x>>16);
|
||||
b[3] = byte(x>>24);
|
||||
}
|
||||
|
||||
func (m *msg) wuint64(x uint64) {
|
||||
b := m.grow(8);
|
||||
lo := uint32(x);
|
||||
b[0] = byte(lo);
|
||||
b[1] = byte(lo>>8);
|
||||
b[2] = byte(lo>>16);
|
||||
b[3] = byte(lo>>24);
|
||||
hi := uint32(x>>32);
|
||||
b[4] = byte(hi);
|
||||
b[5] = byte(hi>>8);
|
||||
b[6] = byte(hi>>16);
|
||||
b[7] = byte(hi>>24);
|
||||
}
|
||||
|
||||
func (m *msg) wbytes(p []byte) {
|
||||
bytes.Copy(m.grow(len(p)), p);
|
||||
}
|
||||
|
||||
func (m *msg) wstring(s string) {
|
||||
b := m.grow(len(s));
|
||||
for i := range b {
|
||||
b[i] = s[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Parsing of RPC header and arguments.
|
||||
//
|
||||
// The header format is:
|
||||
// protocol uint32;
|
||||
// requestId uint64;
|
||||
// isReq bool;
|
||||
// rpcNumber uint32;
|
||||
// status uint32; // only for response
|
||||
//
|
||||
// Then a sequence of values follow, preceded by the length:
|
||||
// nvalue uint32;
|
||||
//
|
||||
// Each value begins with a one-byte type followed by
|
||||
// type-specific data.
|
||||
//
|
||||
// type uint8;
|
||||
// 'b': x bool;
|
||||
// 'C': len uint32; x [len]byte;
|
||||
// 'd': x float64;
|
||||
// 'D': len uint32; x [len]float64;
|
||||
// 'h': x int; // handle aka file descriptor
|
||||
// 'i': x int32;
|
||||
// 'I': len uint32; x [len]int32;
|
||||
// 's': len uint32; x [len]byte;
|
||||
//
|
||||
// If this is a request, a sequence of pseudo-values follows,
|
||||
// preceded by its length (nvalue uint32).
|
||||
//
|
||||
// Each pseudo-value is a one-byte type as above,
|
||||
// followed by a maximum length (len uint32)
|
||||
// for the 'C', 'D', 'I', and 's' types.
|
||||
//
|
||||
// In the Go msg, we represent each argument by
|
||||
// an empty interface containing the type of x in the
|
||||
// corresponding case.
|
||||
|
||||
// The current protocol number.
|
||||
const protocol = 0xc0da0002
|
||||
|
||||
func (m *msg) unpackHeader() {
|
||||
m.protocol = m.uint32();
|
||||
m.requestId = m.uint64();
|
||||
m.isReq = m.uint8() != 0;
|
||||
m.rpcNumber = m.uint32();
|
||||
m.gotHeader = m.status == OK; // signal that header parsed successfully
|
||||
if m.gotHeader && !m.isReq {
|
||||
status := Errno(m.uint32());
|
||||
m.gotHeader = m.status == OK; // still ok?
|
||||
if m.gotHeader {
|
||||
m.status = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *msg) packHeader() {
|
||||
m.wuint32(m.protocol);
|
||||
m.wuint64(m.requestId);
|
||||
if m.isReq {
|
||||
m.wuint8(1);
|
||||
} else {
|
||||
m.wuint8(0);
|
||||
}
|
||||
m.wuint32(m.rpcNumber);
|
||||
if !m.isReq {
|
||||
m.wuint32(uint32(m.status));
|
||||
}
|
||||
}
|
||||
|
||||
func (m *msg) unpackValues(v []interface{}) {
|
||||
for i := range v {
|
||||
t := m.uint8();
|
||||
m.fmt += string(t);
|
||||
switch t {
|
||||
default:
|
||||
if m.status == OK {
|
||||
m.status = ErrBadArgType;
|
||||
}
|
||||
return;
|
||||
case 'b': // bool[1]
|
||||
v[i] = m.uint8() > 0;
|
||||
case 'C': // char array
|
||||
v[i] = m.bytes(int(m.uint32()));
|
||||
case 'd': // double
|
||||
v[i] = math.Float64frombits(m.uint64());
|
||||
case 'D': // double array
|
||||
a := make([]float64, int(m.uint32()));
|
||||
for j := range a {
|
||||
a[j] = math.Float64frombits(m.uint64());
|
||||
}
|
||||
v[i] = a;
|
||||
case 'h': // file descriptor (handle)
|
||||
if len(m.rdesc) == 0 {
|
||||
if m.status == OK {
|
||||
m.status = ErrBadArgType;
|
||||
}
|
||||
return;
|
||||
}
|
||||
v[i] = int(m.rdesc[0]);
|
||||
m.rdesc = m.rdesc[1:len(m.rdesc)];
|
||||
case 'i': // int
|
||||
v[i] = int32(m.uint32());
|
||||
case 'I': // int array
|
||||
a := make([]int32, int(m.uint32()));
|
||||
for j := range a {
|
||||
a[j] = int32(m.uint32());
|
||||
}
|
||||
v[i] = a;
|
||||
case 's': // string
|
||||
v[i] = string(m.bytes(int(m.uint32())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *msg) packValues(v []interface{}) {
|
||||
for i := range v {
|
||||
switch x := v[i].(type) {
|
||||
default:
|
||||
if m.status == OK {
|
||||
m.status = ErrInternalError;
|
||||
}
|
||||
return;
|
||||
case bool:
|
||||
m.wuint8('b');
|
||||
if x {
|
||||
m.wuint8(1);
|
||||
} else {
|
||||
m.wuint8(0);
|
||||
}
|
||||
case []byte:
|
||||
m.wuint8('C');
|
||||
m.wuint32(uint32(len(x)));
|
||||
m.wbytes(x);
|
||||
case float64:
|
||||
m.wuint8('d');
|
||||
m.wuint64(math.Float64bits(x));
|
||||
case []float64:
|
||||
m.wuint8('D');
|
||||
m.wuint32(uint32(len(x)));
|
||||
for _, f := range x {
|
||||
m.wuint64(math.Float64bits(f));
|
||||
}
|
||||
case int32:
|
||||
m.wuint8('i');
|
||||
m.wuint32(uint32(x));
|
||||
case []int32:
|
||||
m.wuint8('I');
|
||||
m.wuint32(uint32(len(x)));
|
||||
for _, i := range x {
|
||||
m.wuint32(uint32(i));
|
||||
}
|
||||
case string:
|
||||
m.wuint8('s');
|
||||
m.wuint32(uint32(len(x)));
|
||||
m.wstring(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *msg) unpackRequest() {
|
||||
m.status = OK;
|
||||
if m.unpackHeader(); m.status != OK {
|
||||
return;
|
||||
}
|
||||
if m.protocol != protocol || !m.isReq {
|
||||
m.status = ErrProtocolMismatch;
|
||||
return;
|
||||
}
|
||||
|
||||
// type-tagged argument values
|
||||
m.Arg = make([]interface{}, m.uint32());
|
||||
m.unpackValues(m.Arg);
|
||||
if m.status != OK {
|
||||
return;
|
||||
}
|
||||
|
||||
// type-tagged expected return sizes.
|
||||
// fill in zero values for each return value
|
||||
// and save sizes.
|
||||
m.fmt += ":";
|
||||
m.Ret = make([]interface{}, m.uint32());
|
||||
m.Size = make([]int, len(m.Ret));
|
||||
for i := range m.Ret {
|
||||
t := m.uint8();
|
||||
m.fmt += string(t);
|
||||
switch t {
|
||||
default:
|
||||
if m.status == OK {
|
||||
m.status = ErrBadArgType;
|
||||
}
|
||||
return;
|
||||
case 'b': // bool[1]
|
||||
m.Ret[i] = false;
|
||||
case 'C': // char array
|
||||
m.Size[i] = int(m.uint32());
|
||||
m.Ret[i] = []byte(nil);
|
||||
case 'd': // double
|
||||
m.Ret[i] = float64(0);
|
||||
case 'D': // double array
|
||||
m.Size[i] = int(m.uint32());
|
||||
m.Ret[i] = []float64(nil);
|
||||
case 'h': // file descriptor (handle)
|
||||
m.Ret[i] = int(-1);
|
||||
case 'i': // int
|
||||
m.Ret[i] = int32(0);
|
||||
case 'I': // int array
|
||||
m.Size[i] = int(m.uint32());
|
||||
m.Ret[i] = []int32(nil);
|
||||
case 's': // string
|
||||
m.Size[i] = int(m.uint32());
|
||||
m.Ret[i] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *msg) packRequest() {
|
||||
m.packHeader();
|
||||
m.wuint32(uint32(len(m.Arg)));
|
||||
m.packValues(m.Arg);
|
||||
m.wuint32(uint32(len(m.Ret)));
|
||||
for i, v := range m.Ret {
|
||||
switch x := v.(type) {
|
||||
case bool:
|
||||
m.wuint8('b');
|
||||
case []byte:
|
||||
m.wuint8('C');
|
||||
m.wuint32(uint32(m.Size[i]));
|
||||
case float64:
|
||||
m.wuint8('d');
|
||||
case []float64:
|
||||
m.wuint8('D');
|
||||
m.wuint32(uint32(m.Size[i]));
|
||||
case int:
|
||||
m.wuint8('h');
|
||||
case int32:
|
||||
m.wuint8('i');
|
||||
case []int32:
|
||||
m.wuint8('I');
|
||||
m.wuint32(uint32(m.Size[i]));
|
||||
case string:
|
||||
m.wuint8('s');
|
||||
m.wuint32(uint32(m.Size[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *msg) unpackResponse() {
|
||||
m.status = OK;
|
||||
if m.unpackHeader(); m.status != OK {
|
||||
return;
|
||||
}
|
||||
if m.protocol != protocol || m.isReq {
|
||||
m.status = ErrProtocolMismatch;
|
||||
return;
|
||||
}
|
||||
|
||||
// type-tagged return values
|
||||
m.fmt = "";
|
||||
m.Ret = make([]interface{}, m.uint32());
|
||||
m.unpackValues(m.Ret);
|
||||
}
|
||||
|
||||
func (m *msg) packResponse() {
|
||||
m.packHeader();
|
||||
m.wuint32(uint32(len(m.Ret)));
|
||||
m.packValues(m.Ret);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
// 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.
|
||||
|
||||
// SRPC server
|
||||
|
||||
package srpc
|
||||
|
||||
import (
|
||||
"bytes";
|
||||
"log";
|
||||
"os";
|
||||
"syscall";
|
||||
)
|
||||
|
||||
// TODO(rsc): I'd prefer to make this
|
||||
// type Handler func(m *msg) Errno
|
||||
// but NaCl can't use closures.
|
||||
// The explicit interface is a way to attach state.
|
||||
|
||||
// A Handler is a handler for an SRPC method.
|
||||
// It reads arguments from m.Arg, checks m.Size for array limits,
|
||||
// writes return values to m.Ret, and returns an Errno status code.
|
||||
type Handler interface {
|
||||
Run(m *msg) Errno
|
||||
}
|
||||
|
||||
type method struct {
|
||||
name string;
|
||||
fmt string;
|
||||
handler Handler;
|
||||
}
|
||||
|
||||
var rpcMethod []method
|
||||
|
||||
// BUG(rsc): Add's format string should be replaced by analyzing the
|
||||
// type of an arbitrary func passed in an interface{} using reflection.
|
||||
|
||||
// Add registers a handler for the named method.
|
||||
// Fmt is a Native Client format string, a sequence of
|
||||
// alphabetic characters representing the types of the parameter values,
|
||||
// a colon, and then a sequence of alphabetic characters
|
||||
// representing the types of the returned values.
|
||||
// The format characters and corresponding dynamic types are:
|
||||
//
|
||||
// b bool
|
||||
// C []byte
|
||||
// d float64
|
||||
// D []float64
|
||||
// h int // a file descriptor (aka handle)
|
||||
// i int32
|
||||
// I []int32
|
||||
// s string
|
||||
//
|
||||
func Add(name, fmt string, handler Handler) {
|
||||
n := len(rpcMethod);
|
||||
if n >= cap(rpcMethod) {
|
||||
a := make([]method, n, (n+4)*2);
|
||||
for i := range a {
|
||||
a[i] = rpcMethod[i];
|
||||
}
|
||||
rpcMethod = a;
|
||||
}
|
||||
rpcMethod = rpcMethod[0:n+1];
|
||||
rpcMethod[n] = method{name, fmt, handler};
|
||||
}
|
||||
|
||||
// Serve accepts new SRPC connections from the file descriptor fd
|
||||
// and answers RPCs issued on those connections.
|
||||
// It closes fd and returns an error if the imc_accept system call fails.
|
||||
func Serve(fd int) os.Error {
|
||||
defer syscall.Close(fd);
|
||||
|
||||
for {
|
||||
cfd, _, e := syscall.Syscall(syscall.SYS_IMC_ACCEPT, uintptr(fd), 0, 0);
|
||||
if e != 0 {
|
||||
return os.NewSyscallError("imc_accept", int(e));
|
||||
}
|
||||
go serveLoop(int(cfd));
|
||||
}
|
||||
panic("unreachable");
|
||||
}
|
||||
|
||||
func serveLoop(fd int) {
|
||||
c := make(chan *msg);
|
||||
go sendLoop(fd, c);
|
||||
|
||||
var r msgReceiver;
|
||||
r.fd = fd;
|
||||
for {
|
||||
m, err := r.recv();
|
||||
if err != nil {
|
||||
break;
|
||||
}
|
||||
m.unpackRequest();
|
||||
if !m.gotHeader {
|
||||
log.Stderrf("cannot unpack header: %s", m.status);
|
||||
continue;
|
||||
}
|
||||
// log.Stdoutf("<- %#v", m);
|
||||
m.isReq = false; // set up for response
|
||||
go serveMsg(m, c);
|
||||
}
|
||||
close(c);
|
||||
}
|
||||
|
||||
func sendLoop(fd int, c <-chan *msg) {
|
||||
var s msgSender;
|
||||
s.fd = fd;
|
||||
for m := range c {
|
||||
// log.Stdoutf("-> %#v", m);
|
||||
m.packResponse();
|
||||
s.send(m);
|
||||
}
|
||||
syscall.Close(fd);
|
||||
}
|
||||
|
||||
func serveMsg(m *msg, c chan<- *msg) {
|
||||
if m.status != OK {
|
||||
c <- m;
|
||||
return;
|
||||
}
|
||||
if m.rpcNumber >= uint32(len(rpcMethod)) {
|
||||
m.status = ErrBadRPCNumber;
|
||||
c <- m;
|
||||
return;
|
||||
}
|
||||
|
||||
meth := &rpcMethod[m.rpcNumber];
|
||||
if meth.fmt != m.fmt {
|
||||
switch {
|
||||
case len(m.fmt) < len(meth.fmt):
|
||||
m.status = ErrTooFewArgs;
|
||||
case len(m.fmt) > len(meth.fmt):
|
||||
m.status = ErrTooManyArgs;
|
||||
default:
|
||||
// There's a type mismatch.
|
||||
// It's an in-arg mismatch if the mismatch happens
|
||||
// before the colon; otherwise it's an out-arg mismatch.
|
||||
m.status = ErrInArgTypeMismatch;
|
||||
for i := 0; i < len(m.fmt) && m.fmt[i] == meth.fmt[i]; i++ {
|
||||
if m.fmt[i] == ':' {
|
||||
m.status = ErrOutArgTypeMismatch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
c <- m;
|
||||
return;
|
||||
}
|
||||
|
||||
m.status = meth.handler.Run(m);
|
||||
c <- m;
|
||||
}
|
||||
|
||||
// ServeRuntime serves RPCs issued by the Native Client embedded runtime.
|
||||
// This should be called by main once all methods have been registered using Add.
|
||||
func ServeRuntime() os.Error {
|
||||
// Call getFd to check that we are running embedded.
|
||||
if _, err := getFd(); err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
// We are running embedded.
|
||||
// The fd returned by getFd is a red herring.
|
||||
// Accept connections on magic fd 3.
|
||||
return Serve(3);
|
||||
}
|
||||
|
||||
// getFd runs the srpc_get_fd system call.
|
||||
func getFd() (fd int, err os.Error) {
|
||||
r1, _, e := syscall.Syscall(syscall.SYS_SRPC_GET_FD, 0, 0, 0);
|
||||
return int(r1), os.NewSyscallError("srpc_get_fd", int(e));
|
||||
}
|
||||
|
||||
// Enabled returns true if SRPC is enabled in the Native Client runtime.
|
||||
func Enabled() bool {
|
||||
_, err:= getFd();
|
||||
return err == nil;
|
||||
}
|
||||
|
||||
// Service #0, service_discovery, returns a list of the other services
|
||||
// and their argument formats.
|
||||
type serviceDiscovery struct{}
|
||||
|
||||
func (serviceDiscovery) Run(m *msg) Errno {
|
||||
var b bytes.Buffer;
|
||||
for _, m := range rpcMethod {
|
||||
b.WriteString(m.name);
|
||||
b.WriteByte(':');
|
||||
b.WriteString(m.fmt);
|
||||
b.WriteByte('\n');
|
||||
}
|
||||
if b.Len() > m.Size[0] {
|
||||
return ErrNoMemory;
|
||||
}
|
||||
m.Ret[0] = b.Bytes();
|
||||
return OK;
|
||||
}
|
||||
|
||||
func init() {
|
||||
Add("service_discovery", ":C", serviceDiscovery{});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue