[WDF] Add Windows Driver Framework files
[reactos.git] / sdk / lib / drivers / wdf / shared / core / fxirpqueue.cpp
diff --git a/sdk/lib/drivers/wdf/shared/core/fxirpqueue.cpp b/sdk/lib/drivers/wdf/shared/core/fxirpqueue.cpp
new file mode 100644 (file)
index 0000000..26baa1d
--- /dev/null
@@ -0,0 +1,964 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+    FxIrpQueue.cpp
+
+Abstract:
+
+    This module implements a common queue structure for the
+    driver frameworks built around the Cancel Safe Queue model
+
+Author:
+
+
+
+
+
+
+
+Environment:
+
+    Both kernel and user mode
+
+Revision History:
+
+
+--*/
+
+#include "coreprivshared.hpp"
+
+// Tracing support
+extern "C" {
+#include "FxIrpQueue.tmh"
+}
+
+//
+// Public constructors
+//
+
+FxIrpQueue::FxIrpQueue(
+    VOID
+    )
+{
+    InitializeListHead(&m_Queue);
+
+    m_LockObject     = NULL;
+
+    m_CancelCallback = NULL;
+
+    m_RequestCount   = 0;
+}
+
+FxIrpQueue::~FxIrpQueue()
+{
+    ASSERT(IsListEmpty(&m_Queue));
+}
+
+VOID
+FxIrpQueue::Initialize(
+    __in FxNonPagedObject* LockObject,
+    __in PFN_IRP_QUEUE_CANCEL Callback
+    )
+
+/*++
+
+Routine Description:
+
+    Initialize the FxIrpQueue.
+
+    Set the callback for when an IRP gets cancelled.
+
+    The callback function is only called when an IRP
+    gets cancelled, and is called with no locks held.
+
+    The cancel function that the caller registers is
+    responsible for completing the IRP with IoCompleteRequest.
+
+    If no Cancel Callback is set, or is set to NULL, IRP's will
+    be automatically completed with STATUS_CANCELED by
+    the FxIrpQueue when they are canceled.
+
+
+    If the caller supplies a LockObject, this object is used
+    to synchronize cancellation access to the list, but it is
+    expected that the caller be holding the same lock for insert/remove
+    operations. This allows the caller to perform insert/remove operations
+    using its own lock in a race free manner.
+
+    If a LockObject is not supplied, the FxIrpQueue uses its own lock.
+
+Arguments:
+
+    LockObject - Object whose lock controls the queue
+
+    Callback - Driver callback function
+
+Returns:
+
+    None
+
+--*/
+
+{
+    ASSERT(LockObject != NULL);
+
+    m_CancelCallback = Callback;
+    m_LockObject = LockObject;
+}
+
+\f
+_Must_inspect_result_
+NTSTATUS
+FxIrpQueue::InsertTailRequest(
+    __inout MdIrp Irp,
+    __in_opt PMdIoCsqIrpContext Context,
+    __out_opt ULONG* pRequestCount
+    )
+
+/*++
+
+Routine Description:
+
+    Enqueue a request to the end of the queue (FIFO) and
+    marks it as pending.
+
+    The PMdIoCsqIrpContext is associated with the IRP.
+
+    PIO_CSQ_IRP_CONTEXT must be in non-paged pool, and can
+    not be released until the IRP is is finally released from
+    the queue.
+
+Arguments:
+
+    Irp - Pointer to IRP
+
+    Context - Pointer to caller allocated CSQ context
+
+    pRequestCount - Location to return new request count of queue
+                    after insertion
+
+Returns:
+
+    STATUS_SUCCESS - Operation completed.
+
+    STATUS_CANCELLED - Request was cancelled, and not inserted
+                       Call is responsible for completing it.
+--*/
+
+{
+    NTSTATUS Status;
+
+    // Note: This marks the IRP Pending
+    Status = InsertIrpInQueue(
+                 Irp,      // Irp to insert
+                 Context,  // PIO_CSQ_IRP_CONTEXT
+                 FALSE,    // InsertInHead
+                 pRequestCount
+                 );
+
+    return Status;
+}
+
+\f
+_Must_inspect_result_
+NTSTATUS
+FxIrpQueue::InsertHeadRequest(
+    __inout MdIrp Irp,
+    __in_opt PMdIoCsqIrpContext Context,
+    __out_opt ULONG* pRequestCount
+    )
+
+/*++
+
+Routine Description:
+
+    Enqueue a request to the head of the queue and
+    marks it as pending.
+
+    The PIO_CSQ_IRP_CONTEXT is associated with the IRP.
+
+    PIO_CSQ_IRP_CONTEXT must be in non-paged pool, and can
+    not be released until the IRP is is finally released from
+    the queue.
+
+Arguments:
+
+    Irp - Pointer to IRP
+
+    Context - Pointer to caller allocated CSQ context
+
+    pRequestCount - Location to return new request count of queue
+                    after insertion
+Returns:
+
+   STATUS_SUCCESS - Operation completed.
+
+   STATUS_CANCELLED - Request was cancelled, and not inserted
+                      Call is responsible for completing it.
+--*/
+
+{
+    NTSTATUS Status;
+
+    // Note: This marks the IRP Pending
+    Status = InsertIrpInQueue(
+                 Irp,      // Irp to insert
+                 Context,  // PIO_CSQ_IRP_CONTEXT
+                 TRUE,     // InsertInHead
+                 pRequestCount
+                 );
+
+    return Status;
+}
+
+\f
+MdIrp
+FxIrpQueue::GetNextRequest(
+    __out PMdIoCsqIrpContext* pCsqContext
+    )
+
+/*++
+
+Routine Description:
+
+    Returns an IRP from the queue, and if successful
+    the IRP is no longer on the CSQ (m_Queue) and
+    is not non-cancellable.
+
+--*/
+
+{
+    return RemoveNextIrpFromQueue(NULL, pCsqContext);
+}
+
+\f
+_Must_inspect_result_
+NTSTATUS
+FxIrpQueue::GetNextRequest(
+    __in_opt PMdIoCsqIrpContext TagContext,
+    __in_opt  MdFileObject         FileObject,
+    __out FxRequest**          ppOutRequest
+    )
+
+/*++
+
+Routine Description:
+
+    Returns a request from the queue using an optional
+    FileObject or TagContext.
+
+--*/
+
+{
+    MdIrp Irp;
+    FxRequest* pRequest;
+    PMdIoCsqIrpContext pCsqContext;
+
+    if( TagContext == NULL ) {
+
+        //
+        // Returns an IRP from the queue, and if successful
+        // the IRP is no longer on the CSQ (m_Queue) and
+        // is not non-cancellable.
+        //
+        Irp = RemoveNextIrpFromQueue(
+                  FileObject,        // PeekContext
+                  &pCsqContext
+                  );
+
+        if( Irp != NULL ) {
+
+            pRequest = FxRequest::RetrieveFromCsqContext(pCsqContext);
+
+            *ppOutRequest = pRequest;
+
+            return STATUS_SUCCESS;
+        }
+        else {
+            return STATUS_NO_MORE_ENTRIES;
+        }
+    }
+    else {
+
+        // Handle TagRequest Case
+        Irp = RemoveIrpFromQueueByContext(
+                  TagContext
+                  );
+
+        if( Irp != NULL ) {
+            pRequest = FxRequest::RetrieveFromCsqContext(TagContext);
+
+            *ppOutRequest = pRequest;
+
+            return STATUS_SUCCESS;
+        }
+        else {
+            return STATUS_NOT_FOUND;
+        }
+    }
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxIrpQueue::PeekRequest(
+    __in_opt  PMdIoCsqIrpContext TagContext,
+    __in_opt  MdFileObject         FileObject,
+    __out FxRequest**          ppOutRequest
+    )
+
+/*++
+
+Routine Description:
+
+    PeekRequest allows a caller to enumerate through requests in
+    a queue, optionally only returning requests that match a specified
+    FileObject.
+
+    The first call specifies TagContext == NULL, and the first request
+    in the queue that matches the FileObject is returned.
+
+    Subsequent requests specify the previous request value as the
+    TagContext, and searching will continue at the request that follows.
+
+    If the queue is empty, there are no requests after TagContext, or no
+    requests match the FileObject, NULL is returned.
+
+    If FileObject == NULL, this matches any FileObject in a request.
+
+    If a WDF_REQUEST_PARAMETERS structure is supplied, the information
+    from the request is returned to allow the driver to further examine
+    the request to decide whether to service it.
+
+    If a TagRequest is specified, and it is not found, the return
+    status STATUS_NOT_FOUND means that the queue should
+    be re-scanned. This is because the TagRequest was cancelled from
+    the queue, or if the queue was active, delivered to the driver.
+    There may still be un-examined requests on the queue that match
+    the drivers search criteria, but the search marker has been lost.
+
+    Re-scanning the queue starting with TagRequest == NULL and
+    continuing until STATUS_NO_MORE_ENTRIES is returned will ensure
+    all requests have been examined.
+
+    Enumerating an active queue with this API could result in the
+    driver frequently having to re-scan.
+
+    If a successful return of a Request object handle occurs, the driver
+    *must* call WdfObjectDereference when done with it.
+
+    NOTE: Synchronization Details
+
+    The driver is allowed to "peek" at requests that are still on
+    the Cancel Safe Queue without removing them. This means that
+    the peek context value used (TagRequest) could go away while
+    still holding it. This does not seem bad in itself, but the request
+    could immediately be re-used by a look aside list and be re-submitted
+    to the queue. At this point, the "tag" value means a completely different
+    request.
+
+    This race is dealt with by reference counting the FxRequest object
+    that contains our PIO_CSQ_IRP_CONTEXT, so its memory remains valid
+    after a cancel, and the driver explicitly releases it with
+    WdfObjectDereference.
+
+    But if this reference is not added under the CSQ's spinlock, there
+    could be a race in which the I/O gets cancelled and the cancel
+    callback completes the request, before we add our reference count.
+    This would then result in attempting to reference count invalid
+    memory. So to close this race, this routine returns the referenced
+    FxRequest object as its result.
+
+Arguments:
+
+    TagRequest  - If !NULL, request to begin search at
+
+    FileObject  - If !NULL, FileObject to match in the request
+
+
+Returns:
+
+    STATUS_NOT_FOUND - TagContext was specified, but not
+                                found in the queue. This could be
+                                because the request was cancelled,
+                                or is part of an active queue and
+                                the request was passed to the driver
+                                or forwarded to another queue.
+
+    STATUS_NO_MORE_ENTRYS -     The queue is empty, or no more requests
+                                match the selection criteria of TagRequest
+                                and FileObject specified above.
+
+    STATUS_SUCCESS        -     A request context was returned in
+                                pOutRequest.
+
+--*/
+
+{
+    PLIST_ENTRY         nextEntry;
+    FxIrp               nextIrp(NULL);
+    PMdIoCsqIrpContext  pCsqContext;
+    BOOLEAN FoundTag =  (TagContext == NULL) ? TRUE : FALSE;
+    FxRequest*          pRequest;
+
+    for( nextEntry = m_Queue.Flink; nextEntry != &this->m_Queue; nextEntry = nextEntry->Flink) {
+
+        nextIrp.SetIrp(FxIrp::GetIrpFromListEntry(nextEntry));
+
+        if(nextIrp.IsCanceled()) {
+            //
+            // This IRP is cancelled and the WdmCancelRoutine is about to run or waiting
+            // for us to drop the lock. So skip this one.
+            //
+            continue;
+        }
+
+        pCsqContext = (PMdIoCsqIrpContext)nextIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
+
+        if( FoundTag ) {
+
+            if( FileObject != NULL ) {
+
+                if(nextIrp.GetFileObject() == FileObject ) {
+
+                    pRequest = FxRequest::RetrieveFromCsqContext(pCsqContext);
+
+                    //
+                    // Must add the reference here under the protection
+                    // of the cancel safe queues spinlock
+                    //
+                    pRequest->ADDREF(NULL);
+
+                    *ppOutRequest = pRequest;
+
+                    return STATUS_SUCCESS;
+                }
+            }
+            else {
+
+                pRequest = FxRequest::RetrieveFromCsqContext(pCsqContext);
+
+                //
+                // Must add the reference here under the protection
+                // of the cancel safe queues spinlock
+                //
+                pRequest->ADDREF(NULL);
+
+                *ppOutRequest = pRequest;
+
+                return STATUS_SUCCESS;
+            }
+        }
+        else {
+
+            // If we found the tag, we want the *next* entry
+            if( pCsqContext == TagContext ) {
+                FoundTag = TRUE;
+            }
+        }
+
+    }
+
+    //
+    // If the caller supplied a tag, and it was
+    // not found, return a different code since
+    // the caller needs to re-scan the queue.
+    //
+    if( (TagContext != NULL) && !FoundTag ) {
+        return STATUS_NOT_FOUND;
+    }
+    else {
+        return STATUS_NO_MORE_ENTRIES;
+    }
+}
+
+MdIrp
+FxIrpQueue::RemoveRequest(
+    __in PMdIoCsqIrpContext Context
+    )
+/*++
+
+Routine Description:
+
+    Returns a request from the queue.
+
+--*/
+{
+    MdIrp Irp;
+
+    ASSERT(Context != NULL);
+
+    //
+    // Returns an IRP from the queue, and if success
+    // the IRP is no longer on the CSQ (m_Queue) and
+    // is not non-cancellable.
+    //
+    Irp = RemoveIrpFromQueueByContext(Context);
+
+    return Irp;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxIrpQueue::InsertIrpInQueue(
+    __inout   MdIrp                 Irp,
+    __in_opt  PMdIoCsqIrpContext    Context,
+    __in      BOOLEAN               InsertInHead,
+    __out_opt ULONG*                pRequestCount
+    )
+/*++
+
+Routine Description:
+
+    Insert the IRP in the queue. If the IRP is already cancelled
+    it removes the IRP from the queue and returns STATUS_CANCELLED.
+
+    If the IRP is cancelled and the CancelRoutine has already started
+    execution, then this function returns STATUS_SUCCESS.
+
+Arguments:
+
+    Irp - Pointer to IRP
+
+    Context - Pointer to caller allocated CSQ context
+
+    InsertInHead - TRUE for head, FALSE for tail.
+
+    pRequestCount - Location to return new request count of queue
+                    after insertion
+
+Returns:
+
+    STATUS_SUCCESS - Operation completed.
+
+    STATUS_CANCELLED - if the request is already cancelled.
+
+--*/
+{
+    FxIrp irp(Irp);
+    MdCancelRoutine cancelRoutine;
+    NTSTATUS        status = STATUS_SUCCESS;
+
+    //
+    // Set the association between the context and the IRP.
+    //
+
+    if (Context) {
+        irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, Context);
+        Context->Irp = Irp;
+        Context->Csq = (PIO_CSQ)this;
+
+
+
+
+
+
+
+
+
+
+        Context->Type = FX_IRP_QUEUE_ENTRY_IDENTIFIER;
+    } else {
+
+        //
+        // Currently always require context, but this will change when we
+        // allow queuing or low level IRP's without FxRequest headers allocated
+        //
+        ASSERT(FALSE);
+
+        irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, this);
+    }
+
+    // See if it's a head insertion
+    if( InsertInHead ) {
+        InsertHeadList(
+            &m_Queue,
+            irp.ListEntry()
+            );
+    }
+    else {
+        InsertTailList(
+            &m_Queue,
+            irp.ListEntry()
+            );
+    }
+
+    m_RequestCount++;
+
+    if( pRequestCount != NULL ) {
+        *pRequestCount = m_RequestCount;
+    }
+
+    irp.MarkIrpPending();
+
+    cancelRoutine = irp.SetCancelRoutine(_WdmCancelRoutineInternal);
+
+    ASSERT(!cancelRoutine);
+    UNREFERENCED_PARAMETER(cancelRoutine);
+
+    if (irp.IsCanceled()) {
+
+        cancelRoutine = irp.SetCancelRoutine(NULL);
+
+        if (cancelRoutine) {
+
+            // Remove the IRP from the list
+            RemoveIrpFromListEntry(&irp);
+
+            if (Context) {
+                Context->Irp = NULL;
+            }
+
+            irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
+
+            //
+            // Caller does not want us to recurse on their lock by invoking
+            // the m_CancelCallback on the insert path. So they will complete
+            // the IRP themselves.
+            //
+            return STATUS_CANCELLED;
+        } else {
+
+            //
+            // The cancel routine beat us to it.
+            //
+            DO_NOTHING();
+        }
+
+    }
+
+    return status;
+}
+
+VOID
+FX_VF_METHOD(FxIrpQueue, VerifyRemoveIrpFromQueueByContext)(
+    __in PFX_DRIVER_GLOBALS  FxDriverGlobals,
+    __in PMdIoCsqIrpContext Context
+    )
+/*++
+
+Routine Description:
+
+    Makes sure that the specified Request (context) belongs to this IRP queue.
+
+--*/
+{
+    PAGED_CODE_LOCKED();
+
+    if (FxDriverGlobals->IsVerificationEnabled(1, 11, OkForDownLevel)) {
+        if (Context->Irp != NULL &&
+            (Context->Type != FX_IRP_QUEUE_ENTRY_IDENTIFIER ||
+             Context->Csq  != (PIO_CSQ)this)) {
+
+            //
+            // This should never happen. Bugcheck before corrupting memory.
+            //
+            DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGREQUEST,
+                                "Irp 0x%p (Context 0x%p) not on IRP queue 0x%p\n",
+                                Context->Irp, Context, this);
+
+            FxVerifierBugCheck(FxDriverGlobals,
+                               WDF_REQUEST_FATAL_ERROR,
+                               WDF_REQUEST_FATAL_ERROR_REQUEST_NOT_IN_QUEUE,
+                               (ULONG_PTR) Context);
+        }
+    }
+}
+
+MdIrp
+FxIrpQueue::RemoveIrpFromQueueByContext(
+    __in PMdIoCsqIrpContext Context
+    )
+/*++
+
+Routine Description:
+
+    Using the context it remove the associated IRP from the queue.
+
+--*/
+{
+    MdIrp    irp;
+    MdCancelRoutine cancelRoutine;
+
+    if (Context->Irp ) {
+        //
+        // Make sure the Irp belongs to this queue.
+        //
+        ASSERT(Context->Csq == (PIO_CSQ)this);
+        VerifyRemoveIrpFromQueueByContext(m_LockObject->GetDriverGlobals(),
+                                          Context);
+
+        irp = Context->Irp;
+
+        FxIrp fxIrp(irp);
+
+        cancelRoutine = fxIrp.SetCancelRoutine(NULL);
+        if (!cancelRoutine) {
+            return NULL;
+        }
+
+        ASSERT(Context == fxIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY));
+
+        RemoveIrpFromListEntry(&fxIrp);
+
+        //
+        // Break the association.
+        //
+
+        Context->Irp = NULL;
+        fxIrp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
+
+        ASSERT(Context->Csq == (PIO_CSQ)this);
+
+        return irp;
+
+    } else {
+        return NULL;
+    }
+}
+
+
+MdIrp
+FxIrpQueue::PeekNextIrpFromQueue(
+    __in_opt MdIrp    Irp,
+    __in_opt PVOID   PeekContext
+    )
+/*++
+
+Routine Description:
+
+    This API look up IRP's in the queue.
+
+    If Irp == NULL, it returns one from the head
+    of the queue.
+
+    If Irp != NULL, it is a "peek context", and the
+    routine returns the *next* IRP in the queue.
+
+--*/
+{
+    PLIST_ENTRY        nextEntry;
+    PLIST_ENTRY        listHead;
+    FxIrp              irp(Irp);
+    FxIrp              nextIrp(NULL);
+
+    listHead = &m_Queue;
+
+    //
+    // If the IRP is NULL, we will start peeking from the listhead, else
+    // we will start from that IRP onwards. This is done under the
+    // assumption that new IRPs are always inserted at the tail.
+    //
+
+    if(Irp == NULL) {
+        nextEntry = listHead->Flink;
+    } else {
+        nextEntry = irp.ListEntry()->Flink;
+    }
+
+    while(nextEntry != listHead) {
+
+        nextIrp.SetIrp(FxIrp::GetIrpFromListEntry(nextEntry));
+
+        //
+        // If PeekContext is supplied, it's a search for an IRP associated
+        // with a particular file object.
+        //
+        if(PeekContext) {
+
+            if(nextIrp.GetFileObject() == (MdFileObject) PeekContext) {
+                break;
+            }
+        } else {
+            break;
+        }
+
+        nextIrp.SetIrp(NULL);
+
+        nextEntry = nextEntry->Flink;
+    }
+
+    return nextIrp.GetIrp();
+}
+
+MdIrp
+FxIrpQueue::RemoveNextIrpFromQueue(
+    __in_opt  PVOID                 PeekContext,
+    __out_opt PMdIoCsqIrpContext*  pCsqContext
+    )
+/*++
+
+Routine Description:
+
+    This routine will return a pointer to the next IRP in the queue adjacent to
+    the irp passed as a parameter. If the irp is NULL, it returns the IRP at the head of
+    the queue.
+
+--*/
+{
+    PMdIoCsqIrpContext context;
+    MdCancelRoutine cancelRoutine;
+    FxIrp    fxIrp(NULL);
+
+    fxIrp.SetIrp(PeekNextIrpFromQueue(NULL, PeekContext));
+
+    for (;;) {
+
+        if (!fxIrp.GetIrp()) {
+            return NULL;
+        }
+
+        cancelRoutine = fxIrp.SetCancelRoutine(NULL);
+        if (!cancelRoutine) {
+            fxIrp.SetIrp(PeekNextIrpFromQueue(fxIrp.GetIrp(), PeekContext));
+            continue;
+        }
+
+        RemoveIrpFromListEntry(&fxIrp);    // Remove this IRP from the queue
+
+        break;
+    }
+
+    context = (PMdIoCsqIrpContext)fxIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
+    if (context->Type == FX_IRP_QUEUE_ENTRY_IDENTIFIER) {
+        context->Irp = NULL;
+        ASSERT(context->Csq == (PIO_CSQ)this);
+    }
+
+    if(pCsqContext != NULL) {
+        *pCsqContext = context;
+    }
+
+    fxIrp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
+
+    return fxIrp.GetIrp();
+}
+
+
+VOID
+FxIrpQueue::_WdmCancelRoutineInternal(
+    __inout MdDeviceObject DeviceObject,
+    __in __drv_useCancelIRQL MdIrp   Irp
+    )
+/*++
+
+Routine Description:
+
+    This is the function called by WDM on the IRP when a cancel occurs
+
+--*/
+{
+    PMdIoCsqIrpContext irpContext;
+    FxIrpQueue* p;
+    KIRQL irql;
+    FxIrp irp(Irp);
+
+    UNREFERENCED_PARAMETER (DeviceObject);
+
+    Mx::ReleaseCancelSpinLock(irp.GetCancelIrql());
+
+    irpContext = (PMdIoCsqIrpContext)irp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
+
+    //
+    // Decide if we have a PIO_CSQ_IRP_CONTEXT or an FxIrpQueue*
+    //
+    if (irpContext->Type == FX_IRP_QUEUE_ENTRY_IDENTIFIER) {
+        p = (FxIrpQueue*)irpContext->Csq;
+    } else {
+        ASSERT(FALSE);
+        p = (FxIrpQueue*)irpContext;
+    }
+
+    ASSERT(p);
+
+    p->LockFromCancel(&irql);
+
+    // Remove the IRP from the list
+    p->RemoveIrpFromListEntry(&irp);
+
+    //
+    // Break the association if necessary.
+    //
+
+    if (irpContext != (PMdIoCsqIrpContext)p) {
+        irpContext->Irp = NULL;
+
+        irp.SetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY, NULL);
+    }
+
+    //
+    // We are calling cancel-callback of the owning object with the lock
+    // held so that it can successfully deliver the canceled request to the driver
+    // if needed. If we don't hold the lock, we run into a race condition between
+    // thread that's deleting the queue and this routine that's trying to deliver
+    // a request to the queue being deleted. So the way it happens is that the dispose
+    // call of queue waits for the request count to go zero. When we remove the
+    // last Irp from the list above, we end up dropping the count to zero. This causes
+    // the delete thread to run thru and destroy the FxIoQueue object. So to avoid that
+    // after popping the request from the FxIrpQueue, we have to call into the FxIoQueue
+    // with the lock and insert the request back into the FxIoQueue list so that delete
+    // thread will wait until the request is delivered to the driver.
+    //
+    if( p->m_CancelCallback != NULL ) {
+        p->m_CancelCallback(p, Irp, irpContext, irql);
+    }
+    else {
+
+        p->UnlockFromCancel(irql);
+
+        //
+        // Dispose of the IRP ourselves
+        //
+        irp.SetStatus(STATUS_CANCELLED);
+        irp.SetInformation(0);
+
+        DoTraceLevelMessage(p->m_LockObject->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
+                            "Irp 0x%p on Queue 0x%p Cancelled\n", Irp, p);
+
+        //
+        // Breakpoint for now. This usually means that someone
+        // is going to leak some driver frameworks state...
+        //
+        FxVerifierDbgBreakPoint(p->m_LockObject->GetDriverGlobals());
+
+        irp.CompleteRequest(IO_NO_INCREMENT);
+    }
+}
+
+BOOLEAN
+FxIrpQueue::IsIrpInQueue(
+    __in PMdIoCsqIrpContext Context
+    )
+/*++
+
+Routine Description:
+    Enumerates the list to see if any of the IRPs there has
+    a context that matches the input one.
+
+--*/
+{
+    PLIST_ENTRY         nextEntry;
+    FxIrp               nextIrp(NULL);
+    PMdIoCsqIrpContext  pCsqContext;
+
+    nextEntry = m_Queue.Flink;
+
+    while( nextEntry != &m_Queue ) {
+        nextIrp.SetIrp(FxIrp::GetIrpFromListEntry(nextEntry));
+
+        pCsqContext = (PMdIoCsqIrpContext)nextIrp.GetContext(FX_IRP_QUEUE_CSQ_CONTEXT_ENTRY);
+
+        if( pCsqContext == Context ) {
+            ASSERT(Context->Irp == nextIrp.GetIrp());
+            return TRUE;
+        }
+
+        nextEntry = nextEntry->Flink;
+    }
+
+    return FALSE;
+}
+
+