The reason why WdfRequestUnmarkCancelable would return STATUS_CANCELLED is easy to understand - it does so if the request is being canceled when we call that function. In other words, it means, "it's too late to cancel it, the cancellation is already going."
But what happens internally when WdfRequestMarkCancelableEx returns STATUS_CANCELLED?
That function is supposed to set up cancellation, but how can a request be canceled if we haven't set up the cancellation yet?
The thread that initiated the request is terminating, for example. You are setting up your driver’s cancel callback routine, not setting up ‘cancellation for the request’.
Yes, it says that. An IO request can be explicitly cancelled, but it also can be implicitly canceled by, for example, thread termination. That is the most likely case.
A WDFREQUEST is tied to a thread if it represents a user mode IRP. It might also have a thread associated with it if a kernel mode component set it up that way. But I think you are not asking the right questions.
Your initial confusion was over why WdfRequestMarkCancelableEx could return STATUS_CANCELLED, and that can happen because your driver can be processing the request on one thread while the IRP, for whatever reason, is being cancelled on another thread.
If you create the request in your driver, if it is not being handed to you in one of the IO request processing event callbacks, WdfRequestMarkCancelableEx is not going to return STATUS_CANCELLED.
Thanks, @Mark_Roddy . I'm still somewhat confused though. Let me explain.
Yes, the underlying IRP initially comes from the user mode: via ReadFile*/WriteFile* APIs.
So my assumption was that before WdfRequestMarkCancelableEx returns STATUS_SUCCESS that that WDFREQUEST (and the underlying IRP) cannot be canceled. Thus, I can safely use it, or say, the buffer that I got from calling WdfRequestRetrieveOutputBuffer on it.
But then after WdfRequestMarkCancelableEx returns STATUS_SUCCESS I should stop accessing the associated WDFREQUEST and its buffers ... until I call WdfRequestUnmarkCancelable on it and have it return STATUS_SUCCESS. At which point the WDFREQUEST will be again impossible to cancel.
Am I correct with my understanding so far?
But if a user thread that the IRP belongs to can be terminated at any point, doesn't that create a race condition in the scenario that I described above?
Say, if I call WdfRequestUnmarkCancelable and it returned STATUS_SUCCESS. Then the user-mode thread that the request belongs to is terminated, but I continue using the buffer that I got from calling WdfRequestRetrieveOutputBuffer on that WDFREQUEST. How do I avoid that race condition then?
You’re conflating the act of canceling the request with completing the request. The request can be marked as canceled at any time. It is up to the driver which has queued the request to complete the canceled request at some point in time (ideally quickly). Until you call WdfRequestMarkCancelableEx the request and the buffers are valid because you have not yet completed the request. Once you call WdfRequestMarkCancelableEx you are essentially giving up direct control of the request lifetime until you unmark it. Once you unmark it, you re-assume direct control of the request lifetime.
Yes, thank you. And after I called WdfRequestUnmarkCancelable and it returned STATUS_SUCCESS, it is guaranteed that the EvtRequestCancel will not be invoked (unless I call WdfRequestMarkCancelableEx), correct?