- 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);