Client-side Asynchronous Pipe Handling

Before making an asynchronous remote call, the client must first initialize the asynchronous handle. As with non-pipe procedures, the client calls an asynchronous function with the asynchronous handle as the first parameter and uses the asynchronous handle to send and receive pipe data, query the status of the call, and receive the reply.

Making Asynchronous Remote Procedure Calls

The client makes the asynchronous remote procedure call with the asynchronous handle as the first parameter. The client can use this handle to query the status of the call and to receive the reply. The asynchronous pipe model is symmetric. Both client and server applications send and receive pipe data actively (as opposed to synchronous RPC, where the pipe data is sent and received passively).

Sending Asynchronous Pipe Data

The client sends asynchronous pipe data by calling the push function on the appropriate asynchronous pipe, with the pipe's state variable as the first parameter. When the push function returns, the client can modify or free the send buffer.

If the RPC_ASYNC_NOTIFY_ON_SEND_COMPLETE flag is set in the asynchronous handle, and if APCs are used as the notification mechanism, an APC is queued when the pipe send is actually complete. You can take advantage of this mechanism to implement flow control. Note, however, that if the client pushes another buffer before the previous push is complete, the client may, depending on the speed of the transfer operation, receive only one send-complete notification, rather than one notification for each buffer or each push operation. When the client has sent all of the pipe data, it makes one final push call with the number of elements set to 0.

Receiving Asynchronous Pipe Data

The client receives asynchronous pipe data by calling the pull function on the appropriate asynchronous pipe, with the pipe's state variable as the first parameter. If no pipe data is available, the pull function returns RPC_S_ASYNC_CALL_PENDING.

If the notification mechanism is APC, and the server returned RPC_S_ASYNC_CALL_PENDING, the client must wait until it receives the RpcReceiveComplete APC from the runtime before calling pull again.

Waiting for reply

Same as the nonpipe case.

Example

// User defined data structure to keep track of the asynchronous call. 
typedef struct {
  RPC_ASYNC_STATE Async;
  ASYNC_INTPIPE inpipe ;
  ASYNC_INTPIPE outpipe ;
  int i;
  BOOL PipeDataSent;
  int PipeBuffer[ASYNC_CHUNK_SIZE] ;
} PIPE_CALL_COOKIE;
 
//call the asynchronous function
 
void AsyncPipesUsingAPC (RPC_BINDING_HANDLE Binding)
{
RPC_STATUS Status;
PIPE_CALL_COOKIE *Cookie;
int retval ;
int a = 10;
int b;
BOOL fDone;
Cookie = new PIPE_CALL_COOKIE;
 
RpcAsyncInitializeHandle (&(Cookie->Async));
Cookie->PipeDataSent = 0;
Cookie->i = 0;
Cookie->Async.Flags = RPC_C_NOTIFY_ON_SEND_COMPLETE;
Cookie->Async.NotificationType =RpcNotificationTypeApc;
Cookie->Async.u.APC.NotificationRoutine = MyAsyncPipeAPCRoutine;
Cookie->Async.u.APC.hThread = 0;
 
// The function calls itself, causing the nonpipe 
//params to be sent.
//The application can send pipe data immediately after returning 
//from this call or it can wait to receive the send complete
//notification before proceeding.
 
MyAsyncPipeFunc(&(Cookie->Async),Binding,a,
                &(Cookie->inpipe),&(Cookie->outpipe),&b);
 
//at this point the application can either send more pipe 
//data or do other work.
 
while (Cookie->PipeDataSent == 0)
{
if (SleepEx(INFINITE, TRUE) != WAIT_IO_COMPLETION)
  {
  RpcRaiseException(APP_ERROR) ;
  }
}
//
// Receiving pipe data:
//
while (!fDone)
  {
  Status = Cookie->outpipe.pull(Cookie->outpipe.state, 
                       (int *) Cookie->PipeBuffer, 
                        ASYNC_CHUNK_SIZE,
                        &num_elements);
switch (Status)
  {
  case RPC_S_ASYNC_CALL_PENDING:
     if (SleepEx(INFINITE, TRUE) != WAIT_IO_COMPLETION)
       {
       RpcRaiseException(APP_ERROR) ;
       }
     break;
  case RPC_S_OK:
     if (num_elements == 0)
       {
       fDone = 1;
       }
     else
       {
// Process the data here.
       }
     break;
  default:
     fDone = 1;
     break;
  }//end switch
//now call the runtime to complete the call:
RpcAsyncCompleteCall (&Cookie->Async, &retval) ;
}  //end while
}//end AsyncPipesUsingAPC
 
//
//sending pipe data
//
void MyAsyncPipeAPCRoutine (IN PRPC_ASYNC_STATE pAsync,
                            IN void *Context,
                            IN unsigned int Flags
                            )
{
PIPE_CALL_COOKIE *Cookie = (PIPE_CALL_COOKIE *) pAsync->UserInfo;
if (Flags & RPC_ASYNC_PIPE_SEND_COMPLETE)
  {
  if (Cookie->i <ASYNC_NUM_CHUNKS)
    {
    Cookie->i++ ;
    Cookie->inpipe.push(cookie->inpipe.state,
                        0,
                        (int *) Cookie->PipeBuffer,
                        ASYNC_CHUNK_SIZE);
    }
  else
    {
    pAsync->Flags = 0;
    Cookie->inpipe.push(cookie->inpipe.state, 0, 0, 0);
    }
  }
if (Flags & RPC_ASYNC_CALL_COMPLETE)
  {
  Cookie->PipeDataSent = 1;
  }
}//end MyAsyncPipeAPCRoutine
 

See Also

Pipes, Client-side Asynchronous RPC