- 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. Modified: trunk/reactos/ntoskrnl/include/internal/ke.h Modified: trunk/reactos/ntoskrnl/include/internal/ps.h Modified: trunk/reactos/ntoskrnl/ke/apc.c Modified: trunk/reactos/ntoskrnl/ke/kthread.c Modified: trunk/reactos/ntoskrnl/ps/kill.c Modified: trunk/reactos/ntoskrnl/ps/notify.c _____
Modified: trunk/reactos/ntoskrnl/include/internal/ke.h --- trunk/reactos/ntoskrnl/include/internal/ke.h 2005-07-23 13:43:52 UTC (rev 16704) +++ trunk/reactos/ntoskrnl/include/internal/ke.h 2005-07-23 17:40:48 UTC (rev 16705) @@ -253,6 +253,10 @@
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 @@ 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); _____
Modified: trunk/reactos/ntoskrnl/include/internal/ps.h --- trunk/reactos/ntoskrnl/include/internal/ps.h 2005-07-23 13:43:52 UTC (rev 16704) +++ trunk/reactos/ntoskrnl/include/internal/ps.h 2005-07-23 17:40:48 UTC (rev 16705) @@ -184,6 +184,10 @@
STDCALL PspRunCreateProcessNotifyRoutines(PEPROCESS, BOOLEAN);
+VOID +STDCALL +PspRunLegoRoutine(IN PKTHREAD Thread); + VOID INIT_FUNCTION PsInitJobManagment(VOID);
/* CLIENT ID */ _____
Modified: trunk/reactos/ntoskrnl/ke/apc.c --- trunk/reactos/ntoskrnl/ke/apc.c 2005-07-23 13:43:52 UTC (rev 16704) +++ trunk/reactos/ntoskrnl/ke/apc.c 2005-07-23 17:40:48 UTC (rev 16705) @@ -270,14 +270,14 @@
*/ 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]->ApcLis tHead[(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 @@
} 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]->ApcLis tHead[(int)Apc->ApcMode], &Apc->ApcListEntry); } @@ -441,6 +441,73 @@ }
/*++ + * 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 * * The KeRemoveQueueApc routine removes a given APC object from the system _____
Modified: trunk/reactos/ntoskrnl/ke/kthread.c --- trunk/reactos/ntoskrnl/ke/kthread.c 2005-07-23 13:43:52 UTC (rev 16704) +++ trunk/reactos/ntoskrnl/ke/kthread.c 2005-07-23 17:40:48 UTC (rev 16705) @@ -387,6 +387,29 @@
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) _____
Modified: trunk/reactos/ntoskrnl/ps/kill.c --- trunk/reactos/ntoskrnl/ps/kill.c 2005-07-23 13:43:52 UTC (rev 16704) +++ trunk/reactos/ntoskrnl/ps/kill.c 2005-07-23 17:40:48 UTC (rev 16705) @@ -213,6 +213,8 @@
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 @@ /* 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()); _____
Modified: trunk/reactos/ntoskrnl/ps/notify.c --- trunk/reactos/ntoskrnl/ps/notify.c 2005-07-23 13:43:52 UTC (rev 16704) +++ trunk/reactos/ntoskrnl/ps/notify.c 2005-07-23 17:40:48 UTC (rev 16705) @@ -26,9 +26,10 @@
static PLOAD_IMAGE_NOTIFY_ROUTINE PspLoadImageNotifyRoutine[MAX_LOAD_IMAGE_NOTIFY_ROUTINE_COUNT]; + +typedef VOID (STDCALL *PLEGO_NOTIFY_ROUTINE)(IN PKTHREAD Thread); +static PLEGO_NOTIFY_ROUTINE PspLegoNotifyRoutine;
-static PVOID PspLegoNotifyRoutine; - /* FUNCTIONS ***************************************************************/
/* @@ -231,4 +232,12 @@ } }
+VOID +STDCALL +PspRunLegoRoutine(IN PKTHREAD Thread) +{ + /* Call it */ + if (PspLegoNotifyRoutine) (PspLegoNotifyRoutine)(Thread); +} + /* EOF */