mirror of
https://github.com/reactos/reactos.git
synced 2024-12-27 09:34:43 +00:00
- 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. svn path=/trunk/; revision=13824
This commit is contained in:
parent
3d5ac6b2be
commit
594c2c424b
5 changed files with 213 additions and 22 deletions
|
@ -426,6 +426,11 @@ IopBootLog(PUNICODE_STRING DriverName, BOOLEAN Success);
|
|||
VOID
|
||||
IopSaveBootLogToFile(VOID);
|
||||
|
||||
/* cancel.c */
|
||||
|
||||
VOID STDCALL
|
||||
IoCancelThreadIo(PETHREAD Thread);
|
||||
|
||||
/* errlog.c */
|
||||
|
||||
NTSTATUS
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue