- 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]->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 @@
 
     } 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);
     }
@@ -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 */