Using Asynchronous I/O Processing in ISAPI Extensions

Asynchronous I/O operations make the most efficient use of the CPU time available to IIS. I/O operations often block threads, therefore asynchronous operations are the most appropriate choice for both reading and writing because they can occur simultaneously. Starting with IIS 5.0, key technologies such as I/O completion ports and robust thread pools provide a wide range of asynchronous I/O support.

In general, all asynchronous operations in IIS behave similarly. First, the ISAPI extension designates an asynchronous callback function, and then calls the appropriate asynchronous read or write function provided by IIS. When IIS completes the operation, it calls the asynchronous callback function to allow the extension to perform further processing or cleanup.

Reading and Writing Asynchronously

Only one asynchronous operation can be pending for each ISAPI request at any given time. I/O completion notifications only contain information about the request, the number of bytes in the completion, and the error code. If, for example, an asynchronous read and an asynchronous write are run at the same time, the I/O completion notifications cannot be differentiated. The request is allowed only one context at a time, and the completion information for the slower operation can overwrite the completion information for the faster one.

To perform an asynchronous read

  1. Set the special asynchronous callback function by calling the ServerSupportFunction function with the HSE_REQ_IO_COMPLETION parameter.

  2. Request that IIS begin the asynchronous read operation by calling the HSE_REQ_ASYNC_READ_CLIENT support function.

  3. Terminate the HttpExtensionProc function and indicate that the extension is waiting for further callbacks by returning the status code HSE_STATUS_PENDING.

  4. When IIS completes the read, or generates an error trying to read, it calls the asynchronous callback function specified in step 1. The extension can now perform any processing on the newly read data, and return to step 2 if more data is available to send.

  5. When no more data is available, and the extension has completed any cleanup necessary for the current request, it should inform IIS that it is done with the request by using the HSE_REQ_DONE_WITH_SESSION support function.

If an ISAPI extension issues a request for an asynchronous read, and it is determined that the request has timed out, the request can be canceled and the connection closed by using the HSE_REQ_CLOSE_CONNECTION support function. However, IIS must call the asynchronous callback function before the connection is considered closed.

ISAPI extensions have two options for performing asynchronous write operations. One option uses an asynchronous WriteClient function, and the other uses the functionality provided by the Win32 API TransmitFile function through the HSE_REQ_TRANSMIT_FILE support function.

Note

In most cases, using the ServerSupportFunction based on TransmitFile yields the highest performance. Both options are included in the following procedure.

To perform an asynchronous write

  1. Provide IIS with a pointer to the asynchronous callback function. If the WriteClient option is being used, this is accomplished with the HSE_REQ_IO_COMPLETION support function. If the TransmitFile option is being used, set the pfnHseIO member of the HSE_TF_INFO structure to point to the asynchronous callback function.

  2. Request that IIS begin the asynchronous write operation, calling either WriteClient (with the dwSync parameter set to HSE_IO_ASYNC) or the HSE_REQ_TRANSMIT_FILE support function.

  3. Terminate the HSE_REQ_TRANSMIT_FILE function, and indicate that the extension is waiting for further callbacks by returning the status code HSE_STATUS_PENDING.

  4. When IIS completes the write, or generates an error trying to write, it calls the asynchronous callback function specified in step 1. The extension can now perform any further processing, and return to step 2 if more data is available to send.

  5. When no more data is available, and the extension has completed any cleanup necessary for the current request, it should inform IIS that it is done with the request by using the HSE_REQ_DONE_WITH_SESSION support function.

The following example shows you how to use the C++ programming language to initiate an asynchronous read from a client. For a set of complete code examples that read and write synchronously and asynchronously, see the "IO" sample files that are included with the IIS section of the Platform SDK, or can be downloaded from Platform SDK Update.

DWORD DoAsyncRC(EXTENSION_CONTROL_BLOCK *pECB) 
{  
  char szHeader[256] = ""; 
  BOOL fReturn = TRUE; 
  DWORD dwFlags;  
  DWORD cbTotalToRead = MAX_BUF_SIZE; 
  DWORD hseStatus = HSE_STATUS_PENDING; // Initialize the context for ReadClient. 

  pByteReadSoFar = &(pECB->cbAvailable); 

  // Specify the asynchronous callback function. 

  fReturn = pECB->ServerSupportFunction( 
      pECB->ConnID, 
      HSE_REQ_IO_COMPLETION, 
      AsyncReadClientIoCompletion, 
      0, 
      pByteReadSoFar); 

  if (!fReturn) 
    hseStatus = HSE_STATUS_ERROR; 

  //  Request that IIS begin asynchronous read operation. 

  dwFlags = HSE_IO_ASYNC; 

  fReturn = pECB->ServerSupportFunction( 
      pECB->ConnID, 
      HSE_REQ_ASYNC_READ_CLIENT, 
      g_ReadBuffer, 
      &cbTotalToRead, 
      &dwFlags); 

  if (!fReturn) 
    hseStatus = HSE_STATUS_ERROR; 

  // Return HSE_STATUS_PENDING, unless an error occurred. 

  return (hseStatus); 
} 

Nagling

The TCP protocol over which HTTP operates has an optimization that increases efficiency by trying to minimize the number of packets needed to send data. It works by waiting to send a packet until its data area is full, or until a 200-millisecond timeout period expires, or until the sender indicates that it is done sending data. This optimization is called nagling, and IIS versions 5.1 and earlier use it for all data sent to the client.

Nagling has a possible downside. If an extension does not fill up the packet, there is still a delay of 200 milliseconds before the response is sent. This behavior is most evident for ISAPI extensions that support Keep-Alive in the response to the client. In this case, IIS does not close the connection after the response, so the final packet ends up waiting for the 200 milliseconds.

Note

An ISAPI extension supports Keep-Alive with the client by including a content-length header in the response.

Nagling still occurs when Keep-Alive is not supported, but it is not as evident because IIS closes the connection when the ISAPI extension is done with the request. In this case, if an ISAPI extension takes some time between its last send and its completion, nagling is noticeable.

To disable nagling for the WriteClient function call, use the HSE_IO_NODELAY flag. If this flag is specified on a call to WriteClient, or included in the dwFlags member of an HSE_TF_INFO structure passed to the HSE_REQ_TRANSMIT_FILE callback function, nagling is disabled for that transmission. In IIS 6.0, nagling is controlled by HTTP.sys, and HSE_IO_NODELAY is ignored.