- Implement NtCancelIoFile.
authorFilip Navara <filip.navara@gmail.com>
Sat, 5 Mar 2005 11:27:15 +0000 (11:27 +0000)
committerFilip Navara <filip.navara@gmail.com>
Sat, 5 Mar 2005 11:27:15 +0000 (11:27 +0000)
- 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.

svn path=/trunk/; revision=13824

reactos/ntoskrnl/include/internal/io.h
reactos/ntoskrnl/io/cancel.c
reactos/ntoskrnl/io/iomgr.c
reactos/ntoskrnl/io/irp.c
reactos/ntoskrnl/ps/kill.c

index 823c153..b23f612 100644 (file)
@@ -426,6 +426,11 @@ IopBootLog(PUNICODE_STRING DriverName, BOOLEAN Success);
 VOID
 IopSaveBootLogToFile(VOID);
 
+/* cancel.c */
+
+VOID STDCALL
+IoCancelThreadIo(PETHREAD Thread);
+
 /* errlog.c */
 
 NTSTATUS
index f5c4312..1ce6919 100644 (file)
@@ -20,12 +20,181 @@ static KSPIN_LOCK CancelSpinLock;
 
 /* 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);
 }
 
 /*
index 81852d2..eff2a12 100644 (file)
@@ -105,15 +105,24 @@ IopDeleteFile(PVOID ObjectBody)
                                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);
index d898dc4..ed98e86 100644 (file)
@@ -268,7 +268,6 @@ IoAllocateIrp(CCHAR StackSize,
       return(NULL);
     }
 
-  RtlZeroMemory(Irp, IoSizeOfIrp(StackSize));
   IoInitializeIrp(Irp,
                  IoSizeOfIrp(StackSize),
                  StackSize);
@@ -364,6 +363,9 @@ IofCompleteRequest(PIRP Irp,
       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 @@ IofCompleteRequest(PIRP Irp,
    /* 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 @@ IoGetTopLevelIrp(VOID)
 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);
 }
 
 
index 1837f09..acd9271 100644 (file)
@@ -158,6 +158,9 @@ PsTerminateCurrentThread(NTSTATUS ExitStatus)
  
    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);