OpenClovis Logo

API Usage Examples
Remote Method Dispatch

Code Examples. More...

Code Examples.

The recommended way to use RMD is to generate the code throught IDE (refer IDE User Guide). The code thus generate works irrespective of the architecture as the arguments are marshalled at sender side and unmarshalled on receiver side using the XDR library.

If there is a need to use RMD Library directly we have below various examples to illustrate the usage of RMD. There are various possibilities with the many flags and options available with RMD. We cover the recommended way to use RMD in the examples.

The communication parties have a client-server relationship. The server exposes certain services in conjunction with the Exectuion Object (EO) infrastructure. The client tries to execute these services either locally or remotely.

The two modes - syncrhonous and asynchronous calls can be made with or without reply from the client. The four combinations explained are:

Server Side:

For the client to make a RMD call the server should expose the services as illustrated below:

A typical service provided by an EO (RMD server here) will look as below:

static ClRcT clRmdServiceExposedByServer(ClEoDataT data,
ClBufferHandleT inMsgHandle,
ClBufferHandleT outMsgHandle)
{
ClRcT rc = CL_OK;
ClUint32T inMsgLength = 0;
ClUint8T *pInData = NULL;
rc = clBufferLengthGet(inMsgHandle, &inMsgLength);
if (CL_OK != rc)
{
clOsalPrintf("clBufferLengthGet() failed, rc[%#X]", rc);
goto failure;
}
rc = clBufferFlatten(inMsgHandle, &pInData);
// alternatively clBufferNBytesRead() can be used with an allocated
// memory
if (CL_OK != rc)
{
clOsalPrintf("clBufferFlatten() failed, rc[%#X]", rc);
goto failure;
}
// The service code will recide here. When the processing is
// complete the application(server) may want to populate the
// outMsgHandle as desired.
clOsalPrintf("\nRmd Request being processed...\n\n");
rc = clBufferNBytesWrite(outMsgHandle, pInData, inMsgLength);
{
clOsalPrintf("clBufferNBytesWrite() failed, rc[%#X]", rc);
goto buffer_flatten;
}
buffer_flatten:
clHeapFree(pInData); // Free the buffer from clBufferFlatten()
failure:
return rc;
}

Many such services as above can be installed into the native table of EO (RMD server) as illustrated below. The serviceTable needs to be populated with all the services that need to be exposed and then installed via the call - clEoClientInstall() during the initialize as described further below:

static ClEoPayloadWithReplyCallbackT serverFuncList[] = {
NULL,
clRmdServiceExposedByServer, /* Service ID 0x1 */
// List of all the services exposed by the server
// which can be referenced using the index in the
// service table - serverFuncList[].
};

The native table (or any other client table) is installed as illustrated below. These calls usually reside in the clCompAppInitialize() callback registered in the clEoConfig structure of the EO.

static ClRcT clRmdServerInit(void)
{
ClRcT rc = CL_OK;
ClEoExecutionObjT *pEoObj = NULL;
rc = clEoMyEoObjectGet(&pEoObj);
if(CL_OK != rc)
{
clOsalPrintf("clEoMyEoObjectGet() failed, rc[%#X]\n");
goto failure;
}
serverFuncList, 0,
CL_SIZEOF_ARRAY(serverFuncList));
if(CL_OK != rc)
{
clOsalPrintf("clEoClientInstall() failed, rc[%#X]\n");
goto failure;
}
failure:
return rc;
}

The uninstall of the native table is done as illustrated below. Such calls reside in the clCompAppTerminate() registered with the Component Manager (CPM).

static ClRcT clRmdServerExit(void)
{
ClRcT rc = CL_OK;
clOsalPrintf("inside function %s\n", __FUNCTION__);
rc = clEoMyEoObjectGet(&eoObj);
if (CL_OK != rc)
{
return rc;
}
clOsalPrintf("exiting function %s\n", __FUNCTION__);
return CL_OK;
}

Client Side:

One of the simpler ways to invoke an RMD is to make a synchronous call with the default flags but we strongly recommend using CL_RMD_CALL_NON_PERSISTENT flag to avoid any unnecessary duplication of buffers (which is the default) unless the inMsgHandle needs to be re-used. Care should be taken in releasing the resources allocated like the outMsgHandle which is not applicable in this case.

static ClRcT clRmdSyncWithoutReply(void)
{
ClRcT rc = CL_OK;
ClIocAddressT destAddr = {{0}};
// Remote Procedure to be invoked
ClUint32T funcId =
ClBufferHandleT inMsgHandle = 0;
ClBufferHandleT outMsgHandle = 0;
ClUint32T flags = CL_RMD_CALL_NON_PERSISTENT;
ClUint8T inArguments[] =
"The bytestream of arguments to be passed to dest "
"(marshalled if necessary to make endian neutral)";
// Update the destination address
// If destination(server) is on the same node
// Well known destionation(server) port
destAddr.iocPhyAddress.portId = 0x3100;
rc = clBufferCreate(&inMsgHandle);
if(CL_OK != rc)
{
clOsalPrintf("clBufferCreate() failed, rc[%#X]\n");
goto failure;
}
rc = clBufferNBytesWrite(inMsgHandle,
(ClUint8T *) &inArguments,
sizeof(inArguments));
if(CL_OK != rc)
{
clOsalPrintf("clBufferNBytesWrite() failed, rc[%#X]\n");
goto failure;
}
rc = clRmdWithMsg(destAddr, funcId, inMsgHandle,
outMsgHandle, flags, &rmdOptions,
NULL);
if (rc != CL_OK)
{
clOsalPrintf("clRmdWithMsg() failed, rc[%#X]\n");
goto failure;
}
clOsalPrintf("*******************************************************\n");
clOsalPrintf("***************** Sync RMD Succeeded ******************\n");
clOsalPrintf("*******************************************************\n");
// Nothing to free - inMsgHandle had to be freed here if
// CL_RMD_CALL_NON_PERSISTENT wasn't set. But that is discouraged
// unless inMsgHandle needs to be re-used which is rare.
failure:
return rc;
}

If the application is interested in the response from the server then it has to create a buffer - outMsgHandle to hold the data the server wants to send back. This buffer needs to be deleted soon after the call when it is no longer being used.

static ClRcT clRmdSyncWithReply(void)
{
ClRcT rc = CL_OK;
ClIocAddressT destAddr = {{0}};
// Remote Procedure to be invoked
ClUint32T funcId =
ClBufferHandleT inMsgHandle = 0;
ClBufferHandleT outMsgHandle = 0;
ClUint8T inArguments[] =
"The bytestream of arguments to be passed to dest "
"(marshalled if necessary to make endian neutral)";
ClUint8T *pReturnData = NULL;
// Update the destination address
// If destination(server) is on the same node
// Well known destionation(server) port
destAddr.iocPhyAddress.portId = 0x3100;
rc = clBufferCreate(&inMsgHandle);
if(CL_OK != rc)
{
clOsalPrintf("clBufferCreate() failed, rc[%#X]\n");
goto failure;
}
rc = clBufferCreate(&outMsgHandle);
if(CL_OK != rc)
{
clOsalPrintf("clBufferCreate() failed, rc[%#X]\n");
goto failure;
}
rc = clBufferNBytesWrite(inMsgHandle,
(ClUint8T *) &inArguments,
sizeof(inArguments));
if(CL_OK != rc)
{
clOsalPrintf("clBufferNBytesWrite() failed, rc[%#X]\n");
goto failure;
}
rc = clRmdWithMsg(destAddr, funcId, inMsgHandle,
outMsgHandle, flags, &rmdOptions,
NULL);
if (rc != CL_OK)
{
clOsalPrintf("clRmdWithMsg() failed, rc[%#X]\n");
goto failure;
}
rc = clBufferFlatten(outMsgHandle, &pReturnData);
if (CL_OK != rc)
{
clOsalPrintf("clBufferMessageNBytesRead() Failed, rc[%#X]\n", rc);
goto rmd_success;
}
clOsalPrintf("*******************************************************\n");
clOsalPrintf("***************** Sync RMD Succeeded ******************\n");
clOsalPrintf("*******************************************************\n");
clOsalPrintf(" Data Returned : [%s]\n", pReturnData);
clOsalPrintf("*******************************************************\n");
clHeapFree(pReturnData); // Free the memory allocated by
// clBufferFlatten()
rmd_success:
clBufferDelete(&outMsgHandle); // Free the out message buffer
// inMsgHandle had to be freed here if CL_RMD_CALL_NON_PERSISTENT
// wasn't set. But that is discouraged unless inMsgHandle needs to
// be re-used which is rare.
failure:
return rc;
}

When the application just wants to notify the server with some information without waiting for a reply then the following mode can be used.

static ClRcT clRmdAsyncWithoutReply(void)
{
ClRcT rc = CL_OK;
ClIocAddressT destAddr = {{0}};
// Remote Procedure to be invoked
ClUint32T funcId =
ClBufferHandleT inMsgHandle = 0;
ClBufferHandleT outMsgHandle = 0;
ClUint8T inArguments[] =
"The bytestream of arguments to be passed to dest "
"(marshalled if necessary to make endian neutral)";
// Update the destination address
// If destination(server) is on the same node
// Well known destionation(server) port
destAddr.iocPhyAddress.portId = 0x3100;
rc = clBufferCreate(&inMsgHandle);
if(CL_OK != rc)
{
clOsalPrintf("clBufferCreate() failed, rc[%#X]\n");
goto failure;
}
rc = clBufferNBytesWrite(inMsgHandle,
(ClUint8T *) &inArguments,
sizeof(inArguments));
if(CL_OK != rc)
{
clOsalPrintf("clBufferNBytesWrite() failed, rc[%#X]\n");
goto failure;
}
rc = clRmdWithMsg(destAddr, funcId, inMsgHandle,
outMsgHandle, flags, &rmdOptions,
NULL);
if (rc != CL_OK)
{
clOsalPrintf("clRmdWithMsg() failed, rc[%#X]\n");
goto failure;
}
// inMsgHandle had to be freed here if CL_RMD_CALL_NON_PERSISTENT
// wasn't set. But that is discouraged unless inMsgHandle needs
// to be re-used which is rare.
failure:
return rc;
}

The application may want to be notified about the success of an asynchronous rmd call with the response data from the server. In this a callback such as the one below is required when invoking the call:

void clRmdAsyncCallback(ClRcT rc, ClPtrT pCookie,
ClBufferHandleT inMsgHandle,
ClBufferHandleT outMsgHandle)
{
ClCharT *pCallbackArg = pCookie;
ClUint8T *pReturnData = NULL;
if (CL_OK != rc) // If the Async RMD failed
{
clOsalPrintf("Async RMD Failed, rc[%#X]\n", rc);
goto failure;
}
rc = clBufferFlatten(outMsgHandle, &pReturnData);
if (CL_OK != rc)
{
clOsalPrintf("clBufferMessageNBytesRead() Failed, rc[%#X]\n", rc);
goto failure;
}
clOsalPrintf("*******************************************************\n");
clOsalPrintf("***************** Async RMD Succeeded *****************\n");
clOsalPrintf("*******************************************************\n");
clOsalPrintf(" Data Returned : [%s]\n", pReturnData);
clOsalPrintf(" Cookie Supplied : [%s]\n", pCallbackArg);
clOsalPrintf("*******************************************************\n");
clHeapFree(pReturnData); // Free the memory allocated
// by clBufferFlatten()
failure:
clHeapFree(pCallbackArg); // Free the memory allocated for cookie
clBufferDelete(&outMsgHandle); // Free the out message buffer
// inMsgHandle had to be freed here if CL_RMD_CALL_NON_PERSISTENT
// wasn't set. But that is discouraged unless inMsgHandle needs to
// be re-used which is rare.
return;
}

To make the asyncrhonous call the application needs to specify the callback as illustrated below in the form of asyncRmdOptions. It can also supply some argument which will be supplied in the callback (cookie). In this case care has to be taken to release the outMsgHandle in the callback instead of after the RMD call unless there is an error condition. The return code in the above callback lets the application know if the RMD call was successful.

static ClRcT clRmdAsyncWithReply(void)
{
ClRcT rc = CL_OK;
ClIocAddressT destAddr = {{0}};
// Remote Procedure to be invoked
ClUint32T funcId =
ClBufferHandleT inMsgHandle = 0;
ClBufferHandleT outMsgHandle = 0;
ClUint32T flags = CL_RMD_CALL_ASYNC |
ClRmdAsyncOptionsT asyncRmdOptions = {0};
ClCharT cookie[] = "User Specified Argument for the async callback";
ClPtrT pCallbackArg = NULL;
ClUint8T inArguments[] =
"The bytestream of arguments to be passed to dest "
"(marshalled if necessary to make endian neutral)";
// Populate the argument to be passed to the Callback.
pCallbackArg = clHeapAllocate(sizeof(cookie));
if (NULL == pCallbackArg)
{
clOsalPrintf("Failed to allocate Memory\n");
goto failure;
}
memcpy(pCallbackArg, cookie, sizeof(cookie));
// Populate the Async RMD options.
asyncRmdOptions.fpCallback = clRmdAsyncCallback;
asyncRmdOptions.pCookie = pCallbackArg;
// Update the destination address
// If destination(server) is on the same node
// Well known destionation(server) port
destAddr.iocPhyAddress.portId = 0x3100;
rc = clBufferCreate(&inMsgHandle);
if(CL_OK != rc)
{
clOsalPrintf("clBufferCreate() failed, rc[%#X]\n");
goto failure;
}
rc = clBufferCreate(&outMsgHandle);
if(CL_OK != rc)
{
clOsalPrintf("clBufferCreate() failed, rc[%#X]\n");
goto failure;
}
rc = clBufferNBytesWrite(inMsgHandle,
(ClUint8T *) &inArguments,
sizeof(inArguments));
if(CL_OK != rc)
{
clOsalPrintf("clBufferNBytesWrite() failed, rc[%#X]\n");
goto failure;
}
rc = clRmdWithMsg(destAddr, funcId, inMsgHandle,
outMsgHandle, flags, &rmdOptions,
&asyncRmdOptions);
if (rc != CL_OK)
{
clOsalPrintf("clRmdWithMsg() failed, rc[%#X]\n");
goto failure;
}
failure:
return rc;
}

The above code only depicts the typical flags used. If you want to ensure that the invoked call is executed no more than once at the server you need to set the CL_RMD_CALL_ATMOST_ONCE flag. This flag is only necessary only for the calls that have a side effect if invoked more than once and are not idempotent in nature. The buffer is held at the server so this flag needs to be used judiciously.

The CL_RMD_CALL_NON_PERSISTENT is recommended as in most cases the application deletes the inMsgHandle as soon the call is invoked. Only if the application intends to re-use the inMsgHandle should it avoid this flag. In such a case the application is required to delete the inMsgHandle as indicated in the comments in the examples above.


Generated on Tue Jan 10 10:29:15 PST 2012 for OpenClovis SDK using Doxygen