- 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
This commit is contained in:
Alex Ionescu 2005-07-23 17:40:48 +00:00
parent b244520c5a
commit 7669e8fd6d
6 changed files with 164 additions and 5 deletions

View file

@ -253,6 +253,10 @@ ULONG
STDCALL STDCALL
KeForceResumeThread(IN PKTHREAD Thread); KeForceResumeThread(IN PKTHREAD Thread);
BOOLEAN
STDCALL
KeDisableThreadApcQueueing(IN PKTHREAD Thread);
BOOLEAN STDCALL KiInsertTimer(PKTIMER Timer, LARGE_INTEGER DueTime); BOOLEAN STDCALL KiInsertTimer(PKTIMER Timer, LARGE_INTEGER DueTime);
VOID inline FASTCALL KiSatisfyObjectWait(PDISPATCHER_HEADER Object, PKTHREAD Thread); VOID inline FASTCALL KiSatisfyObjectWait(PDISPATCHER_HEADER Object, PKTHREAD Thread);
@ -292,6 +296,12 @@ VOID STDCALL KiInitializeUserApc(IN PVOID Reserved,
IN PVOID SystemArgument1, IN PVOID SystemArgument1,
IN PVOID SystemArgument2); 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 KiAttachProcess(struct _KTHREAD *Thread, struct _KPROCESS *Process, KIRQL ApcLock, struct _KAPC_STATE *SavedApcState);
VOID STDCALL KiSwapProcess(struct _KPROCESS *NewProcess, struct _KPROCESS *OldProcess); VOID STDCALL KiSwapProcess(struct _KPROCESS *NewProcess, struct _KPROCESS *OldProcess);

View file

@ -184,6 +184,10 @@ VOID
STDCALL STDCALL
PspRunCreateProcessNotifyRoutines(PEPROCESS, BOOLEAN); PspRunCreateProcessNotifyRoutines(PEPROCESS, BOOLEAN);
VOID
STDCALL
PspRunLegoRoutine(IN PKTHREAD Thread);
VOID INIT_FUNCTION PsInitJobManagment(VOID); VOID INIT_FUNCTION PsInitJobManagment(VOID);
/* CLIENT ID */ /* CLIENT ID */

View file

@ -270,14 +270,14 @@ KiInsertQueueApc(PKAPC Apc,
*/ */
if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) { 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; Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = TRUE;
InsertHeadList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode], InsertHeadList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
&Apc->ApcListEntry); &Apc->ApcListEntry);
} else if (Apc->NormalRoutine == NULL) { } 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; for (ApcListEntry = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode].Flink;
ApcListEntry != &Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode]; ApcListEntry != &Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode];
@ -293,7 +293,7 @@ KiInsertQueueApc(PKAPC Apc,
} else { } 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], InsertTailList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
&Apc->ApcListEntry); &Apc->ApcListEntry);
} }
@ -440,6 +440,73 @@ KeInsertQueueApc(PKAPC Apc,
return Inserted; 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 * KeRemoveQueueApc
* *

View file

@ -387,6 +387,29 @@ KeGetPreviousMode(VOID)
return (ULONG)PsGetCurrentThread()->Tcb.PreviousMode; 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 VOID
STDCALL STDCALL
KeRundownThread(VOID) KeRundownThread(VOID)

View file

@ -213,6 +213,8 @@ PspExitThread(NTSTATUS ExitStatus)
PTERMINATION_PORT TerminationPort; PTERMINATION_PORT TerminationPort;
PTEB Teb; PTEB Teb;
KIRQL oldIrql; KIRQL oldIrql;
PLIST_ENTRY ApcEntry, CurrentApc;
PKAPC Apc;
DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread()); DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread());
@ -332,6 +334,50 @@ PspExitThread(NTSTATUS ExitStatus)
/* Rundown Mutexes */ /* Rundown Mutexes */
KeRundownThread(); 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 */ /* Terminate the Thread from the Scheduler */
KeTerminateThread(0); KeTerminateThread(0);
DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread()); DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());

View file

@ -26,8 +26,9 @@ PspProcessNotifyRoutine[MAX_PROCESS_NOTIFY_ROUTINE_COUNT];
static PLOAD_IMAGE_NOTIFY_ROUTINE static PLOAD_IMAGE_NOTIFY_ROUTINE
PspLoadImageNotifyRoutine[MAX_LOAD_IMAGE_NOTIFY_ROUTINE_COUNT]; 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 ***************************************************************/ /* FUNCTIONS ***************************************************************/
@ -231,4 +232,12 @@ PspRunLoadImageNotifyRoutines(PUNICODE_STRING FullImageName,
} }
} }
VOID
STDCALL
PspRunLegoRoutine(IN PKTHREAD Thread)
{
/* Call it */
if (PspLegoNotifyRoutine) (PspLegoNotifyRoutine)(Thread);
}
/* EOF */ /* EOF */