From 7669e8fd6d214835a339524e4960f3ab7d5464cd Mon Sep 17 00:00:00 2001 From: Alex Ionescu Date: Sat, 23 Jul 2005 17:40:48 +0000 Subject: [PATCH] - Disable APC Queuing & Flush APC queues during thread shutdown. - Detect if Kernel APCs were pending during thread shutdown. - Call Lego Routine, if registered, during thread shutdown. svn path=/trunk/; revision=16705 --- reactos/ntoskrnl/include/internal/ke.h | 10 ++++ reactos/ntoskrnl/include/internal/ps.h | 4 ++ reactos/ntoskrnl/ke/apc.c | 73 ++++++++++++++++++++++++-- reactos/ntoskrnl/ke/kthread.c | 23 ++++++++ reactos/ntoskrnl/ps/kill.c | 46 ++++++++++++++++ reactos/ntoskrnl/ps/notify.c | 13 ++++- 6 files changed, 164 insertions(+), 5 deletions(-) diff --git a/reactos/ntoskrnl/include/internal/ke.h b/reactos/ntoskrnl/include/internal/ke.h index cc0419312cb..dcea4428c46 100644 --- a/reactos/ntoskrnl/include/internal/ke.h +++ b/reactos/ntoskrnl/include/internal/ke.h @@ -253,6 +253,10 @@ ULONG STDCALL KeForceResumeThread(IN PKTHREAD Thread); +BOOLEAN +STDCALL +KeDisableThreadApcQueueing(IN PKTHREAD Thread); + BOOLEAN STDCALL KiInsertTimer(PKTIMER Timer, LARGE_INTEGER DueTime); VOID inline FASTCALL KiSatisfyObjectWait(PDISPATCHER_HEADER Object, PKTHREAD Thread); @@ -292,6 +296,12 @@ VOID STDCALL KiInitializeUserApc(IN PVOID Reserved, IN PVOID SystemArgument1, IN PVOID SystemArgument2); +PLIST_ENTRY +STDCALL +KeFlushQueueApc(IN PKTHREAD Thread, + IN KPROCESSOR_MODE PreviousMode); + + VOID STDCALL KiAttachProcess(struct _KTHREAD *Thread, struct _KPROCESS *Process, KIRQL ApcLock, struct _KAPC_STATE *SavedApcState); VOID STDCALL KiSwapProcess(struct _KPROCESS *NewProcess, struct _KPROCESS *OldProcess); diff --git a/reactos/ntoskrnl/include/internal/ps.h b/reactos/ntoskrnl/include/internal/ps.h index 38654ebfdbf..5bc2f94ea48 100644 --- a/reactos/ntoskrnl/include/internal/ps.h +++ b/reactos/ntoskrnl/include/internal/ps.h @@ -184,6 +184,10 @@ VOID STDCALL PspRunCreateProcessNotifyRoutines(PEPROCESS, BOOLEAN); +VOID +STDCALL +PspRunLegoRoutine(IN PKTHREAD Thread); + VOID INIT_FUNCTION PsInitJobManagment(VOID); /* CLIENT ID */ diff --git a/reactos/ntoskrnl/ke/apc.c b/reactos/ntoskrnl/ke/apc.c index e19a09ffa6f..c8c48474838 100644 --- a/reactos/ntoskrnl/ke/apc.c +++ b/reactos/ntoskrnl/ke/apc.c @@ -270,14 +270,14 @@ KiInsertQueueApc(PKAPC Apc, */ if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) { - DPRINT ("Inserting the Process Exit APC into the Queue\n"); + DPRINT1("Inserting the Process Exit APC into the Queue\n"); Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = TRUE; InsertHeadList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode], &Apc->ApcListEntry); } else if (Apc->NormalRoutine == NULL) { - DPRINT ("Inserting Special APC %x into the Queue\n", Apc); + DPRINT("Inserting Special APC %x into the Queue\n", Apc); for (ApcListEntry = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode].Flink; ApcListEntry != &Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode]; @@ -293,7 +293,7 @@ KiInsertQueueApc(PKAPC Apc, } else { - DPRINT ("Inserting Normal APC %x into the %x Queue\n", Apc, Apc->ApcMode); + DPRINT("Inserting Normal APC %x into the %x Queue\n", Apc, Apc->ApcMode); InsertTailList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode], &Apc->ApcListEntry); } @@ -440,6 +440,73 @@ KeInsertQueueApc(PKAPC Apc, return Inserted; } +/*++ + * KeFlushQueueApc + * + * The KeFlushQueueApc routine flushes all APCs of the given processor mode + * from the specified Thread's APC queue. + * + * Params: + * Thread - Pointer to the thread whose APC queue will be flushed. + * + * PreviousMode - Specifies which APC Queue to flush. + * + * Returns: + * A pointer to the first entry in the flushed APC queue. + * + * Remarks: + * If the routine returns NULL, it means that no APCs were to be flushed. + * + * Callers of KeFlushQueueApc must be running at DISPATCH_LEVEL or lower. + * + *--*/ +PLIST_ENTRY +STDCALL +KeFlushQueueApc(IN PKTHREAD Thread, + IN KPROCESSOR_MODE PreviousMode) +{ + KIRQL OldIrql; + PKAPC Apc; + PLIST_ENTRY ApcEntry, CurrentEntry; + + /* Lock the Dispatcher Database and APC Queue */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); + + /* Check if the list is empty */ + if (IsListEmpty(&Thread->ApcState.ApcListHead[PreviousMode])) + { + /* We'll return NULL */ + ApcEntry = NULL; + } + else + { + /* Remove this one */ + RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]); + CurrentEntry = ApcEntry; + + /* Remove all the other ones too, if present */ + do + { + /* Get the APC */ + Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry); + + /* Move to the next one */ + CurrentEntry = CurrentEntry->Flink; + + /* Mark it as not inserted */ + Apc->Inserted = FALSE; + } while (ApcEntry != CurrentEntry); + } + + /* Release the locks */ + KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); + KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Return the first entry */ + return ApcEntry; +} + /*++ * KeRemoveQueueApc * diff --git a/reactos/ntoskrnl/ke/kthread.c b/reactos/ntoskrnl/ke/kthread.c index d4110b523c0..31ab30ab04d 100644 --- a/reactos/ntoskrnl/ke/kthread.c +++ b/reactos/ntoskrnl/ke/kthread.c @@ -387,6 +387,29 @@ KeGetPreviousMode(VOID) return (ULONG)PsGetCurrentThread()->Tcb.PreviousMode; } +BOOLEAN +STDCALL +KeDisableThreadApcQueueing(IN PKTHREAD Thread) +{ + KIRQL OldIrql; + BOOLEAN PreviousState; + + /* Lock the Dispatcher Database */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Save old state */ + PreviousState = Thread->ApcQueueable; + + /* Disable it now */ + Thread->ApcQueueable = FALSE; + + /* Release the Lock */ + KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Return old state */ + return PreviousState; +} + VOID STDCALL KeRundownThread(VOID) diff --git a/reactos/ntoskrnl/ps/kill.c b/reactos/ntoskrnl/ps/kill.c index f45cd2ae74d..2bb45c6d66d 100644 --- a/reactos/ntoskrnl/ps/kill.c +++ b/reactos/ntoskrnl/ps/kill.c @@ -213,6 +213,8 @@ PspExitThread(NTSTATUS ExitStatus) PTERMINATION_PORT TerminationPort; PTEB Teb; KIRQL oldIrql; + PLIST_ENTRY ApcEntry, CurrentApc; + PKAPC Apc; DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread()); @@ -332,6 +334,50 @@ PspExitThread(NTSTATUS ExitStatus) /* Rundown Mutexes */ KeRundownThread(); + /* Disable new APC Queuing, this is as far as we'll let them go */ + KeDisableThreadApcQueueing(&CurrentThread->Tcb); + + /* Flush the User APCs */ + if ((ApcEntry = KeFlushQueueApc(&CurrentThread->Tcb, UserMode))) + { + CurrentApc = ApcEntry; + do + { + /* Get the APC */ + Apc = CONTAINING_RECORD(CurrentApc, KAPC, ApcListEntry); + + /* Move to the next one */ + CurrentApc = CurrentApc->Flink; + + /* Rundown the APC or de-allocate it */ + if (Apc->RundownRoutine) + { + /* Call its own routine */ + (Apc->RundownRoutine)(Apc); + } + else + { + /* Do it ourselves */ + ExFreePool(Apc); + } + } + while (CurrentApc != ApcEntry); + } + + /* Call the Lego routine */ + if (CurrentThread->Tcb.LegoData) PspRunLegoRoutine(&CurrentThread->Tcb); + + /* Flush the APC queue, which should be empty */ + if ((ApcEntry = KeFlushQueueApc(&CurrentThread->Tcb, KernelMode))) + { + /* Bugcheck time */ + KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT, + (ULONG_PTR)ApcEntry, + CurrentThread->Tcb.KernelApcDisable, + oldIrql, + 0); + } + /* Terminate the Thread from the Scheduler */ KeTerminateThread(0); DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread()); diff --git a/reactos/ntoskrnl/ps/notify.c b/reactos/ntoskrnl/ps/notify.c index 703d82114d9..46ac36ee31c 100644 --- a/reactos/ntoskrnl/ps/notify.c +++ b/reactos/ntoskrnl/ps/notify.c @@ -26,8 +26,9 @@ PspProcessNotifyRoutine[MAX_PROCESS_NOTIFY_ROUTINE_COUNT]; static PLOAD_IMAGE_NOTIFY_ROUTINE PspLoadImageNotifyRoutine[MAX_LOAD_IMAGE_NOTIFY_ROUTINE_COUNT]; - -static PVOID PspLegoNotifyRoutine; + +typedef VOID (STDCALL *PLEGO_NOTIFY_ROUTINE)(IN PKTHREAD Thread); +static PLEGO_NOTIFY_ROUTINE PspLegoNotifyRoutine; /* FUNCTIONS ***************************************************************/ @@ -231,4 +232,12 @@ PspRunLoadImageNotifyRoutines(PUNICODE_STRING FullImageName, } } +VOID +STDCALL +PspRunLegoRoutine(IN PKTHREAD Thread) +{ + /* Call it */ + if (PspLegoNotifyRoutine) (PspLegoNotifyRoutine)(Thread); +} + /* EOF */ -- 2.17.1