Channels ▼
RSS

Inside NT's Asynchronous Procedure Call


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);
}



Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.