FREE Subscription to Dr. Dobb’s Digest: Same Great Content, New Digital Edition
Site Archive (Complete)
Dobbs M-Dev
Email
Print
Reprint

add to:
Del.icio.us
Digg
Google
Furl
Slashdot
Y! MyWeb
Blink
November 01, 2002

Inside NT's Asynchronous Procedure Call

(Page 7 of 7)
November 2002/Inside NT's Asynchronous Procedure Call

Listing 6 The pseudocode for the NT APC dispatcher subroutine KiDeliverApc


//=========================================================================
//
// KiDeliverApc
//
// Copyright (C) 2002 Albert Almeida
//
// Pseudo-code for the NT/2000's APC dispatcher subroutine KiDeliverApc
// in Ntoskrnl.exe.
// 
// The kernel always executes this subroutine at APC Level.
// Reserved is always passed as NULL.
// 
//=========================================================================

VOID KiDeliverApc(KPROCESSOR_MODE PreviousMode,
                  PVOID Reserved,
                  PKTRAP_FRAME TrapFrame)
{
        //
        // Get a pointer to the currently executing thread from the 
        // Processor Region Control Block (PRCB)
        //
        PKTHREAD CurrentThread = PCR->CurrentThread;

        //
        // Raises the processor's IRQL to Clock2 Level
        //
        OldIrql = _KeRaiseIrqlToSynchLevel();

        CurrentThread->ApcState.KernelApcPending = FALSE;

        //
        // If kernel-mode APC list is empty then check user-mode APC list
        //
        if (!IsListEmpty(&CurrentThread->ApcState.ApcListhead[KernelMode]))
        {
                do 
                {
                        //
                        // Get the APC object at the list head
                        //
                        entry = CurrentThread->ApcState.ApcListHead [KernelMode].Flink;

                        Apc = CONTAINING_RECORD(entry, KAPC, ApcListEntry);

                        //
                        // Save the APC attributes in local variables so that  		   
                        // it's safe to free
                        // the APC object in KernelRoutine
                        //
                        NormalRoutine   = Apc->NormalRoutine;
                        KernelRoutine   = Apc->KernelRoutine;
                        NormalContext   = Apc->NormalContext;
                        SystemArgument1 = Apc->SystemArgument1;
                        SystemArgument2 = Apc->SystemArgument2;

                        //
                        // If NormalRoutine is NULL this is a special kernel-mode APC
                        //
                        if (NormalRoutine == NULL)
                        {       
                                //
                                // Dequeue the APC object from the list head
                                //
                                RemoveHeadlList(&CurrentThread->ApcState.ApcListHead[KernelMode]);

                                Apc->Inserted = FALSE;

                                //
                                // Lower the IRQL to APC Level
                                //
                                _KfLowerIrql(OldIrql);
                                
                                //
                                // Note that nothing prevents special kernel                                  
                                // APCs from executing
                                //
                                Kernelroutine(Apc, 
                                              &NormalRoutine,
                                              &NormalContext,
                                              &SystemArgument1,
                                              &SystemArgument2);

                                OldIrql = _KeRaiseIrqlToSynchLevel();
                        }
                        else
                        {
                                //
                                // We are here is NormalRoutine is non-NULL 			           
                                // which means this is
                                // a regular kernel-mode APC. Then check is 			           
                                // there are any 
                                // regular kernel-mode APCs in progress or 				
                                // the thread is inside a 
                                // critical region in which case 					
                                // KernelApcDisable is TRUE.
                                // If any of these conditions hold, return to 			
                                // the caller
                                //
                                if (CurrentThread->ApcState.KernelApcInProgress || 
                                    CurrentThread->KernelApcDisable) goto restore_irql_and_exit;
                                
                                //
                                // Dequeue the APC object from the list head
                                //
                                RemoveHeadList(&CurrentThread->ApcState.ApcListHead[KernelMode]);

                                //
                                // Clear the kernel-mode Alerted flag
                                //
                                CurrentThread->Alerted[0] = FALSE;

                                _KfLowerIrql(OldIrql);
                                
                                //
                                // Call this regular-kernel mode APC 					
                                // KernelRoutine
                                //
                                Kernelroutine(Apc, 
                                              &NormalRoutine,
                                              &NormalContext,
                                              &SystemArgument1,
                                              &SystemArgument2);
                                
                                //
                                // If the KernelRoutine of this regualr    			
                                // kernel-mode APC did not
                                // clear the NormalRoutine function pointer, 				
                                // call this function
                                // at Passive Level
                                //
                                if (NormalRoutine != NULL)
                                {
                                        //
                                        // Because the NormalRoutine of a                                         
                                        // regular kernel-mode APC
                                        // runs at Passive Level, it can be                                          
                                        // preempted by
                                        // special kernel-mode APCs
                                        //
                                        CurrentThread->ApcState.KernelApcInProgress = TRUE;
          
                                        _KfLowerIrql(PASSIVE_LEVEL);
        
                                        NormalRoutine(NormalContext,
                                                      SystemArgument1,
                                                      SystemArgument2);
                        
                                        OldIrql = _KfRaiseIrql(APC_LEVEL);
                                }
                                
                                OldIrql = _KeRaiseIrqlToSynchLevel();

                                CurrentThread->ApcState.KernelApcInProgress = FALSE;
                        }       
                }
                while (!IsListEmpty(&CurrentThread->ApcState.ApcListhead [KernelMode]))
        }

        //
        // Check if user-mode APC list is empty
        //
        if (!IsListEmpty(&CurrentThread->ApcState.ApcListhead[UserMode]))
        {
                //
                // If PreviousMode is not user mode or there are  
                // no pending user APCs, just return to the caller
                //
                if (PreviousMode != UserMode ||
                    CurrentThread->ApcState.UserApcPending == FALSE) goto restore_irql_and_exit;

                CurrentThread->ApcState.UserApcPending = FALSE;

                //
                // Get the APC object at the list head
                //
                entry = CurrentThread->ApcState.ApcListHead[UserMode].Flink;
        
                Apc = CONTAINING_RECORD(entry, KAPC, ApcListEntry);

                //
                // Save the APC attributes in local variables so that it×s                 
                // safe 
                // to free the APC object in KernelRoutine
                //
                NormalRoutine   = Apc->NormalRoutine;
                KernelRoutine   = Apc->KernelRoutine;
                NormalContext   = Apc->NormalContext;
                SystemArgument1 = Apc->SystemArgument1;
                SystemArgument2 = Apc->SystemArgument2;

                RemoveHeadList(&CurrentThread->ApcState.ApcListHead[UserMode]);

                //
                // Clear the kernel-mode Alerted flag
                //
                CurrentThread->Alerted[0] = FALSE;
        
                //
                // Calls this user APC's KernelRoutine
                //
                Kernelroutine(Apc,
                              &NormalRoutine,
                              &NormalContext,
                              &SystemArgument1,
                              &SystemArgument2);

                if (NormalRoutine == NULL)
                {
                        //
                        // This function checks whether there are more                          
                        // pending user APCs
                        // and in such a case, set the UserModeApcPendig                          
                        // flag to TRUE.
                        //
                        _KeTestAlertThread(UserMode);

                        return;
                }
                else
                {
                        //
                        // This function sets up the thread's the trap frame                         
                        // to start
                        // executing in user mode at KiUserApcDispatcher in                          
                        // Ntdll.dll
                        //
                        _KiInitializeUserApc(Reserved,
                                             TrapFrame,
                                             NormalRoutine,
                                             NormalContext,
                                             SystemArgument1,
                                             SystemArgument2);

                        return;
                }
        }

        restore_irql_and_exit:
        
        _KfLowerIrql(OldIrql);
}


Previous Page | 1 | 2 | 3 | 4 | 5 | 6 | 7
TOP 5 ARTICLES
No Top Articles.



MICROSITES
FEATURED TOPIC

ADDITIONAL TOPICS

INFO-LINK