The Windows File Management 15

 

 

 

 

 

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:

  1. There is no guarantee that underlying drivers correctly support cancellation.
  2. When canceling asynchronous I/O, when no overlapped structure is supplied to the CancelIoEx() function, the function attempts to cancel all outstanding I/O on the file on all threads in the process. Each thread is processed individually, so after a thread has been processed it may start another I/O on the file before all the other threads have had their I/O for the file canceled, causing synchronization issues.
  3. When canceling asynchronous I/O, do not reuse overlapped structures with targeted cancellation. Once the I/O operation completes (either successfully or with a canceled status) then the overlapped structure is no longer in use by the system and can be reused.
  4. When canceling synchronous I/O, calling the CancelSynchronousIo() function attempts to cancel any current synchronous call on the thread. You must take care to ensure the synchronization of the calls is correct; the wrong call in a series of calls could get canceled. For example, if the CancelSynchronousIo() function is called for a synchronous operation, X, operation Y only starts after that operation X is completed (normally or with an error). If the thread that called operation X then starts another synchronous call to X, the cancel call could interrupt this new I/O request.
  5. When canceling synchronous I/O, be aware that a race condition can exist whenever a thread is shared between different parts of an application, for example, with a thread-pool thread.

 

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:

  1. CopyFile() - use CopyFileEx()
  2. MoveFile() - use MoveFileWithProgress()
  3. MoveFileEx() - use MoveFileWithProgress()
  4. ReplaceFile()

 

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;

}

 

 

 

 

< Windows Files 14 | Win32 Programming | Win32 File Index | Windows Files 16 >