- 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 */