Issuing calls through the stub

Examples of calling one-way and two-way functions are made using interface Ix.


$interface+ rmi Ix
{
    void Sum([in]int32 a,[in]int32 b,[out]int32& c);   // c = a+b
    void PrintInt([in]int32 x);                        // Print the value of x
};

Function calls through a stub are always asynchronous. All function calls on a given stub (whether for one-way or two-way functions) are invoked on the receiver in the same order they were invoked on the stub.

One-way calls

PrintInt() has no out-parameters and therefore is referred to as a one-way function. This means there will be no response message (not even an acknowledge) paired with the request message.


stub->PrintInt(1);
stub->PrintInt(2);
stub->PrintInt(3);

One-way functions are very efficient (e.g. 10 million function calls per second is possible on a modest machine).

Two-way calls

In the interface Ix, Sum() has two in-parameters (a,b) and one out-parameter (c). A client can send outgoing messages through a stub like this


int32 c;
stub->Sum(10,20,c);     // asynchronously calculate c = 10+20 = 30
Mark(rmiCaller);
Wait(rmiCaller);
assert(c == 30);

Even though Sum() is a two-way function it is called asynchronously. Just after the call on the stub the message may not yet have been delivered to the receiver and of course the out-parameter(s) typically won't have been assigned. The calls to Mark() and Wait() are necessary to ensure the response has been received and the out-parameter updated.

In more detail the following sequence of events occur

  1. stub->Sum() is called making the sender serialise a "request" message. The in-parameters (a=10, b=20) are serialised as part of the request. This message is written to local buffers and not flushed.
  2. stub->Sum() returns before flushing the request or waiting for a response message! It cannot be assumed c has been assigned to 30!
  3. Mark() is called on the sender message queue. This call sets a mark position associated with all the messages that have been issued on the stub prior to the call to Mark(). This call also queues a special message to request a flush on the receiver.
  4. Wait() is called on the message queue. This flushes all outgoing messages, then it blocks the calling thread until all responses for all calls issued on the stub up to the previously set mark position have been received and processed.
  5. The receiver deserialises the request, forwards the call onto the local recipient used to process all messages, and sends back a "response" message.
  6. The sender deserialises the response (i.e. the out parameters) then releases the thread blocking on the call to Wait().

The advantage of the approach becomes more apparent when many calls on the stub are made and they are effectively performed in parallel, reducing the significant overhead of round trip network delays.


int c1, c2, c3;
stub->Sum(1,2,c1);
stub->Sum(5,4,c2);
stub->Sum(7,2,c3);
MarkAndWait(rmiCaller);

Single producer

The stub is not threadsafe. Clients must ensure that all calls through the stub are serialised, as well as calls to Flush() on the RmiCaller.

No upperbound on write Buffering

The stub allows for infinite write buffering (meaning that it won't block on I/O because a buffer has reached some upper bound). However when buffers reach 4MB asynchronous sending of buffered messages is automatically triggered.

Asynchronous flushing

Flush() on the RmiCaller is asynchronous - meaning that is causes flushing to occur but returns before waiting for the flush to have completed.