- Implement NtCancelIoFile.
- Queue the IRP to correct thread in IoQueueThreadIrp.
- Cancel I/O requests when thread is about to be terminated.
- Do not queue close requests to thread IRP list.
Modified: trunk/reactos/ntoskrnl/include/internal/io.h
Modified: trunk/reactos/ntoskrnl/io/cancel.c
Modified: trunk/reactos/ntoskrnl/io/iomgr.c
Modified: trunk/reactos/ntoskrnl/io/irp.c
Modified: trunk/reactos/ntoskrnl/ps/kill.c

Modified: trunk/reactos/ntoskrnl/include/internal/io.h
--- trunk/reactos/ntoskrnl/include/internal/io.h	2005-03-05 05:13:38 UTC (rev 13823)
+++ trunk/reactos/ntoskrnl/include/internal/io.h	2005-03-05 11:27:15 UTC (rev 13824)
@@ -426,6 +426,11 @@
 VOID
 IopSaveBootLogToFile(VOID);
 
+/* cancel.c */
+
+VOID STDCALL
+IoCancelThreadIo(PETHREAD Thread);
+
 /* errlog.c */
 
 NTSTATUS

Modified: trunk/reactos/ntoskrnl/io/cancel.c
--- trunk/reactos/ntoskrnl/io/cancel.c	2005-03-05 05:13:38 UTC (rev 13823)
+++ trunk/reactos/ntoskrnl/io/cancel.c	2005-03-05 11:27:15 UTC (rev 13824)
@@ -20,14 +20,183 @@
 
 /* FUNCTIONS *****************************************************************/
 
+/**
+ * @name NtCancelIoFile
+ *
+ * Cancel all pending I/O operations in the current thread for specified 
+ * file object.
+ *
+ * @param FileHandle
+ *        Handle to file object to cancel requests for. No specific
+ *        access rights are needed.
+ * @param IoStatusBlock
+ *        Pointer to status block which is filled with final completition
+ *        status on successful return.
+ *
+ * @return Status.
+ *
+ * @implemented
+ */
+
 NTSTATUS STDCALL
-NtCancelIoFile (IN	HANDLE			FileHandle,
-		OUT	PIO_STATUS_BLOCK	IoStatusBlock)
+NtCancelIoFile(
+   IN HANDLE FileHandle,
+   OUT PIO_STATUS_BLOCK IoStatusBlock)
 {
-  UNIMPLEMENTED;
-  return(STATUS_NOT_IMPLEMENTED);
+   NTSTATUS Status;
+   PFILE_OBJECT FileObject;
+   PETHREAD Thread;
+   PLIST_ENTRY IrpEntry;
+   PIRP Irp;
+   KIRQL OldIrql;
+   BOOLEAN OurIrpsInList = FALSE;
+   LARGE_INTEGER Interval;
+
+   if ((ULONG_PTR)IoStatusBlock >= MmUserProbeAddress &&
+       KeGetPreviousMode() == UserMode)
+      return STATUS_ACCESS_VIOLATION;
+
+   Status = ObReferenceObjectByHandle(FileHandle, 0, IoFileObjectType,
+                                      KeGetPreviousMode(), (PVOID*)&FileObject,
+                                      NULL);
+   if (!NT_SUCCESS(Status))
+      return Status;
+
+   /* IRP cancellations are synchronized at APC_LEVEL. */
+   OldIrql = KfRaiseIrql(APC_LEVEL);
+
+   /*
+    * Walk the list of active IRPs and cancel the ones that belong to
+    * our file object.
+    */
+
+   Thread = PsGetCurrentThread();
+   for (IrpEntry = Thread->IrpList.Flink;
+        IrpEntry != &Thread->IrpList;
+        IrpEntry = IrpEntry->Flink)
+   {
+      Irp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
+      if (Irp->Tail.Overlay.OriginalFileObject == FileObject)
+      {
+         IoCancelIrp(Irp);
+         /* Don't break here, we want to cancel all IRPs for the file object. */
+         OurIrpsInList = TRUE;
+      }
+   }   
+
+   KfLowerIrql(OldIrql);
+
+   while (OurIrpsInList)
+   {
+      OurIrpsInList = FALSE;
+
+      /* Wait a short while and then look if all our IRPs were completed. */
+      Interval.QuadPart = -1000000; /* 100 milliseconds */
+      KeDelayExecutionThread(KernelMode, FALSE, &Interval);
+
+      OldIrql = KfRaiseIrql(APC_LEVEL);
+
+      /*
+       * Look in the list if all IRPs for the specified file object
+       * are completed (or cancelled). If someone sends a new IRP
+       * for our file object while we're here we can happily loop
+       * forever.
+       */
+
+      for (IrpEntry = Thread->IrpList.Flink;
+           IrpEntry != &Thread->IrpList;
+           IrpEntry = IrpEntry->Flink)
+      {
+         Irp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
+         if (Irp->Tail.Overlay.OriginalFileObject == FileObject)
+         {
+            OurIrpsInList = TRUE;
+            break;
+         }
+      }
+
+      KfLowerIrql(OldIrql);
+   }
+
+   _SEH_TRY
+   {
+      IoStatusBlock->Status = STATUS_SUCCESS;
+      IoStatusBlock->Information = 0;
+      Status = STATUS_SUCCESS;
+   }
+   _SEH_HANDLE
+   {
+      Status = STATUS_UNSUCCESSFUL;
+   }
+   _SEH_END;
+
+   ObDereferenceObject(FileObject);
+
+   return Status;
 }
 
+/**
+ * @name IoCancelThreadIo
+ *
+ * Cancel all pending I/O request associated with specified thread.
+ *
+ * @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;
+
+   OldIrql = KfRaiseIrql(APC_LEVEL);
+
+   /*
+    * 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);
+   }
+
+   /*
+    * Wait till all the IRPs are completed or cancelled.
+    */
+
+   while (!IsListEmpty(&Thread->IrpList))
+   {
+      KfLowerIrql(OldIrql);
+
+      /* 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)
+      {
+         /* FIXME: Handle this gracefully. */
+         DPRINT1("Thread with dead IRPs!");
+         ASSERT(FALSE);
+      }
+      
+      OldIrql = KfRaiseIrql(APC_LEVEL);
+   }
+
+   KfLowerIrql(OldIrql);
+}
+
 /*
  * @implemented
  */

Modified: trunk/reactos/ntoskrnl/io/iomgr.c
--- trunk/reactos/ntoskrnl/io/iomgr.c	2005-03-05 05:13:38 UTC (rev 13823)
+++ trunk/reactos/ntoskrnl/io/iomgr.c	2005-03-05 11:27:15 UTC (rev 13824)
@@ -105,15 +105,24 @@
 			        UserMode);
 #endif   
      KeResetEvent( &FileObject->Event );
-     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_CLOSE,
-				        FileObject->DeviceObject,
-				        NULL,
-				        0,
-				        NULL,
-				        &FileObject->Event,
-				        NULL);
+
+     Irp = IoAllocateIrp(FileObject->DeviceObject->StackSize, TRUE);
+     if (Irp == NULL)
+     {
+        /*
+         * FIXME: This case should eventually be handled. We should wait
+         * until enough memory is available to allocate the IRP.
+         */
+        ASSERT(FALSE);
+     }
+   
+     Irp->UserEvent = &FileObject->Event;
+     Irp->Tail.Overlay.Thread = PsGetCurrentThread();
      Irp->Flags |= IRP_CLOSE_OPERATION;
+   
      StackPtr = IoGetNextIrpStackLocation(Irp);
+     StackPtr->MajorFunction = IRP_MJ_CLOSE;
+     StackPtr->DeviceObject = FileObject->DeviceObject;
      StackPtr->FileObject = FileObject;
    
      Status = IoCallDriver(FileObject->DeviceObject, Irp);

Modified: trunk/reactos/ntoskrnl/io/irp.c
--- trunk/reactos/ntoskrnl/io/irp.c	2005-03-05 05:13:38 UTC (rev 13823)
+++ trunk/reactos/ntoskrnl/io/irp.c	2005-03-05 11:27:15 UTC (rev 13824)
@@ -268,7 +268,6 @@
       return(NULL);
     }
 
-  RtlZeroMemory(Irp, IoSizeOfIrp(StackSize));
   IoInitializeIrp(Irp,
 		  IoSizeOfIrp(StackSize),
 		  StackSize);
@@ -364,6 +363,9 @@
       ULONG MasterIrpCount;
       PIRP MasterIrp = Irp->AssociatedIrp.MasterIrp;
 
+      /* This should never happen! */
+      ASSERT(IsListEmpty(&Irp->ThreadListEntry));
+
       MasterIrpCount = InterlockedDecrement(&MasterIrp->AssociatedIrp.IrpCount);
       while ((Mdl = Irp->MdlAddress))
       {
@@ -386,6 +388,9 @@
    /* Windows NT File System Internals, page 165 */
    if (Irp->Flags & (IRP_PAGING_IO|IRP_CLOSE_OPERATION))
    {
+      /* This should never happen! */
+      ASSERT(IsListEmpty(&Irp->ThreadListEntry));
+
       /* 
        * If MDL_IO_PAGE_READ is set, then the caller is responsible 
        * for deallocating of the mdl. 
@@ -582,19 +587,19 @@
 VOID STDCALL
 IoQueueThreadIrp(IN PIRP Irp)
 {
-/* undefine this when (if ever) implementing irp cancellation */
-#if 0
-  KIRQL oldIrql;
+   KIRQL OldIrql;
   
-  oldIrql = KfRaiseIrql(APC_LEVEL);
+   OldIrql = KfRaiseIrql(APC_LEVEL);
   
-  /* Synchronous irp's are queued to requestor thread. If they are not completed
-  when the thread exits, they are canceled (cleaned up).
-  -Gunnar */
-  InsertTailList(&PsGetCurrentThread()->IrpList, &Irp->ThreadListEntry);
+   /*
+    * Synchronous irp's are queued to requestor thread. If they are not
+    * completed when the thread exits, they are canceled (cleaned up).
+    * - Gunnar
+    */
+
+   InsertTailList(&Irp->Tail.Overlay.Thread->IrpList, &Irp->ThreadListEntry);
     
-  KfLowerIrql(oldIrql);    
-#endif
+   KfLowerIrql(OldIrql);
 }
 
 

Modified: trunk/reactos/ntoskrnl/ps/kill.c
--- trunk/reactos/ntoskrnl/ps/kill.c	2005-03-05 05:13:38 UTC (rev 13823)
+++ trunk/reactos/ntoskrnl/ps/kill.c	2005-03-05 11:27:15 UTC (rev 13824)
@@ -158,6 +158,9 @@
  
    PsLockProcess(CurrentProcess, FALSE);
 
+   /* Cancel I/O for the thread. */
+   IoCancelThreadIo(CurrentThread);
+
    /* Remove the thread from the thread list of its process */
    RemoveEntryList(&CurrentThread->ThreadListEntry);
    Last = IsListEmpty(&CurrentProcess->ThreadListHead);