Fix filip's fixme and alex's fixme (ie: make IRP cancellation work properly). This should appease Hartmut even more =).
Modified: trunk/reactos/ntoskrnl/io/irp.c

Modified: trunk/reactos/ntoskrnl/io/irp.c
--- trunk/reactos/ntoskrnl/io/irp.c	2005-06-04 20:48:51 UTC (rev 15793)
+++ trunk/reactos/ntoskrnl/io/irp.c	2005-06-04 21:07:00 UTC (rev 15794)
@@ -37,6 +37,87 @@
     IoFreeIrp(CONTAINING_RECORD(Apc, IRP, Tail.Apc));
 }
 
+VOID
+STDCALL
+IopRemoveThreadIrp(VOID)
+{
+    KIRQL OldIrql;
+    PIRP DeadIrp;
+    PETHREAD IrpThread;
+    PLIST_ENTRY IrpEntry;
+    PIO_ERROR_LOG_PACKET ErrorLogEntry;
+    
+    /* First, raise to APC to protect IrpList */
+    KeRaiseIrql(APC_LEVEL, &OldIrql);
+    
+    /* Get the Thread and check the list */
+    IrpThread = PsGetCurrentThread();
+    if (IsListEmpty(&IrpThread->IrpList))
+    {
+        /* It got completed now, so quit */
+        KeLowerIrql(OldIrql);
+        return;
+    }
+    
+    /* Get the misbehaving IRP */
+    IrpEntry = IrpThread->IrpList.Flink;
+    DeadIrp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
+    
+    /* Disown the IRP! */
+    DeadIrp->Tail.Overlay.Thread = NULL;
+    InitializeListHead(&DeadIrp->ThreadListEntry);
+    RemoveHeadList(&IrpThread->IrpList);
+    
+    /* Lower IRQL now */
+    KeLowerIrql(OldIrql);
+        
+    /* Check if we can send an Error Log Entry*/
+    if (DeadIrp->CurrentLocation <= DeadIrp->StackCount)
+    {
+        /* Allocate an entry */
+        ErrorLogEntry = IoAllocateErrorLogEntry(IoGetCurrentIrpStackLocation(DeadIrp)->DeviceObject, 
+                                                sizeof(IO_ERROR_LOG_PACKET));
+        
+        /* Write the entry */
+        ErrorLogEntry->ErrorCode = 0xBAADF00D; /* FIXME */
+        IoWriteErrorLogEntry(ErrorLogEntry);
+    }
+}
+
+VOID
+STDCALL
+IopCleanupIrp(PIRP Irp,
+              PFILE_OBJECT FileObject)
+{
+    PMDL Mdl;
+    
+    /* Check if there's an MDL */
+    while ((Mdl = Irp->MdlAddress))
+    {
+        /* Clear all of them */
+        Irp->MdlAddress = Mdl->Next;
+        IoFreeMdl(Mdl);
+    }
+    
+    /* Free the buffer */
+    if (Irp->Flags & IRP_DEALLOCATE_BUFFER)
+    {
+        ExFreePoolWithTag(Irp->AssociatedIrp.SystemBuffer, TAG_SYS_BUF);
+    }
+    
+    /* Derefernce the User Event */
+    if (Irp->UserEvent && !(Irp->Flags & IRP_SYNCHRONOUS_API) && FileObject)
+    {
+        ObDereferenceObject(Irp->UserEvent);
+    }
+    
+    /* Dereference the File Object */
+    if (FileObject) ObDereferenceObject(FileObject);
+    
+    /* Free the IRP */
+    IoFreeIrp(Irp);
+}
+    
 /*
  * FUNCTION: Performs the second stage of irp completion for read/write irps
  *
@@ -884,59 +965,55 @@
  * @param Thread
  *        Thread to cancel requests for.
  */
-
 VOID
 STDCALL
 IoCancelThreadIo(PETHREAD Thread)
 {
-   PLIST_ENTRY IrpEntry;
-   PIRP Irp;
-   KIRQL OldIrql;
-   ULONG Retries = 3000;
-   LARGE_INTEGER Interval;
+    PLIST_ENTRY IrpEntry;
+    PIRP Irp;
+    KIRQL OldIrql;
+    ULONG Retries = 3000;
+    LARGE_INTEGER Interval;
 
-   OldIrql = KfRaiseIrql(APC_LEVEL);
+    /* Raise to APC to protect the IrpList */
+    OldIrql = KfRaiseIrql(APC_LEVEL);
 
-   /*
-    * Start by cancelling all the IRPs in the current thread queue.
-    */
-
-   for (IrpEntry = Thread->IrpList.Flink;
+    /* Start by cancelling all the IRPs in the current thread queue. */
+    for (IrpEntry = Thread->IrpList.Flink;
         IrpEntry != &Thread->IrpList;
         IrpEntry = IrpEntry->Flink)
-   {
-      Irp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
-      IoCancelIrp(Irp);
-   }
+    {
+       /* Get the IRP */
+        Irp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
+        
+        /* Cancel it */
+        IoCancelIrp(Irp);
+    }
+    
+     /* Wait 100 milliseconds */
+    Interval.QuadPart = -1000000;
 
-   /*
-    * Wait till all the IRPs are completed or cancelled.
-    */
+    /* Wait till all the IRPs are completed or cancelled. */
+    while (!IsListEmpty(&Thread->IrpList))
+    {
+        /* Now we can lower */
+        KfLowerIrql(OldIrql);
 
-   while (!IsListEmpty(&Thread->IrpList))
-   {
-      KfLowerIrql(OldIrql);
+        /* Wait a short while and then look if all our IRPs were completed. */
+        KeDelayExecutionThread(KernelMode, FALSE, &Interval);
 
-      /* Wait a short while and then look if all our IRPs were completed. */
-      Interval.QuadPart = -1000000; /* 100 milliseconds */
-      KeDelayExecutionThread(KernelMode, FALSE, &Interval);
+        /*
+         * Don't stay here forever if some broken driver doesn't complete
+         * the IRP.
+         */
+        if (Retries-- == 0) IopRemoveThreadIrp();
 
-      /*
-       * Don't stay here forever if some broken driver doesn't complete
-       * the IRP.
-       */
-
-      if (Retries-- == 0)
-      {
-         /* FIXME: Handle this gracefully. */
-         DPRINT1("Thread with dead IRPs!");
-         ASSERT(FALSE);
-      }
-
-      OldIrql = KfRaiseIrql(APC_LEVEL);
-   }
-
-   KfLowerIrql(OldIrql);
+        /* Raise the IRQL Again */
+        OldIrql = KfRaiseIrql(APC_LEVEL);
+    }
+    
+    /* We're done, lower the IRQL */
+    KfLowerIrql(OldIrql);
 }
 
 #ifdef IoCallDriver
@@ -1204,8 +1281,29 @@
     }
     else
     {
-        /* The IRP just got cancelled... don't think this happens in ROS yet */
-        DPRINT1("The IRP was cancelled. Go Bug Alex\n");
+        /* The IRP just got cancelled... does a thread still own it? */
+        if ((Thread = Irp->Tail.Overlay.Thread))
+        {
+            /* Yes! There is still hope! */
+            DPRINT("KMODE APC QUEUE\n");
+            KeInitializeApc(&Irp->Tail.Apc,
+                            &Thread->Tcb,
+                            Irp->ApcEnvironment,
+                            IopCompleteRequest,
+                            NULL,
+                            (PKNORMAL_ROUTINE) NULL,
+                            KernelMode,
+                            NULL);
+            KeInsertQueueApc(&Irp->Tail.Apc,
+                             FileObject,
+                             NULL, /* This is used for REPARSE stuff */
+                             PriorityBoost);
+        }
+        else
+        {
+            /* Nothing left for us to do, kill it */
+            IopCleanupIrp(Irp, FileObject);
+        }
     }
 }