Canceling I/O Requests
[This topic applies to UMDF 1.x.]
A device's in-progress I/O operation (such as a request to read several blocks from a disk) can be canceled by an application, the system, or a driver. If a device's I/O operation is canceled, the I/O Manager attempts to cancel all unprocessed I/O requests that are associated with the I/O operation. The device's drivers can register to be notified when the I/O Manager attempts to cancel I/O requests, and the drivers can cancel the requests that they own by completing them with a completion status of HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED).
The framework handles some of the cancellation work for framework-based drivers. If a device's I/O operation is canceled, the framework completes--with a completion status of HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)--the following I/O requests that are associated with the canceled operation:
Undelivered I/O requests that the framework has placed in the driver's default I/O queue.
Undelivered I/O requests that the framework has forwarded to another queue because the driver called IWDFIoQueue::ConfigureRequestDispatching.
Because the framework cancels these requests, it does not deliver them to the driver.
After the framework has delivered an I/O request to the driver, the driver owns the request and the framework cannot cancel it. At this point, only the driver can cancel the I/O request, but the framework must notify the driver that a request should be canceled. Drivers receive this notification by providing an IRequestCallbackCancel::OnCancel callback function.
Sometimes a driver receives an I/O request from an I/O queue but, instead of processing the request, the driver requeues the request to the same or another I/O queue for later processing. For example, the framework might deliver an I/O request to one of the driver's request handlers, and the driver might subsequently call either IWDFIoRequest::ForwardToIoQueue to place the request in a different queue or IWDFIoRequest2::Requeue to place the request back into the same queue.
In these cases, the framework can cancel the I/O request because the request is in an I/O queue. However, if the driver has registered an callback function for the I/O queue in which the request resides, the framework calls the callback function, instead of canceling the request, when the associated I/O operation is being canceled. If the framework calls the driver's callback function, the driver must cancel the request.
In summary, when an I/O operation is canceled, the framework always cancels all associated I/O requests that were never delivered to the driver. If the driver receives a request and then requeues it, the framework will cancel the request (if the request is in the queue) unless the driver provides an callback function for the I/O queue.
A driver can call IWDFIoRequest::MarkCancelable to register an IRequestCallbackCancel::OnCancel callback function. If the driver has called MarkCancelable, and if the I/O operation associated with the request is canceled, the framework calls the driver's OnCancel callback function so that the driver can cancel the I/O request.
A driver should call MarkCancelable if it will own a request for a relatively long time. For example, a driver might have to wait for a device to respond, or it might have to wait for lower drivers to complete a set of requests that the driver created when it received a single request.
If a driver does not call MarkCancelable, or if a driver calls IWDFIoRequest::UnmarkCancelable after calling MarkCancelable, the driver is not aware of the cancellation and therefore handles the request as it typically would.
If a driver has not called MarkCancelable to register an OnCancel callback function, it can call IWDFIoRequest2::IsCanceled to determine if the I/O Manager has attempted to cancel an I/O request. If IsCanceled returns TRUE, the driver should cancel the request.
For example, a driver that receives a large read or write request that it breaks into several smaller requests might call IsCanceled after the driver's I/O target completes each of the smaller requests, if the driver has not called MarkCancelable for the received request.
Canceling an I/O request might involve any of the following:
Stopping an in-progress I/O operation.
Not forwarding the request to an I/O target.
Calling IWDFIoRequest::CancelSentRequest to attempt to cancel a request that the driver had previously submitted to an I/O target.
If a driver is canceling an I/O request for a request object that the driver received from the framework, the driver must always complete the request by calling IWDFIoRequest::Complete or IWDFIoRequest::CompleteWithInformation, with a CompletionStatus parameter of HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED). (If the driver called IWDFDevice::CreateRequest to create a request object, the driver calls IWDFObject::DeleteWdfObject instead of completing the request.)