Inside the run-time system, a typical RPC interaction involves three parties, the client making the remote procedure call, the server on which the remote procedure is to be run, and the location server responsible for assisting the client in making contact with the server.
The client preparing to invoke a remote procedure call must specify the name of the service to be invoked across the network, the order and types of the parameters to be sent in the procedure call, and the expected type of the return value.
Similarly, a server preparing to accept remote procedure calls from across the network must specify the name of the service it is providing, the order and types of parameters expected, and the type of the return value. In addition, it must register its available services with the location server so that clients may find out about the services. This is similar to putting your telephone number in the Yellow Pages so that customers can find you.
When the client actually makes an RPC, it provides the values of the parameters. The run-time system then contacts the location server to establish a binding between the client and the server. Depending upon the RPC implementation, the binding may be established directly by the location server requesting a connection to be made between the client and the server, or the location server may send enough information to the client so that the client can make the connection request itself. In either case, once the connection is made, the parameter values are marshalled (packed for transmission) and sent as a message to the server. Upon receipt of this message, the server demarshalls the parameters and calls the procedure using the parameter values that were sent by the client. Finally, the return value is marshalled and sent back to the client, where it is demarshalled and used as the return value from the procedure call. At this point, the connection may be terminated or left in place to expedite future RPCs from the client to the server.
As you might have guessed, this process takes time, so the client may or may not want to wait while the RPC is in progress. Therefore, there are two types of RPC, called blocking and non-blocking. A blocking RPC is most like an ordinary procedure call. When the client makes the call, the call does not return until the return value comes back from the server. In contrast, a non-blocking RPC returns immediately, returning the client program an object called a promise. The client program can then go on to do other useful work while the RPC is in progress. When the RPC completes and the return value arrives at the client, the value is placed into the promise object so the client program may claim it.
The client may periodically test a promise to see if it is ready to be claimed. If the client program claims the promise before it is ready, then the client will be blocked until the return value comes back from the RPC for delivery to the client. Thus, making a non-blocking RPC and then immediately claiming the promise is essentially the same as making a blocking RPC.
In this project you will design and implement a non-blocking RPC mechanism on top of the asynchronous data communication provided by Playground. You will implement classes that will support RPC synchronization of client and server modules, and you will build a location server module that will be used to assist clients in connecting to servers. You will test your RPC support using an example client/server application, an air pollution database server and a data browser client.
To simplify your job, you will use the Playground data types for communication of parameters and return values, so you don't have to marshal and demarshal the data yourself. Throughout the lab, we'll assume that all parameters and return values are Playground base types (integers, reals, booleans, and strings). Another simplification is that your location server module will request the creation of Playground connections to establish communication between the client and the server when an RPC is made, so you don't have to worry about low-level communication details.
There are two aspects to an RPC mechanism, declaration and execution.
You will create RPCclient and RPCserver classes to provide the basic RPC functionality. When instances of these classes are created, the constructor will perform the "declaration" part of the RPC. Arguments to the constructor for both RPCserver and RPCclient will include the name of the service and a description of the parameter and return types. (The parameter and return types can be described as you see fit. For example, you might use an array of characters, where different letters represent different types.) The constructor will create a PGtuple containing fields to hold the parameters and return values for communication between the client and the server. The PGclient object will publish this tuple with a unique name. The PGserver object will publish an aggregate of these tuples (for use by multiple clients) with a unique name that it will also forward to the location server in order to register itself.
The execution of an RPC starts at the RPCclient object. Your
RPCclient object should provide a call method that will
take in (a variable number of) parameter values for the call. All
parameters, which will be of type PGvariable pointer, are copied by the call
method into the PGtuple you created for communication with the server.
Then, the call method should send a request message to the location
server. The request message (another PGtuple) should contain the
public name of the PGtuple on the client side that is holding the
parameter values, as well as the name of the service desired. Using
this information, your location server should request a bidirectional
element-to-aggregate connection from the client's PGtuple to the
appropriate presentation entry on the server side. When this
connection is established, the client's parameter values will be
automatically sent to the server for execution of the remote
procedure.
Execution continues on the server side as follows. A new PGtuple will
be created in the aggregate when the element-to-aggregate connection
is established from the client. Then, the client's parameter values
will be assigned into the PGtuple for use by the RPCserver. The
RPCserver should react to the incoming values by calling a virtual
function handler. (The application programmer will have
subclassed the RPCserver, providing an actual handler
method that will accept the parameters, do the work of the server, and
return a result. Some casting will need to be done here since the
parameters types are just pointers to PGvariables.)
After the handler method returns, the RPCserver
should put the return value into the PGtuple for communication back to
the client. The PGtuple may then be removed from the aggregate and
deleted, or left in place to expedite future calls from the client.
(If deleted, the destructor of the PGtuple will automatically request
the termination of the connection to the client, so you don't
have to worry about removing the connection.)
Back on the client side, a reactor should be present to handle
the receipt of the return value from the server. This reactor should
stuff the return value into the promise object that was returned by
the call method. As part of this, the promise's
internal status should be set to ready so that the client's
program can claim the promise.
The claim method on the promise object should return the
value that was returned by the server to the client.
As mentioned earlier, if the
client tries to claim the promise before it is ready, then
the claim method should busy-wait (sit in a loop calling PGsleep
until the promise is ready).
The design of the location server is fairly open-ended. It needs to take registration information from servers and RPC connection requests from clients. It must keep track of server names and locations so it can send out connection requests on behalf of clients.
You will use your RPC mechanism to implement an interactive multidimensional data browser for air pollution data. Data to be browsed has three dimensions: space, time, and the type of atmospheric species (hydrogen, sulfur, or NO2). Your browser application will consist of one or more data servers, a query generator, and a data viewer. You will create Playground modules for the data server and the query generator. EUPHORIA will be used as the data viewer. In the data viewer, the user will specify at which location and at which time they want to view amount of a particular atmospheric species. This information, collectively called the "cursor" in the rest of this assignment, will go to the query generator that will pose a corresponding data query to be sent to the data server using your RPC mechanism. That is, the RPC will ask data server to calculate/retrieve information from the database according to the current cursor. This return value from the RPC is then sent to viewer for display. Details follow.
| Field Number | Value |
|---|---|
| Field 1 | Location Code - unique location identifier |
| Field 2 | Location Name |
| Field 3 | Location Longitude - radians, negative |
| Field 4 | Location Latitude - radians, positive |
| Fields 5-16 | data values for the months Jan. - Dec. 1991 |
Extra Credit: Create one data server module for each atmospheric species. Based on location and time from EUPHORIA, your Query Generator will ask for the corresponding value from every data server using instances of PromiseClient class. Simultaneously display the data for all species in EUPHORIA. Use promises to make all the requests and then go back and claim the return values.
To receive credit for this lab, you should: