Native Client currently provides a very basic Remote Procedure Call (RPC) mechanism for implementing services that communicate using only C++ basic types (no structs, unions, etc.). It is called Simple RPC or SRPC.
Adding Simple RPC to Your ModuleWhen the Native Client service runtime (sel_ldr) starts a module, it initiates a thread. To implement a blocking RPC service, a module needs
The simple RPC library provides an easy method to get a simple single-threaded message processing and dispatch loop. Implementing RPC MethodsThe methods that implement the service are written by the user, but differ very little from ordinary C/C++ functions. An example method that uses simple RPC is given below. #include <nacl/nacl_srpc.h>
NaClSrpcError MyMethod(NaClSrpcChannel *channel
NaClSrpcArg **in_args,
NaClSrpcArg **out_args)
{
int v0 = in_args[0]->u.ival;
int v1 = in_args[1]->u.ival;
int v2;
int num = out_args[0]->u.iaval.count;
int *dest = out_args[0]->u.iaval.iarr;
int i;
if (num < 2) return NACL_SRPC_RESULT_APP_ERROR;
*dest++ = v0;
*dest++ = v1;
for (i = 2; i < num; ++i) {
v2 = v0 + v1;
*dest++ = v2;
v0 = v1; v1 = v2;
}
return NACL_SRPC_RESULT_OK;
}
// Export MyMethod as fib, which takes two scalar integers and returns an array of integers.
NACL_SRPC_METHOD("fib:ii:I", MyMethod);
Because the method uses simple RPC it needs to include Method Export Declarations and TypesExporting an RPC method requires using the
Handles are further described below. Adding the Library to Your ModuleAdding simple RPC library support is as easy as adding Using Built-in Method ProcessingAdding the library to your module provides you with two methods for simple testing of your modules. The first is by using sel_ldr directly, by invoking sel_ldr -f mymodule.nexe you would see: RPC Name Input args Output args
0 service_discovery C
1 fib ii I
0>
Typing "service" will return a string containing this information. The rpc command is the most interesting, with the command format being rpc name in_args * out_args where name is the name of the method you want to invoke, and in_args and out_args are space-separated lists of type_char(value) # for scalars type_char(length, v0, v1, ...) # for array input types type_char(length) # for array output types So, for example, typing rpc fib i(1) i(1) * I(5) yields using rpc fib no 1 fib RESULTS: I(5,1,1,2,3,5) Using sel_universal and the plug_test.html ExampleThe same processing loop is used when invoking sel_universal. That is: sel_universal -f mymodule.nexe would be the same. There is also a web-based version in tests/plug_univ/plug_test.html in the source tree. It is currently not capable of handling array types or string arguments. Using SRPC from the BrowserNote: SRPC in the browser is not supported any more. Use the asynchronous PostMessage instead. SRPC may be used to communicate from the browser to Native Client module and to coordinate communication amongst several Native Client modules. To load a Native Client module one needs to use an embed tag such as <embed type="application/x-nacl-srpc" id="nacl" src="srpc_nrd_server.nexe"> The id "nacl" of course could be different, but it is necessary to have a handle to access a scriptable plugin instance. A plugin instance provides two useful basic functions: loading Native Client modules and creating shared memory regions. Loading a Native Client module is illustrated by the following example: var postLoadInit = function() {
if (1 == plugin.__moduleReady) {
clearTimeout(startupTimeout);
// do your scripting here or afterwards.
} else {
if (plugin.__moduleReady == undefined) {
alert('The Native Client plugin was unable to load');
return;
}
startupTimeout = setTimeout(postLoadInit, 100);
}
}
var Init = function() {
var plugin = document.getElementById('nacl');
// Note that at this point the module may not have been loaded yet.
// Scripting must wait for the __moduleReady property above to become 1 before starting.
postLoadInit();
}
Once a module is loaded, any of the methods exported may be invoked using its method name and the proper set of argument types. For example, var arr = service.fib(1, 1, 5); invokes the the To see a working demo of loading and interacting with Native Client modules via SRPC, please take a look at any of the tests in Creating shared memory may also be done by invoking method on a plugin instance. var region_size = 65536;
var plugin = document.getElementById('nacl');
var shared_memory = plugin.__shmFactory(region_size);
The shared_memory.write(offset, length, string); writes var str = shared_memory.read(offset, length); read reads a string of length Shared memory regions are passable to and from Native Client modules as handles. Native Client HandlesHandles provide access to NaCl resource descriptors. Inside of Native Client modules handles are integers, just like Linux file descriptors, and they may be used as described in the intermodule communication module discussion. Most handle types are treated opaquely in JavaScript. Shared memory handles were described above. The only other handle type that has a JavaScript-related action is a socket address handle. Native Client modules typically communicate by creating bound socket / socket address pairs and relying on JavaScript to pass the socket address handle from one module to another. The JavaScript may also open connections to socket address objects returned from Native Client modules. For example, the fragment var connected_socket = socket_address.connect(); results in the creation of a connected socket object "connected_socket" which will implement an SRPC interface. To see a full illustration of creating and passing various sorts of handles, please take a look at In trusted code they are pointer to |
