Client-side Asynchronous RPC

Client-side asynchronous function handling has three components:

Making the Asynchronous Call

The client must initialize the asynchronous handle, which is a pointer to the RPC_ASYNC_STATE structure, before it can make an asynchronous remote call. Every outstanding call must have its own unique asynchronous handle. After making the call, the client uses the asynchronous handle to query the status of the call, wait on the call, and to receive the reply.

The client must also ensure that the buffers it supplies for the out parameters and the in, out parameters remain allocated until the remote procedure call is logically completed, that is, when it has received the reply from the server.

Waiting For the Reply

The client waits to be notified of a reply from the server. The client can receive this notification in any one of the following ways:

The client can, at any time, request cancellation of an outstanding call by calling RpcAsyncCancelCall.

Receiving The Reply

Once it is notified that the server has sent a reply, the client calls RpcAsyncCompleteCall with the asynchronous handle to receive the reply. When RpcAsyncCompleteCall has completed successfully, *Reply contains the return value of the remote function, and the buffers supplied by the user for the out and in, out parameters of that remote function are valid. If the client calls RpcAsyncCompleteCall before the server has sent the reply, that call will fail with RPC_S_ASYNC_CALL_PENDING.

Example

This example shows a client-side asynchronous call that employs a user-mode asynchronous procedure call (APC) for the notification mechanism. Scroll down to see an example of the same call using notification by event.

//CALL_COOKIE is a user-defined object that keeps
//track of the asynchronous call. It includes the 
//RPC_ASYNC_STATE structure
typedef struct {
    RPC_ASYNC_STATE Async ;
    int c ;
    BOOL CallFinished ;
    } CALL_COOKIE ;
 
#define APP_ERROR -1
 
//This function calls the asynchronous remotefunction, MyAsyncFunc
void AsyncUsingAPC( RPC_BINDING_HANDLE Binding )
{
CALL_COOKIE *Cookie ;
int retval ;
 
  Cookie =new CALL_COOKIE ;
  Cookie->CallFinished = 0;
 
//let the runtime initialize the RPC_ASYNC_STATE struct:
  RpcAsyncInitializeHandle(&(Cookie->Async));
 
//now set the remaining RPC_ASYNC_STATE fields:
  Cookie->Async.NotificationType = notification_type_apc ;
  Cookie->Async.u.APC.NotificationRoutine = MyAsyncFuncAPCRoutine ;
  Cookie->Async.u.APC.hThread = 0;
  Cookie->Async.UserInfo = (void *) Cookie ;
  
 
//call the asynchronous function. The buffers supplied for the [out] 
//and [in, out] parameters should be valid until the logical RPC 
//has completed.
  MyAsyncFunc(&Cookie->Async, Binding, 10, 20, &Cookie->c) 
 
//The client application can do other work here, while the 
//call is in progress.
//SleepEx causes the thread to enter a wait state until an APC is
//queued to the thread. The fAlertable flag must be set to TRUE
  if (SleepEx(INFINITE, TRUE) != WAIT_IO_COMPLETION
      || Cookie->CallFinished == 0)
      {
      RpcRaiseException(APP_ERROR) ;
      }
  RpcAsyncCompleteCall (&Cookie->Async, &retval) ;
//retval now contains the return value for MyAsyncFunc  
}     //end AsyncUsingAPC
 
// make the asynchronous function call
// the buffers supplied for the [out] and the [in, out] params
// should be valid until the logical RPC call has completed.
 
 
//This routine is called when an asynchronous call completes. 
//For an APC to be dispatched, the client thread must be waiting
//in an alertable state. 
void MyAsyncFuncAPCRoutine ( IN PRPC_ASYNC_STATE pAsync,
                             IN void *Context,
                             IN unsigned int Flags )
{
CALL_COOKIE *Cookie = (CALL_COOKIE *) pAsync->UserInfo ;
if (Flags & RPC_ASYNC_CALL_COMPLETE)
   {
   Cookie->CallFinished = 1;
   }
}  end MyAsyncFuncAPCRoutine
//End APC example.
 

Example 2

In this example the client uses an event as the notification mechanism.

//XAsyncCl.c (fragment)
 
void AsyncUsingEvent( IN RPC_BINDING_HANDLE Binding )
//This function calls MyAsyncFunc, the actual asynchronous function.
{
HANDLE HandleToThread ;
CALL_COOKIE *Cookie ; //as defined in first example
Cookie = new CALL_COOKIE ;
int a = 10, b = 20;
Cookie->Async.Size = sizeof(RPC_ASYNC_STATE) ;
Cookie->Async.Flags = 0;
Cookie->Async.u.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
if (Cookie->Async.u.hEvent == 0)
   {
   RpcRaiseException(APP_ERROR) ;
   }
Cookie->Async.NotificationType = notification_type_event ;
Cookie->Async.UserInfo = (void *) Cookie ;
// call the asynchronous function
// the buffers supplied for the [out] and the [in, out] params
// should be valid until the logical RPC call has completed. 
MyAsyncFunc(&Cookie->Async, Binding, a, b, &Cookie->c) ;
 
WaitForSingleObject(Cookie->Async.u.hEvent, INFINITE) ;
RpcAsyncCompleteCall (&Cookie->Async, &retval) ;
}    //end AsyncUsingEvent