PrevUpHomeNext

Asynchronous Remote Calls

Asynchronous remote call invocation
Asynchronous remote call dispatching

Asynchronous remote calls allow you to initate remote calls on one thread, and have them complete asynchronously on another thread.

Asynchronous remote calls are implemeted in RCF using the RCF::Future<> template. Given an RCF interface:

RCF_BEGIN(I_HelloWorld, "I_HelloWorld")

    // Returns number of characters printed.
    RCF_METHOD_R1(int, Print, const std::string &)

RCF_END(I_HelloWorld)

, an asynchronous call is made by specifying one or more RCF::Future<> arguments. For example:

RcfClient<I_HelloWorld> client(( RCF::TcpEndpoint(port) ));
RCF::Future<int> fRet = client.Print("Hello World");

The thread that executes this code will return immediately. It can subsequently poll for completion:

// Poll until remote call completes.
while (!fRet.ready())
{
    RCF::sleepMs(500);
}

, or it can wait for completion:

// Wait for remote call to complete.
fRet.wait();

Once the call is complete, the return values can be recovered from the relevant Future<> instances:

int charsPrinted = *fRet;

If the call resulted in an error, the error will be thrown when the Future<> instance is dereferenced, as above. Alternatively, the error can be retrieved by calling Future<>::getAsyncException():

std::auto_ptr<RCF::Exception> ePtr = fRet.getAsyncException();

Instead of polling or waiting, the thread that initiates an asynchronous remote call can provide a completion callback that will be called by the RCF runtime, on a background thread, when the call completes:

typedef boost::shared_ptr< RcfClient<I_HelloWorld> > HelloWorldPtr;
void onCallCompleted(HelloWorldPtr client, RCF::Future<int> fRet)
{
    std::auto_ptr<RCF::Exception> ePtr = fRet.getAsyncException();
    if (ePtr.get())
    {
        // Deal with any exception.
        // ...
    }
    else
    {
        int charsPrinted = *fRet;
        // ...
    }
}

RCF::Future<int> fRet;
HelloWorldPtr client( new RcfClient<I_HelloWorld>(RCF::TcpEndpoint(port)) );
fRet = client->Print( 
    RCF::AsyncTwoway( boost::bind(&onCallCompleted, client, fRet)), 
    "Hello World");

Notice that the Future<> arguments are passed as arguments to the completion callback function. Future<> objects are internally reference counted, and can be copied freely, while still referring to the same underlying value.

An asynchronous call in progress can be canceled by disconnecting the client:

client.getClientStub().disconnect();

If a RcfClient is destroyed while an asynchronous call is in progress, the call is automatically disconnected and any asynchronous operations are canceled.

On the server-side, RCF will normally dispatch a remote call on the same server thread that receives the remote call request from the transport. Asynchronous dispatching allows you to instead transfer the remote call over to other threads, freeing up the RCF thread to process other remote calls.

The RCF::RemoteCallContext<> class is used to capture the server-side context of a remote call. RemoteCallContext<> objects can be copied into queues and stored for later execution on arbitrary application threads.

RemoteCallContext<> objects are created from within the corresponding servant implemenation method. Here is a non-asynchronously dispatched Print() method:

RCF_BEGIN(I_HelloWorld, "I_HelloWorld")

    // Returns number of characters printed.
    RCF_METHOD_R1(int, Print, const std::string &)

RCF_END(I_HelloWorld)

class HelloWorldImpl
{
public:
    int Print(const std::string & s)
    {
        std::cout << "I_HelloWorld service: " << s << std::endl;
        return s.length();
    }
};

To instead dispatch the call asynchronously, a RemoteCallContext<> object is created in Print(), with template parameters corresponding to the method signature:

class HelloWorldImpl
{
public:

    typedef RCF::RemoteCallContext<int, const std::string&> PrintContext;

    int Print(const std::string & s)
    {
        // Capture current remote call context.
        PrintContext printContext(RCF::getCurrentRcfSession());

        // Create a new thread to dispatch the remote call.
        RCF::ThreadPtr threadPtr( new RCF::Thread( boost::bind(
            &HelloWorldImpl::threadFunc, 
            this, 
            printContext) ) );

        return 0;
    }

    void threadFunc(PrintContext printContext)
    {
        const std::string & s = printContext.parameters().a1.get();
        std::cout << "I_HelloWorld service: " << s << std::endl;
        printContext.parameters().r.set( s.length() );
        printContext.commit();
    }
};

Once created, the RemoteCallContext<> can be stored and copied like any other C++ object. When the Print() function returns, RCF will not send a response to the client. The response will only be sent when RemoteCallContext<>::commit() is called.

RemoteCallContext::parameters() provides access to all the parameters of the remote call, including the return value. We can access in parameters:

const std::string & s = printContext.parameters().a1.get();

, and set out parameters (in this case the return value):

printContext.parameters().r.set( s.length() );


PrevUpHomeNext