KD System Rewrite:
[reactos.git] / reactos / ntoskrnl / ke / apc.c
index 202774f..81feb42 100644 (file)
@@ -1,30 +1,11 @@
 /*
- *  ReactOS kernel
- *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/*
+ * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * FILE:            ntoskrnl/ke/apc.c
- * PURPOSE:         Possible implementation of APCs
- * PROGRAMMER:      David Welch (welch@cwcom.net)
- * PORTABILITY:     Unchecked
- * UPDATE HISTORY:
- *                  Created 22/05/98
- *                  12/11/99:  Phillip Susi: Reworked the APC code
+ * PURPOSE:         NT Implementation of APCs
+ * 
+ * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
+ *                  Phillip Susi
  */
 
 /* INCLUDES *****************************************************************/
 #define NDEBUG
 #include <internal/debug.h>
 
-/* GLOBALS *******************************************************************/
-
-KSPIN_LOCK PiApcLock;
-
-VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
-
-#define TAG_KAPC     TAG('K', 'A', 'P', 'C')
-
 /* FUNCTIONS *****************************************************************/
 
-/*
- * @unimplemented
- */
-BOOLEAN
+/*++
+ * KiKernelApcDeliveryCheck 
+ * @implemented NT 5.2
+ *
+ *     The KiKernelApcDeliveryCheck routine is called whenever APCs have just
+ *     been re-enabled in Kernel Mode, such as after leaving a Critical or
+ *     Guarded Region. It delivers APCs if the environment is right.
+ *
+ * Params:
+ *     None.
+ *
+ * Returns:
+ *     None.
+ *
+ * Remarks:
+ *     This routine allows KeLeave/EnterCritical/GuardedRegion to be used as a
+ *     macro from inside WIN32K or other Drivers, which will then only have to
+ *     do an Import API call in the case where APCs are enabled again.
+ *
+ *--*/
+VOID
 STDCALL
-KeAreApcsDisabled(
-       VOID
-       )
+KiKernelApcDeliveryCheck(VOID)
 {
-       UNIMPLEMENTED;
+    /* We should only deliver at passive */
+    if (KeGetCurrentIrql() == PASSIVE_LEVEL)
+    {
+        /* Raise to APC and Deliver APCs, then lower back to Passive */
+        KfRaiseIrql(APC_LEVEL);
+        KiDeliverApc(KernelMode, 0, 0);
+        KfLowerIrql(PASSIVE_LEVEL);
+    }
+    else
+    {
+        /*
+         * If we're not at passive level it means someone raised IRQL
+         * to APC level before the a critical or guarded section was entered
+         * (e.g) by a fast mutex). This implies that the APCs shouldn't
+         * be delivered now, but after the IRQL is lowered to passive
+         * level again.
+         */
+        HalRequestSoftwareInterrupt(APC_LEVEL);
+    }
 }
 
-VOID KiRundownThread(VOID)
-/*
- * FUNCTION: 
- */
+/*++
+ * KeEnterCriticalRegion 
+ * @implemented NT4
+ *
+ *     The KeEnterCriticalRegion routine temporarily disables the delivery of 
+ *     normal kernel APCs; special kernel-mode APCs are still delivered.
+ *
+ * Params:
+ *     None.
+ *
+ * Returns:
+ *     None.
+ *
+ * Remarks:
+ *     Highest-level drivers can call this routine while running in the context
+ *     of the thread that requested the current I/O operation. Any caller of 
+ *     this routine should call KeLeaveCriticalRegion as quickly as possible.
+ *
+ *     Callers of KeEnterCriticalRegion must be running at IRQL <= APC_LEVEL.
+ *
+ *--*/
+#undef KeEnterCriticalRegion
+VOID 
+STDCALL 
+KeEnterCriticalRegion(VOID)
 {
+    /* Disable Kernel APCs */
+    PKTHREAD Thread = KeGetCurrentThread();
+    if (Thread) Thread->KernelApcDisable--;
 }
 
-BOOLEAN KiTestAlert(VOID)
-/*
- * FUNCTION: Tests whether there are any pending APCs for the current thread
- * and if so the APCs will be delivered on exit from kernel mode
- */
+/*++
+ * KeLeaveCriticalRegion 
+ * @implemented NT4
+ *
+ *     The KeLeaveCriticalRegion routine reenables the delivery of normal 
+ *     kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
+ *
+ * Params:
+ *     None.
+ *
+ * Returns:
+ *     None.
+ *
+ * Remarks:
+ *     Highest-level drivers can call this routine while running in the context
+ *     of the thread that requested the current I/O operation. 
+ *
+ *     Callers of KeLeaveCriticalRegion must be running at IRQL <= DISPATCH_LEVEL.
+ *
+ *--*/
+#undef KeLeaveCriticalRegion
+VOID 
+STDCALL 
+KeLeaveCriticalRegion (VOID)
 {
-   KIRQL oldIrql; 
-   
-   KeAcquireSpinLock(&PiApcLock, &oldIrql);
-   if (KeGetCurrentThread()->ApcState.UserApcPending == 0)
-     {
-       KeReleaseSpinLock(&PiApcLock, oldIrql);
-       return(FALSE);
-     }
-   KeGetCurrentThread()->Alerted[0] = 1;
-   KeReleaseSpinLock(&PiApcLock, oldIrql);
-   return(TRUE);
+    PKTHREAD Thread = KeGetCurrentThread(); 
+
+    /* Check if Kernel APCs are now enabled */
+    if((Thread) && (++Thread->KernelApcDisable == 0)) 
+    { 
+        /* Check if we need to request an APC Delivery */
+        if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) 
+        { 
+            /* Check for the right environment */
+            KiKernelApcDeliveryCheck();
+        } 
+    } 
 }
 
+/*++
+ * KeInitializeApc 
+ * @implemented NT4
+ *
+ *     The The KeInitializeApc routine initializes an APC object, and registers
+ *     the Kernel, Rundown and Normal routines for that object.
+ *
+ * Params:
+ *     Apc - Pointer to a KAPC structure that represents the APC object to 
+ *           initialize. The caller must allocate storage for the structure
+ *           from resident memory.
+ *
+ *     Thread - Thread to which to deliver the APC.
+ *
+ *     TargetEnvironment - APC Environment to be used.
+ *
+ *     KernelRoutine - Points to the KernelRoutine to associate with the APC.
+ *                     This routine is executed for all APCs.
+ *
+ *     RundownRoutine - Points to the RundownRoutine to associate with the APC.
+ *                      This routine is executed when the Thread exists with
+ *                      the APC executing.
+ *
+ *     NormalRoutine - Points to the NormalRoutine to associate with the APC.
+ *                     This routine is executed at PASSIVE_LEVEL. If this is
+ *                     not specifed, the APC becomes a Special APC and the
+ *                     Mode and Context parameters are ignored.
+ *
+ *     Mode - Specifies the processor mode at which to run the Normal Routine.
+ *
+ *     Context - Specifices the value to pass as Context parameter to the 
+ *               registered routines.
+ *
+ * Returns:
+ *     None.
+ *
+ * Remarks:
+ *     The caller can queue an initialized APC with KeInsertQueueApc.
+ *
+ *     Storage for the APC object must be resident, such as nonpaged pool 
+ *     allocated by the caller.
+ *
+ *     Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
+ *
+ *--*/
 VOID
-KiDeliverNormalApc(VOID)
+STDCALL
+KeInitializeApc(IN PKAPC Apc,
+                IN PKTHREAD Thread,
+                IN KAPC_ENVIRONMENT TargetEnvironment,
+                IN PKKERNEL_ROUTINE KernelRoutine,
+                IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
+                IN PKNORMAL_ROUTINE NormalRoutine,
+                IN KPROCESSOR_MODE Mode,
+                IN PVOID Context)
 {
-   PETHREAD Thread = PsGetCurrentThread();
-   PLIST_ENTRY current;
-   PKAPC Apc;
-   KIRQL oldlvl;
-   PKNORMAL_ROUTINE NormalRoutine;
-   PVOID NormalContext;
-   PVOID SystemArgument1;
-   PVOID SystemArgument2;
-
-   KeAcquireSpinLock(&PiApcLock, &oldlvl);
-   while(!IsListEmpty(&(Thread->Tcb.ApcState.ApcListHead[0])))
-     {
-       current = RemoveTailList(&Thread->Tcb.ApcState.ApcListHead[0]);
-       Apc = CONTAINING_RECORD(current, KAPC, ApcListEntry);
-       if (Apc->NormalRoutine == NULL)
-        {
-          DbgPrint("Exiting kernel with kernel APCs pending.\n");
-          KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT, (ULONG)Apc,
-                       Thread->Tcb.KernelApcDisable, oldlvl, 0);
-        }
-       Apc->Inserted = FALSE;
-       Thread->Tcb.ApcState.KernelApcInProgress++;
-       Thread->Tcb.ApcState.KernelApcPending--;
-       
-       KeReleaseSpinLock(&PiApcLock, oldlvl);
-       
-       NormalRoutine = Apc->NormalRoutine;
-       NormalContext = Apc->NormalContext;
-       SystemArgument1 = Apc->SystemArgument1;
-       SystemArgument2 = Apc->SystemArgument2;
-       Apc->KernelRoutine(Apc,
-                         &NormalRoutine,
-                         &NormalContext,
-                         &SystemArgument1,
-                         &SystemArgument2);
-       NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
-       
-       KeAcquireSpinLock(&PiApcLock, &oldlvl);
-       Thread->Tcb.ApcState.KernelApcInProgress--;
-     }
-   KeReleaseSpinLock(&PiApcLock, oldlvl);
+    DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, "
+           "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
+           "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine,
+            NormalRoutine,Mode,Context);
+
+    /* Set up the basic APC Structure Data */
+    RtlZeroMemory(Apc, sizeof(KAPC));
+    Apc->Type = ApcObject;
+    Apc->Size = sizeof(KAPC);
+    
+    /* Set the Environment */
+    if (TargetEnvironment == CurrentApcEnvironment) {
+        
+        Apc->ApcStateIndex = Thread->ApcStateIndex;
+        
+    } else {
+        
+        Apc->ApcStateIndex = TargetEnvironment;
+    }
+    
+    /* Set the Thread and Routines */
+    Apc->Thread = Thread;
+    Apc->KernelRoutine = KernelRoutine;
+    Apc->RundownRoutine = RundownRoutine;
+    Apc->NormalRoutine = NormalRoutine;
+   
+    /* Check if this is a Special APC, in which case we use KernelMode and no Context */
+    if (ARGUMENT_PRESENT(NormalRoutine)) {
+        
+        Apc->ApcMode = Mode;
+        Apc->NormalContext = Context;
+        
+    } else {
+        
+        Apc->ApcMode = KernelMode;
+    }  
 }
 
-BOOLEAN 
-KiDeliverUserApc(PKTRAP_FRAME TrapFrame)
-/*
- * FUNCTION: Tests whether there are any pending APCs for the current thread
- * and if so the APCs will be delivered on exit from kernel mode.
- * ARGUMENTS:
- *        Thread = Thread to test for alerts
- *        UserContext = The user context saved on entry to kernel mode
- */
+/*++
+ * KiInsertQueueApc 
+ *
+ *     The KiInsertQueueApc routine queues a APC for execution when the right
+ *     scheduler environment exists.
+ *
+ * Params:
+ *     Apc - Pointer to an initialized control object of type DPC for which the
+ *           caller provides the storage. 
+ *
+ *     PriorityBoost - Priority Boost to apply to the Thread.
+ *
+ * Returns:
+ *     If the APC is already inserted or APC queueing is disabled, FALSE.
+ *     Otherwise, TRUE.
+ *
+ * Remarks:
+ *     The APC will execute at APC_LEVEL for the KernelRoutine registered, and
+ *     at PASSIVE_LEVEL for the NormalRoutine registered.
+ *
+ *     Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
+ *
+ *--*/
+BOOLEAN
+STDCALL
+KiInsertQueueApc(PKAPC Apc,
+                 KPRIORITY PriorityBoost)
 {
-   PLIST_ENTRY current_entry;
-   PKAPC Apc;
-   PULONG Esp;
-   PCONTEXT Context;
-   KIRQL oldlvl;
-   PKTHREAD Thread;
-
-   DPRINT("KiDeliverUserApc(TrapFrame %x/%x)\n", TrapFrame,
-         KeGetCurrentThread()->TrapFrame);
-   Thread = KeGetCurrentThread();
-
-   /*
-    * Check for thread termination
+    PKTHREAD Thread = Apc->Thread;
+    PLIST_ENTRY ApcListEntry;
+    PKAPC QueuedApc;
+    
+    /* Don't do anything if the APC is already inserted */
+    if (Apc->Inserted) {
+        
+        return FALSE;
+    }
+    
+    /* Three scenarios: 
+       1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
+       2) User APC which is PsExitSpecialApc = Put it at the front of the List
+       3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
     */
+    if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) {
+        
+        DPRINT ("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);
+        
+        for (ApcListEntry = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode].Flink;
+             ApcListEntry != &Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode];
+             ApcListEntry = ApcListEntry->Flink) {
+
+            QueuedApc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
+            if (Apc->NormalRoutine != NULL) break;
+        }
+        
+        /* We found the first "Normal" APC, so write right before it */
+        ApcListEntry = ApcListEntry->Blink;
+        InsertHeadList(ApcListEntry, &Apc->ApcListEntry);
+        
+    } else {
+        
+        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);
+    }
+    
+    /* Confirm Insertion */    
+    Apc->Inserted = TRUE;
 
-   KeAcquireSpinLock(&PiApcLock, &oldlvl);
-
-   current_entry = Thread->ApcState.ApcListHead[1].Flink;
-   
-   /*
-    * Shouldn't happen but check anyway.
-    */
-   if (current_entry == &Thread->ApcState.ApcListHead[1])
-     {
-       KeReleaseSpinLock(&PiApcLock, oldlvl);
-       DbgPrint("KiDeliverUserApc called but no APC was pending\n");
-       return(FALSE);
-     }
-
-   while (!IsListEmpty(&Thread->ApcState.ApcListHead[1]))
-     {
-       current_entry = RemoveHeadList(&Thread->ApcState.ApcListHead[1]);
-       Apc = CONTAINING_RECORD(current_entry, KAPC, ApcListEntry);
-       Apc->Inserted = FALSE;
-
-       /*
-       * We've dealt with one pending user-mode APC
-       */
-       Thread->ApcState.UserApcPending--;
-       KeReleaseSpinLock(&PiApcLock, oldlvl);       
-       
-       /*
-       * Save the thread's current context (in other words the registers
-       * that will be restored when it returns to user mode) so the
-       * APC dispatcher can restore them later
-       */
-       Context = (PCONTEXT)(((PUCHAR)TrapFrame->Esp) - sizeof(CONTEXT));
-       memset(Context, 0, sizeof(CONTEXT));
-       Context->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | 
-        CONTEXT_SEGMENTS | CONTEXT_i386;
-       Context->SegGs = TrapFrame->Gs;
-       Context->SegFs = TrapFrame->Fs;
-       Context->SegEs = TrapFrame->Es;
-       Context->SegDs = TrapFrame->Ds;
-       Context->Edi = TrapFrame->Edi;
-       Context->Esi = TrapFrame->Esi;
-       Context->Ebx = TrapFrame->Ebx;
-       Context->Edx = TrapFrame->Edx;
-       Context->Ecx = TrapFrame->Ecx;
-       Context->Eax = TrapFrame->Eax;
-       Context->Ebp = TrapFrame->Ebp;
-       Context->Eip = TrapFrame->Eip;
-       Context->SegCs = TrapFrame->Cs;
-       Context->EFlags = TrapFrame->Eflags;
-       Context->Esp = TrapFrame->Esp;
-       Context->SegSs = TrapFrame->Ss;
-       
-       /*
-       * Setup the trap frame so the thread will start executing at the
-       * APC Dispatcher when it returns to user-mode
-       */
-       Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) - 
-                     (sizeof(CONTEXT) + (6 * sizeof(ULONG))));
-       
-       Esp[0] = 0xdeadbeef;
-       Esp[1] = (ULONG)Apc->NormalRoutine;
-       Esp[2] = (ULONG)Apc->NormalContext;
-       Esp[3] = (ULONG)Apc->SystemArgument1;
-       Esp[4] = (ULONG)Apc->SystemArgument2;
-       Esp[5] = (ULONG)Context;
-       TrapFrame->Eip = (ULONG)LdrpGetSystemDllApcDispatcher();
-       TrapFrame->Esp = (ULONG)Esp;     
-       
+    /*
+     * Three possibilites here again:
+     *  1) Kernel APC, The thread is Running: Request an Interrupt
+     *  2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled and not in progress: Unwait the Thread
+     *  3) User APC, Unwait the Thread if it is alertable
+     */ 
+    if (Apc->ApcMode == KernelMode) { 
+        
+        /* Set Kernel APC pending */
+        Thread->ApcState.KernelApcPending = TRUE;
+        
+        /* Check the Thread State */
+        if (Thread->State == Running) { 
+            
+            /* FIXME: Use IPI */
+            DPRINT ("Requesting APC Interrupt for Running Thread \n");
+            HalRequestSoftwareInterrupt(APC_LEVEL);
+            
+        } else if ((Thread->State == Waiting) && (Thread->WaitIrql == PASSIVE_LEVEL) &&
+                   ((Apc->NormalRoutine == NULL) || 
+                   ((!Thread->KernelApcDisable) && (!Thread->ApcState.KernelApcInProgress)))) {
+          
+            DPRINT("Waking up Thread for Kernel-Mode APC Delivery \n");
+            KiAbortWaitThread(Thread, STATUS_KERNEL_APC, PriorityBoost);
+        }
+        
+   } else if ((Thread->State == Waiting) && 
+              (Thread->WaitMode == UserMode) && 
+              (Thread->Alertable)) {
        
-       /*
-       * Now call for the kernel routine for the APC, which will free
-       * the APC data structure, we can't do this ourselves because
-       * the APC may be embedded in some larger structure e.g. an IRP
-       * We also give the kernel routine a last chance to modify the 
-       * arguments to the user APC routine.
-       */
-       Apc->KernelRoutine(Apc,
-                         (PKNORMAL_ROUTINE*)&Esp[1],
-                         (PVOID*)&Esp[2],
-                         (PVOID*)&Esp[3],
-                         (PVOID*)&Esp[4]);
-
-       KeAcquireSpinLock(&PiApcLock, &oldlvl);
-     }       
-   Thread->Alerted[0] = 0;
-   KeReleaseSpinLock(&PiApcLock, oldlvl);
-
-   return(TRUE);
-}
-
-/*
- * @implemented
- */
-VOID STDCALL 
-KiDeliverApc(ULONG Unknown1,
-            ULONG Unknown2,
-            ULONG Unknown3)
-/*
- * FUNCTION: Deliver an APC to the current thread.
- * NOTES: This is called from the IRQL switching code if the current thread
- * is returning from an IRQL greater than or equal to APC_LEVEL to 
- * PASSIVE_LEVEL and there are kernel-mode APCs pending. This means any
- * pending APCs will be delivered after a thread gets a new quantum and
- * after it wakes from a wait. 
- */
-{
-   PETHREAD Thread = PsGetCurrentThread();
-   PLIST_ENTRY current_entry;
-   PKAPC Apc;
-   KIRQL oldlvl;
-
-   DPRINT("KiDeliverApc()\n");
-   KeAcquireSpinLock(&PiApcLock, &oldlvl);
-   current_entry = Thread->Tcb.ApcState.ApcListHead[0].Flink;
-   while(current_entry != &Thread->Tcb.ApcState.ApcListHead[0])
-     {
-       Apc = CONTAINING_RECORD(current_entry, KAPC, ApcListEntry);
-       if (Apc->NormalRoutine == NULL)
-        {
-          Apc->Inserted = FALSE;
-          RemoveEntryList(&Apc->ApcListEntry);
-          Thread->Tcb.ApcState.KernelApcInProgress++;
-          Thread->Tcb.ApcState.KernelApcPending--;
-          
-          KeReleaseSpinLock(&PiApcLock, oldlvl);
-          
-          Apc->KernelRoutine(Apc,
-                             &Apc->NormalRoutine,
-                             &Apc->NormalContext,
-                             &Apc->SystemArgument1,
-                             &Apc->SystemArgument2);
-          
-          KeAcquireSpinLock(&PiApcLock, &oldlvl);
-          Thread->Tcb.ApcState.KernelApcInProgress--;
-          current_entry = Thread->Tcb.ApcState.ApcListHead[0].Flink;
-        }
-       else
-        {
-          current_entry = current_entry->Flink;
-        }
-     }
-   KeReleaseSpinLock(&PiApcLock, oldlvl);
+        DPRINT("Waking up Thread for User-Mode APC Delivery \n");
+        Thread->ApcState.UserApcPending = TRUE;
+        KiAbortWaitThread(Thread, STATUS_USER_APC, PriorityBoost);
+    }
+    
+    return TRUE;
 }
+    
+/*++
+ * KeInsertQueueApc 
+ * @implemented NT4
+ *
+ *     The KeInsertQueueApc routine queues a APC for execution when the right
+ *     scheduler environment exists.
+ *
+ * Params:
+ *     Apc - Pointer to an initialized control object of type DPC for which the
+ *           caller provides the storage. 
+ *
+ *     SystemArgument[1,2] - Pointer to a set of two parameters that contain
+ *                           untyped data.
+ *
+ *     PriorityBoost - Priority Boost to apply to the Thread.
+ *
+ * Returns:
+ *     If the APC is already inserted or APC queueing is disabled, FALSE.
+ *     Otherwise, TRUE.
+ *
+ * Remarks:
+ *     The APC will execute at APC_LEVEL for the KernelRoutine registered, and
+ *     at PASSIVE_LEVEL for the NormalRoutine registered.
+ *
+ *     Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
+ *
+ *--*/
+BOOLEAN
+STDCALL
+KeInsertQueueApc(PKAPC Apc,
+                 PVOID SystemArgument1,
+                 PVOID SystemArgument2,
+                 KPRIORITY PriorityBoost)
 
-/*
- * @implemented
- */
-BOOLEAN STDCALL
-KeInsertQueueApc (PKAPC        Apc,
-                 PVOID SystemArgument1,
-                 PVOID SystemArgument2,
-                 KPRIORITY PriorityBoost)
-/*
- * FUNCTION: Queues an APC for execution
- * ARGUMENTS:
- *         Apc = APC to be queued
- *         SystemArgument[1-2] = TBD
- *         Mode = TBD
- */
 {
-   //FIXME: return FALSE if APC can't be queued to target thread (thread has ended)
-   KIRQL oldlvl;
-   PKTHREAD TargetThread;
+    KIRQL OldIrql;
+    PKTHREAD Thread;
+    BOOLEAN Inserted;
    
-   DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
-         "SystemArgument2 %x)\n",Apc,SystemArgument1,
-         SystemArgument2);
-
-   Apc->SystemArgument1 = SystemArgument1;
-   Apc->SystemArgument2 = SystemArgument2;
-
-   if (Apc->Inserted)
-     {
-#if 0
-       /* TMN: This code is in error */
-       DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
-       KEBUGCHECK(0);
-#else
-       return FALSE;
-#endif
-     }
-
-   KeAcquireSpinLock(&PiApcLock, &oldlvl);
-
-   TargetThread = Apc->Thread;
-
-   if (TargetThread->State == THREAD_STATE_TERMINATED_1 ||
-       TargetThread->State == THREAD_STATE_TERMINATED_2)
-     {
-       KeReleaseSpinLock(&PiApcLock, oldlvl);
-       return(FALSE);
-     }
-
-   if (Apc->ApcMode == KernelMode)
-     {
-       InsertTailList(&TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->ApcListHead[0], 
-                      &Apc->ApcListEntry);
-       TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->KernelApcPending++;
-     }
-   else
-     {
-       InsertTailList(&TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->ApcListHead[1],
-                      &Apc->ApcListEntry);
-       TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->UserApcPending++;
-     }
-   Apc->Inserted = TRUE;
-
-   /*
-    * If this is a kernel-mode APC for the current thread and we are not
-    * inside a critical section or at APC level then call it, in fact we
-    * rely on the side effects of dropping the IRQL level when we release
-    * the spinlock
-    */
-   if (Apc->ApcMode == KernelMode && TargetThread == KeGetCurrentThread() && 
-       Apc->NormalRoutine == NULL)
-     {
-       KeReleaseSpinLock(&PiApcLock, oldlvl);
-       return TRUE;
-     }
-
-   /*
-    * If this is a kernel-mode APC and it is waiting at PASSIVE_LEVEL and
-    * not inside a critical section then wake it up. Otherwise it will
-    * execute the APC as soon as it returns to PASSIVE_LEVEL.
-    * FIXME: If the thread is running on another processor then send an
-    * IPI.
-    * FIXME: Check if the thread is terminating.
-    */
-   if (Apc->ApcMode == KernelMode && TargetThread->WaitIrql < APC_LEVEL && 
-       Apc->NormalRoutine == NULL)
-     {
-       KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
-                              STATUS_KERNEL_APC, TRUE);
-     }
-
-   /*
-    * If this is a 'funny' user-mode APC then mark the thread as
-    * alerted so it will execute the APC on exit from kernel mode. If it
-    * is waiting alertably then wake it up so it can return to user mode.
-    */
-   if (Apc->ApcMode == KernelMode && Apc->NormalRoutine != NULL)
-     {
-       TargetThread->Alerted[1] = 1;
-       if (TargetThread->Alertable == TRUE &&
-          TargetThread->WaitMode == UserMode)
-        {
-          PETHREAD Thread;
-
-          Thread = CONTAINING_RECORD(TargetThread, ETHREAD, Tcb);
-          KeRemoveAllWaitsThread(Thread, STATUS_USER_APC, TRUE);
-        }
-     }
-
-   /*
-    * If the thread is waiting alertably then wake it up and it will
-    * return to to user-mode executing the APC in the process. Otherwise the
-    * thread will execute the APC next time it enters an alertable wait.
-    */
-   if (Apc->ApcMode == UserMode && TargetThread->Alertable == TRUE &&
-       TargetThread->WaitMode == UserMode)
-     {
-       NTSTATUS Status;
-       
-       DPRINT("Resuming thread for user APC\n");
-       
-       Status = STATUS_USER_APC;
-       TargetThread->Alerted[0] = 1;
-       KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
-                              STATUS_USER_APC, TRUE);
-     }
-   KeReleaseSpinLock(&PiApcLock, oldlvl);
-   return TRUE;
-}
+    ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
+    DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
+           "SystemArgument2 %x)\n",Apc,SystemArgument1,
+            SystemArgument2);
 
-BOOLEAN STDCALL
-KeRemoveQueueApc (PKAPC Apc)
-/*
- * FUNCTION: Removes APC object from the apc queue
- * ARGUMENTS:
- *          Apc = APC to remove
- * RETURNS: TRUE if the APC was in the queue
- *          FALSE otherwise
- * NOTE: This function is not exported.
- */
-{
-   KIRQL oldIrql;
-   PKTHREAD TargetThread;
-
-   KeRaiseIrql(HIGH_LEVEL, &oldIrql);
-   KiAcquireSpinLock(&PiApcLock);
-   if (Apc->Inserted == FALSE)
-     {
-        KiReleaseSpinLock(&PiApcLock);
-        KeLowerIrql(oldIrql);
+    /* Lock the Dispatcher Database */
+    OldIrql = KeAcquireDispatcherDatabaseLock();
+    
+    /* Get the Thread specified in the APC */
+    Thread = Apc->Thread;
+       
+    /* Make sure the thread allows APC Queues.
+    * The thread is not apc queueable, for instance, when it's (about to be) terminated.
+    */
+    if (Thread->ApcQueueable == FALSE) {
+        DPRINT("Thread doesn't allow APC Queues\n");
+        KeReleaseDispatcherDatabaseLock(OldIrql);
         return FALSE;
-     }
-
-   TargetThread = Apc->Thread;
-   RemoveEntryList(&Apc->ApcListEntry);
-   if (Apc->ApcMode == KernelMode)
-     {
-       TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->KernelApcPending--;
-     }
-   else
-     {
-       TargetThread->ApcStatePointer[(int) Apc->ApcStateIndex]->UserApcPending--;
-     }
-   Apc->Inserted = FALSE;
-
-   KiReleaseSpinLock(&PiApcLock);
-   KeLowerIrql(oldIrql);   
-   return(TRUE);
-}
+    }
+    
+    /* Set the System Arguments */
+    Apc->SystemArgument1 = SystemArgument1;
+    Apc->SystemArgument2 = SystemArgument2;
 
+    /* Call the Internal Function */
+    Inserted = KiInsertQueueApc(Apc, PriorityBoost);
 
-/*
- * @implemented
- */
-VOID STDCALL
-KeInitializeApc(
-  IN PKAPC  Apc,
-       IN PKTHREAD  Thread,
-  IN KAPC_ENVIRONMENT  TargetEnvironment,
-       IN PKKERNEL_ROUTINE  KernelRoutine,
-       IN PKRUNDOWN_ROUTINE  RundownRoutine OPTIONAL,
-       IN PKNORMAL_ROUTINE  NormalRoutine,
-       IN KPROCESSOR_MODE  Mode,
-       IN PVOID  Context)
-/*
- * FUNCTION: Initialize an APC object
- * ARGUMENTS:
- *       Apc = Pointer to the APC object to initialized
- *       Thread = Thread the APC is to be delivered to
- *       StateIndex = KAPC_ENVIRONMENT
- *       KernelRoutine = Routine to be called for a kernel-mode APC
- *       RundownRoutine = Routine to be called if the thread has exited with
- *                        the APC being executed
- *       NormalRoutine = Routine to be called for a user-mode APC
- *       Mode = APC mode
- *       Context = Parameter to be passed to the APC routine
- */
+    /* Return Sucess if we are here */
+    KeReleaseDispatcherDatabaseLock(OldIrql);
+    return Inserted;
+}
+
+/*++
+ * KeRemoveQueueApc 
+ *
+ *     The KeRemoveQueueApc routine removes a given APC object from the system 
+ *     APC queue.
+ *
+ * Params:
+ *     APC - Pointer to an initialized APC object that was queued by calling
+ *           KeInsertQueueApc.
+ *
+ * Returns:
+ *     TRUE if the APC Object is in the APC Queue. If it isn't, no operation is
+ *     performed and FALSE is returned.
+ *
+ * Remarks:
+ *     If the given APC Object is currently queued, it is removed from the queue
+ *     and any calls to the registered routines are cancelled.
+ *
+ *     Callers of KeLeaveCriticalRegion can be running at any IRQL.
+ *
+ *--*/
+BOOLEAN 
+STDCALL
+KeRemoveQueueApc(PKAPC Apc)
 {
-   DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, "
-         "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
-    "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine,
-         NormalRoutine,Mode,Context);
-
-   memset(Apc, 0, sizeof(KAPC));
-   Apc->Thread = Thread;
-   Apc->ApcListEntry.Flink = NULL;
-   Apc->ApcListEntry.Blink = NULL;
-   Apc->KernelRoutine = KernelRoutine;
-   Apc->RundownRoutine = RundownRoutine;
-   Apc->NormalRoutine = NormalRoutine;
-   Apc->NormalContext = Context;
-   Apc->Inserted = FALSE;
-
-   if (TargetEnvironment == CurrentApcEnvironment)
-   {
-      Apc->ApcStateIndex = Thread->ApcStateIndex;
-   }
-   else
-   {
-      Apc->ApcStateIndex = TargetEnvironment;   
-   }
-
-   if (Apc->NormalRoutine != NULL)
-     {
-       Apc->ApcMode = Mode;
-     }
-   else
-     {
-       Apc->ApcMode = KernelMode;
-     }
+    KIRQL OldIrql;
+    PKTHREAD Thread = Apc->Thread;
+
+    ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
+    DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc);
+    
+    OldIrql = KeAcquireDispatcherDatabaseLock();
+    KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
+    
+    /* Check if it's inserted */
+    if (Apc->Inserted) {
+        
+        /* Remove it from the Queue*/
+        RemoveEntryList(&Apc->ApcListEntry);
+        Apc->Inserted = FALSE;
+        
+        /* If the Queue is completely empty, then no more APCs are pending */
+        if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode])) {
+            
+            /* Set the correct State based on the Apc Mode */
+            if (Apc->ApcMode == KernelMode) {
+                
+                Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->KernelApcPending = FALSE;
+                
+            } else {
+                
+                Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = FALSE;
+            }
+        }
+        
+    } else {
+        
+        /* It's not inserted, fail */
+        KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
+        KeReleaseDispatcherDatabaseLock(OldIrql);
+        return(FALSE);
+    }
+    
+    /* Restore IRQL and Return */
+    KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
+    KeReleaseDispatcherDatabaseLock(OldIrql);
+    return(TRUE);
 }
 
-VOID STDCALL
-NtQueueApcRundownRoutine(PKAPC Apc)
+/*++
+ * KiDeliverApc 
+ * @implemented @NT4
+ *    
+ *     The KiDeliverApc routine is called from IRQL switching code if the
+ *     thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
+ *     pending. 
+ *
+ * Params:
+ *     DeliveryMode - Specifies the current processor mode.
+ *
+ *     Reserved - Pointer to the Exception Frame on non-i386 builds.
+ *
+ *     TrapFrame - Pointer to the Trap Frame.
+ *
+ * Returns:
+ *     None.
+ *
+ * Remarks:
+ *     First, Special APCs are delivered, followed by Kernel-Mode APCs and
+ *     User-Mode APCs. Note that the TrapFrame is only valid if the previous
+ *     mode is User.
+ *
+ *     Upon entry, this routine executes at APC_LEVEL.
+ *
+ *--*/
+VOID 
+STDCALL
+KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
+             PVOID Reserved,
+             PKTRAP_FRAME TrapFrame)
 {
-   ExFreePool(Apc);
+    PKTHREAD Thread = KeGetCurrentThread();
+    PLIST_ENTRY ApcListEntry;
+    PKAPC Apc;
+    KIRQL OldIrql;
+    PKKERNEL_ROUTINE KernelRoutine;
+    PVOID NormalContext;
+    PKNORMAL_ROUTINE NormalRoutine;
+    PVOID SystemArgument1;
+    PVOID SystemArgument2;
+   
+    ASSERT_IRQL_EQUAL(APC_LEVEL);
+
+    /* Lock the APC Queue and Raise IRQL to Synch */
+    KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
+
+    /* Clear APC Pending */
+    Thread->ApcState.KernelApcPending = FALSE;
+    
+    /* Do the Kernel APCs first */
+    while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) {
+    
+        /* Get the next Entry */
+        ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
+        Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
+        
+        /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
+        NormalRoutine   = Apc->NormalRoutine;
+        KernelRoutine   = Apc->KernelRoutine;
+        NormalContext   = Apc->NormalContext;
+        SystemArgument1 = Apc->SystemArgument1;
+        SystemArgument2 = Apc->SystemArgument2;
+       
+        /* Special APC */
+       if (NormalRoutine == NULL) {
+           
+            /* Remove the APC from the list */
+            Apc->Inserted = FALSE;
+            RemoveEntryList(ApcListEntry);
+            
+            /* Go back to APC_LEVEL */
+            KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
+            
+            /* Call the Special APC */
+            DPRINT("Delivering a Special APC: %x\n", Apc);
+            KernelRoutine(Apc,
+                      &NormalRoutine,
+                      &NormalContext,
+                      &SystemArgument1,
+                      &SystemArgument2);
+
+            /* Raise IRQL and Lock again */
+            KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
+            
+        } else {
+            
+             /* Normal Kernel APC */
+            if (Thread->ApcState.KernelApcInProgress || Thread->KernelApcDisable) {
+            
+                /*
+                 * DeliveryMode must be KernelMode in this case, since one may not
+                 * return to umode while being inside a critical section or while 
+                 * a regular kmode apc is running (the latter should be impossible btw).
+                 * -Gunnar
+                 */
+                ASSERT(DeliveryMode == KernelMode);
+
+                KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
+                return;
+            }
+            
+            /* Dequeue the APC */
+            RemoveEntryList(ApcListEntry);
+            Apc->Inserted = FALSE;
+            
+            /* Go back to APC_LEVEL */
+            KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
+            
+            /* Call the Kernel APC */
+            DPRINT("Delivering a Normal APC: %x\n", Apc);
+            KernelRoutine(Apc,
+                      &NormalRoutine,
+                      &NormalContext,
+                      &SystemArgument1,
+                      &SystemArgument2);
+            
+            /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
+            if (NormalRoutine != NULL) {
+                
+                /* At Passive Level, this APC can be prempted by a Special APC */
+                Thread->ApcState.KernelApcInProgress = TRUE;
+                KeLowerIrql(PASSIVE_LEVEL);
+                
+                /* Call and Raise IRQ back to APC_LEVEL */
+                DPRINT("Calling the Normal Routine for a Normal APC: %x\n", Apc);
+                NormalRoutine(&NormalContext, &SystemArgument1, &SystemArgument2);
+                KeRaiseIrql(APC_LEVEL, &OldIrql);
+            }
+
+            /* Raise IRQL and Lock again */
+            KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
+            Thread->ApcState.KernelApcInProgress = FALSE;
+        }
+    }
+    
+    /* Now we do the User APCs */
+    if ((!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
+        (DeliveryMode == UserMode) && (Thread->ApcState.UserApcPending == TRUE)) {
+             
+        /* It's not pending anymore */
+        Thread->ApcState.UserApcPending = FALSE;
+
+        /* Get the APC Object */
+        ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
+        Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
+        
+        /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
+        NormalRoutine   = Apc->NormalRoutine;
+        KernelRoutine   = Apc->KernelRoutine;
+        NormalContext   = Apc->NormalContext;
+        SystemArgument1 = Apc->SystemArgument1;
+        SystemArgument2 = Apc->SystemArgument2; 
+        
+        /* Remove the APC from Queue, restore IRQL and call the APC */
+        RemoveEntryList(ApcListEntry);
+        Apc->Inserted = FALSE;
+      
+        KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
+        DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc);
+        KernelRoutine(Apc,
+                  &NormalRoutine,
+                  &NormalContext,
+                  &SystemArgument1,
+                  &SystemArgument2);
+
+        if (NormalRoutine == NULL) {
+            
+            /* Check if more User APCs are Pending */
+            KeTestAlertThread(UserMode);
+            
+        } else {
+            
+            /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
+            DPRINT("Delivering a User APC: %x\n", Apc);
+            KiInitializeUserApc(Reserved, 
+                        TrapFrame,
+                        NormalRoutine,
+                        NormalContext,
+                        SystemArgument1,
+                        SystemArgument2);
+        }
+        
+    } else {
+        
+        /* Go back to APC_LEVEL */
+        KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
+    }
 }
 
-VOID STDCALL
-NtQueueApcKernelRoutine(PKAPC Apc,
-                       PKNORMAL_ROUTINE* NormalRoutine,
-                       PVOID* NormalContext,
-                       PVOID* SystemArgument1,
-                       PVOID* SystemArgument2)
+VOID 
+STDCALL
+KiFreeApcRoutine(PKAPC Apc,
+                 PKNORMAL_ROUTINE* NormalRoutine,
+                 PVOID* NormalContext,
+                 PVOID* SystemArgument1,
+                 PVOID* SystemArgument2)
 {
-   ExFreePool(Apc);
+    /* Free the APC and do nothing else */
+    ExFreePool(Apc);
 }
 
-NTSTATUS STDCALL
-NtQueueApcThread(HANDLE                        ThreadHandle,
-                PKNORMAL_ROUTINE       ApcRoutine,
-                PVOID                  NormalContext,
-                PVOID                  SystemArgument1,
-                PVOID                  SystemArgument2)
+/*++
+ * KiInitializeUserApc 
+ *    
+ *     Prepares the Context for a User-Mode APC called through NTDLL.DLL
+ *
+ * Params:
+ *     Reserved - Pointer to the Exception Frame on non-i386 builds.
+ *
+ *     TrapFrame - Pointer to the Trap Frame.
+ *
+ *     NormalRoutine - Pointer to the NormalRoutine to call.
+ *
+ *     NormalContext - Pointer to the context to send to the Normal Routine.
+ *
+ *     SystemArgument[1-2] - Pointer to a set of two parameters that contain
+ *                           untyped data.
+ *
+ * Returns:
+ *     None.
+ *
+ * Remarks:
+ *     None.
+ *
+ *--*/
+VOID
+STDCALL
+KiInitializeUserApc(IN PVOID Reserved,
+                    IN PKTRAP_FRAME TrapFrame,
+                    IN PKNORMAL_ROUTINE NormalRoutine,
+                    IN PVOID NormalContext,
+                    IN PVOID SystemArgument1,
+                    IN PVOID SystemArgument2)  
 {
-   PKAPC Apc;
-   PETHREAD Thread;
-   NTSTATUS Status;
-   PVOID ThreadVar;
+    PCONTEXT Context;
+    PULONG Esp;
 
-   ThreadVar = &Thread;
-   
-   Status = ObReferenceObjectByHandle(ThreadHandle,
-                                     THREAD_ALL_ACCESS, /* FIXME */
-                                     PsThreadType,
-                                     UserMode,
-                                     &ThreadVar,
-                                     NULL);
-   if (!NT_SUCCESS(Status))
-     {
-       return(Status);
-     }
-   
-   Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_KAPC);
-   if (Apc == NULL)
-     {
-       ObDereferenceObject(Thread);
-       return(STATUS_NO_MEMORY);
-     }
-   
-   KeInitializeApc(Apc,
-                  &Thread->Tcb,
-         OriginalApcEnvironment,
-                  NtQueueApcKernelRoutine,
-                  NtQueueApcRundownRoutine,
-                  ApcRoutine,
-                  UserMode,
-                  NormalContext);
-   KeInsertQueueApc(Apc,
-                   SystemArgument1,
-                   SystemArgument2,
-                   IO_NO_INCREMENT);
+    DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame, KeGetCurrentThread()->TrapFrame);
    
-   ObDereferenceObject(Thread);
-   return(STATUS_SUCCESS);
+    /*
+     * Save the thread's current context (in other words the registers
+     * that will be restored when it returns to user mode) so the
+     * APC dispatcher can restore them later
+     */
+    Context = (PCONTEXT)(((PUCHAR)TrapFrame->Esp) - sizeof(CONTEXT));
+    RtlZeroMemory(Context, sizeof(CONTEXT));
+    Context->ContextFlags = CONTEXT_FULL;
+    Context->SegGs = TrapFrame->Gs;
+    Context->SegFs = TrapFrame->Fs;
+    Context->SegEs = TrapFrame->Es;
+    Context->SegDs = TrapFrame->Ds;
+    Context->Edi = TrapFrame->Edi;
+    Context->Esi = TrapFrame->Esi;
+    Context->Ebx = TrapFrame->Ebx;
+    Context->Edx = TrapFrame->Edx;
+    Context->Ecx = TrapFrame->Ecx;
+    Context->Eax = TrapFrame->Eax;
+    Context->Ebp = TrapFrame->Ebp;
+    Context->Eip = TrapFrame->Eip;
+    Context->SegCs = TrapFrame->Cs;
+    Context->EFlags = TrapFrame->Eflags;
+    Context->Esp = TrapFrame->Esp;
+    Context->SegSs = TrapFrame->Ss;
+       
+    /*
+     * Setup the trap frame so the thread will start executing at the
+     * APC Dispatcher when it returns to user-mode
+     */
+    Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) - (sizeof(CONTEXT) + (6 * sizeof(ULONG))));
+    Esp[0] = 0xdeadbeef;
+    Esp[1] = (ULONG)NormalRoutine;
+    Esp[2] = (ULONG)NormalContext;
+    Esp[3] = (ULONG)SystemArgument1;
+    Esp[4] = (ULONG)SystemArgument2;
+    Esp[5] = (ULONG)Context;
+    TrapFrame->Eip = (ULONG)LdrpGetSystemDllApcDispatcher();
+    DPRINT("TrapFrame->Eip: %x\n", TrapFrame->Eip);
+    TrapFrame->Esp = (ULONG)Esp;
 }
 
-
-NTSTATUS STDCALL NtTestAlert(VOID)
+/*++
+ * KeAreApcsDisabled 
+ * @implemented NT4
+ *    
+ *     Prepares the Context for a User-Mode APC called through NTDLL.DLL
+ *
+ * Params:
+ *     None.
+ *
+ * Returns:
+ *     KeAreApcsDisabled returns TRUE if the thread is within a critical region
+ *     or a guarded region, and FALSE otherwise.
+ *
+ * Remarks:
+ *     A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled to 
+ *     determine if normal kernel APCs are disabled. A thread that is inside a 
+ *     critical region has both user APCs and normal kernel APCs disabled, but 
+ *     not special kernel APCs. A thread that is inside a guarded region has 
+ *     all APCs disabled, including special kernel APCs.
+ *
+ *     Callers of this routine must be running at IRQL <= APC_LEVEL.
+ *
+ *--*/
+BOOLEAN
+STDCALL
+KeAreApcsDisabled(VOID)
 {
-   KiTestAlert();
-   return(STATUS_SUCCESS);
+    /* Return the Kernel APC State */
+    return KeGetCurrentThread()->KernelApcDisable ? TRUE : FALSE;
 }
 
-VOID INIT_FUNCTION
-PiInitApcManagement(VOID)
+/*++
+ * NtQueueApcThread 
+ * NT4
+ *    
+ *    This routine is used to queue an APC from user-mode for the specified 
+ *    thread.
+ *
+ * Params:
+ *     Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
+ *
+ *     ApcRoutine - Pointer to the APC Routine to call when the APC executes.
+ *
+ *     NormalContext - Pointer to the context to send to the Normal Routine.
+ *
+ *     SystemArgument[1-2] - Pointer to a set of two parameters that contain
+ *                           untyped data.
+ *
+ * Returns:
+ *     STATUS_SUCCESS or failure cute from associated calls.
+ *
+ * Remarks:
+ *      The thread must enter an alertable wait before the APC will be 
+ *      delivered.
+ *
+ *--*/
+NTSTATUS 
+STDCALL
+NtQueueApcThread(HANDLE ThreadHandle,
+         PKNORMAL_ROUTINE ApcRoutine,
+         PVOID NormalContext,
+         PVOID SystemArgument1,
+         PVOID SystemArgument2)
 {
-   KeInitializeSpinLock(&PiApcLock);
+    PKAPC Apc;
+    PETHREAD Thread;
+    KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+    NTSTATUS Status;
+
+    /* Get ETHREAD from Handle */
+    Status = ObReferenceObjectByHandle(ThreadHandle,
+                       THREAD_SET_CONTEXT,
+                       PsThreadType,
+                       PreviousMode,
+                       (PVOID)&Thread,
+                       NULL);
+    
+    /* Fail if the Handle is invalid for some reason */
+    if (!NT_SUCCESS(Status)) {
+        
+        return(Status);
+    }
+    
+    /* If this is a Kernel or System Thread, then fail */
+    if (Thread->Tcb.Teb == NULL) {
+        
+        ObDereferenceObject(Thread);
+        return STATUS_INVALID_HANDLE;
+    }
+   
+    /* Allocate an APC */
+    Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('P', 's', 'a', 'p'));
+    if (Apc == NULL) {
+        
+        ObDereferenceObject(Thread);
+        return(STATUS_NO_MEMORY);
+    }
+   
+    /* Initialize and Queue a user mode apc (always!) */
+    KeInitializeApc(Apc,
+                    &Thread->Tcb,
+                    OriginalApcEnvironment,
+                    KiFreeApcRoutine,
+                    NULL,
+                    ApcRoutine,
+                    UserMode,
+                    NormalContext);
+    
+    if (!KeInsertQueueApc(Apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) {
+        
+        Status = STATUS_UNSUCCESSFUL;
+        
+    } else {
+        
+        Status = STATUS_SUCCESS;
+    }
+   
+    /* Dereference Thread and Return */
+    ObDereferenceObject(Thread);
+    return Status;
 }
 
-static VOID FASTCALL
-RepairList(PLIST_ENTRY Original, PLIST_ENTRY Copy, int Mode)
+static inline 
+VOID RepairList(PLIST_ENTRY Original, 
+                PLIST_ENTRY Copy,
+                KPROCESSOR_MODE Mode)
 {
-  if (IsListEmpty(&Original[Mode]))
-    {
-      InitializeListHead(&Copy[Mode]);
-    }
-  else
-    {
-      Copy[Mode].Flink->Blink = &Copy[Mode];
-      Copy[Mode].Blink->Flink = &Copy[Mode];
+    /* Copy Source to Desination */
+    if (IsListEmpty(&Original[(int)Mode])) {
+        
+        InitializeListHead(&Copy[(int)Mode]);
+        
+    } else {
+        
+        Copy[(int)Mode].Flink = Original[(int)Mode].Flink; 
+        Copy[(int)Mode].Blink = Original[(int)Mode].Blink;
+        Original[(int)Mode].Flink->Blink = &Copy[(int)Mode];
+        Original[(int)Mode].Blink->Flink = &Copy[(int)Mode];
     }
 }
 
-
-VOID FASTCALL
-KiSwapApcEnvironment(
-  PKTHREAD Thread,
-  PKPROCESS NewProcess)
+VOID
+STDCALL
+KiMoveApcState(PKAPC_STATE OldState,
+               PKAPC_STATE NewState)
 {
-  /* FIXME: Grab the process apc lock or the PiApcLock? And why does both exist? */
-
-  if (Thread->ApcStateIndex == AttachedApcEnvironment)
-  {
-    /* NewProcess must be the same as in the original-environment */
-    assert(NewProcess == Thread->ApcStatePointer[OriginalApcEnvironment]->Process);
-
-    /*    
-    FIXME: Deliver any pending apc's queued to the attached environment before 
-    switching back to the original environment.
-    This is not crucial thou, since i don't think we'll ever target apc's
-    to attached environments (or?)...   
-    Remove the following asserts if implementing this.
-    -Gunnar
-    */
-
-    /* we don't support targeting apc's at attached-environments (yet)... */
-    assert(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]));
-    assert(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]));
-    assert(Thread->ApcState.KernelApcInProgress == FALSE);
-    assert(Thread->ApcState.KernelApcPending == FALSE);
-    assert(Thread->ApcState.UserApcPending == FALSE);
+    /* Restore backup of Original Environment */
+    *NewState = *OldState;
     
-    /* restore backup of original environment */
-    Thread->ApcState = Thread->SavedApcState;
-    /* repair lists */
-    RepairList(Thread->SavedApcState.ApcListHead, Thread->ApcState.ApcListHead,
-               KernelMode);
-    RepairList(Thread->SavedApcState.ApcListHead, Thread->ApcState.ApcListHead,
-               UserMode);
-
-    /* update environment pointers */
-    Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
-    Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
-
-    /* update current-environment index */
-    Thread->ApcStateIndex = OriginalApcEnvironment;
-  }
-  else if (Thread->ApcStateIndex == OriginalApcEnvironment)
-  {
-    /* backup original environment */
-    Thread->SavedApcState = Thread->ApcState;
-    /* repair lists */
-    RepairList(Thread->ApcState.ApcListHead, Thread->SavedApcState.ApcListHead,
-               KernelMode);
-    RepairList(Thread->ApcState.ApcListHead, Thread->SavedApcState.ApcListHead,
-               UserMode);
-
-    /*
-    FIXME: Is it possible to target an apc to an attached environment even if the 
-    thread is not currently attached???? If so, then this is bougus since it 
-    reinitializes the attached apc environment then located in SavedApcState.
-    -Gunnar
-    */
-
-    /* setup a fresh new attached environment */
-    InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
-    InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
-    Thread->ApcState.Process = NewProcess;
-    Thread->ApcState.KernelApcInProgress = FALSE;
-    Thread->ApcState.KernelApcPending = FALSE;
-    Thread->ApcState.UserApcPending = FALSE;
-
-    /* update environment pointers */
-    Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->SavedApcState;
-    Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState;
-
-    /* update current-environment index */
-    Thread->ApcStateIndex = AttachedApcEnvironment;
-  }
-  else
-  {
-    /* FIXME: Is this the correct bug code? */
-    KEBUGCHECK(APC_INDEX_MISMATCH);
-  }
+    /* Repair Lists */
+    RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode);
+    RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode);
 }