Canceling Pending I/O Operations
Allowing users to cancel I/O requests that are slow or blocked can enhance the usability and robustness of your application. For example, if a call to the OpenFile() function is blocked because the call is to a very slow device, canceling it enables the call to be made again, with new parameters, without terminating the application. Windows Vista extends the cancellation capabilities and includes support for canceling synchronous operations. Take note that by calling the CancelIoEx() function does not guarantee that an I/O operation will be canceled; the driver which is handling the operation must support cancellation and the operation must be in a state that can be canceled.
Cancellation Considerations
When programming cancellation calls, keep in mind the following considerations:
Operations That Cannot Be Canceled
Some functions cannot be canceled using the CancelIo(), CancelIoEx(), or CancelSynchronousIo() function. Some of these functions have been extended to allow cancellation (for example, the CopyFileEx() function) and you should use these instead. In addition to supporting cancellation, these functions also have built-in callbacks to support you when tracking the progress of the operation. The following functions do not support cancellation:
Canceling Asynchronous I/O
You can cancel asynchronous I/O from any thread in the process that issued the I/O operation. You must specify the handle which the I/O was performed on and, optionally, the overlapped structure that was used to perform the I/O. You can determine if the cancel occurred by examining the status returned in the overlapped structure or in the completion callback. A status of ERROR_OPERATION_ABORTED indicates that the operation was canceled. The following code snippet example shows a routine that takes a timeout and attempts a read operation, canceling it with the CancelIoEx() function if the timeout expires.
#include <windows.h>
BOOL DoCancelableRead(HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped,
DWORD dwTimeout,
LPBOOL pbCancelCalled)
//
// Parameters:
// hFile - An open handle to a readable file or device.
// lpBuffer - A pointer to the buffer to store the data being read.
// nNumberOfBytesToRead - The number of bytes to read from the file or
// device. Must be less than or equal to the actual size of
// the buffer referenced by lpBuffer.
// lpNumberOfBytesRead - A pointer to a DWORD to receive the number
// of bytes read after all I/O is complete or cancelled.
// lpOverlapped - A pointer to a preconfigured OVERLAPPED structure that
// has a valid hEvent.
// If the caller does not properly initialize this structure, this
// routine will fail.
//
// dwTimeout - The desired time-out, in milliseconds, for the I/O read.
// After this time expires, the I/O is cancelled.
// pbCancelCalled - A pointer to a Boolean to notify the caller if this
// routine attempted to perform an I/O cancel.
// Return Value:
// TRUE on success, FALSE on failure.
{
BOOL result;
DWORD waitResult;
DWORD waitTimer;
BOOL bIoComplete = FALSE;
const DWORD PollInterval = 100; // milliseconds
// Initialize out parameters
*pbCancelCalled = FALSE;
*lpNumberOfBytesRead = 0;
// Perform the I/O, in this case a read operation.
result = ReadFile(hFile,
lpBuffer,
nNumberOfBytesToRead,
lpNumberOfBytesRead,
lpOverlapped );
if (result == FALSE)
{
if (GetLastError() != ERROR_IO_PENDING)
{
// The function call failed. ToDo: Error logging and recovery.
return FALSE;
}
}
else
{
// The I/O completed, done.
return TRUE;
}
// The I/O is pending, so wait and see if the call times out.
// If so, cancel the I/O using the CancelIoEx function.
for (waitTimer = 0; waitTimer < dwTimeout; waitTimer += PollInterval)
{
result = GetOverlappedResult( hFile, lpOverlapped, lpNumberOfBytesRead, FALSE );
if (result == FALSE)
{
if (GetLastError() != ERROR_IO_PENDING)
{
// The function call failed. TODO: Error logging and recovery.
return FALSE;
}
Sleep(PollInterval);
}
else
{
bIoComplete = TRUE;
break;
}
}
if (bIoComplete == FALSE)
{
result = CancelIoEx( hFile, lpOverlapped );
*pbCancelCalled = TRUE;
if (result == TRUE || GetLastError() != ERROR_NOT_FOUND)
{
// Wait for the I/O subsystem to acknowledge our cancellation.
// Depending on the timing of the calls, the I/O might complete with a
// cancellation status, or it might complete normally (if the ReadFile was
// in the process of completing at the time CancelIoEx was called, or if
// the device does not support cancellation).
// This call specifies TRUE for the bWait parameter, which will block
// until the I/O either completes or is cancelled, thus resuming execution,
// provided the underlying device driver and associated hardware are functioning
// properly. If there is a problem with the driver it is better to stop
// responding, or hang, here than to try to continue while masking the problem.
result = GetOverlappedResult( hFile, lpOverlapped, lpNumberOfBytesRead, TRUE );
// TODO: check result and log errors.
}
}
return result;
}