test: apcsup.c File Reference

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# apcsup.c File Reference

`#include "ki.h"`

Go to the source code of this file.

## Functions

VOID | KiDeliverApc (IN KPROCESSOR_MODE PreviousMode, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame)
BOOLEAN FASTCALL | KiInsertQueueApc (IN PKAPC Apc, IN KPRIORITY Increment)

## Function Documentation

VOID KiDeliverApc | ( | IN KPROCESSOR_MODE | PreviousMode,
|  | IN PKEXCEPTION_FRAME | ExceptionFrame,
|  | IN PKTRAP_FRAME | TrapFrame
| ) |

Definition at line 30 of file apcsup.c.

References APC_LEVEL, _KAPC_STATE::ApcListHead, _KTHREAD::ApcState, FALSE, _KAPC::Inserted, KeBugCheckEx(), KeGetCurrentThread, KeLowerIrql(), KeRaiseIrql(), _KTHREAD::KernelApcDisable, _KAPC_STATE::KernelApcInProgress, _KAPC_STATE::KernelApcPending, KernelMode, _KAPC::KernelRoutine, KeTestAlertThread(), KiInitializeUserApc(), KiLockApcQueue, KiUnlockApcQueue, _KAPC::NormalContext, _KAPC::NormalRoutine, NULL, PKKERNEL_ROUTINE, PKNORMAL_ROUTINE, _KAPC::SystemArgument1, _KAPC::SystemArgument2, TRUE, _KAPC_STATE::UserApcPending, and UserMode.

Referenced by KiDispatchSoftwareInterrupt().

    00038                    :
    00039
    00040     This function is called from the APC interrupt code and when one or
    00041     more of the APC pending flags are set at system exit and the previous
    00042     IRQL is zero. All special kernel APC's are delivered first, followed
    00043     by normal kernel APC's if one is not already in progress, and finally
    00044     if the user APC queue is not empty, the user APC pending flag is set,
    00045     and the previous mode is user, then a user APC is delivered. On entry
    00046     to this routine IRQL is set to APC_LEVEL.
    00047
    00048     N.B. The exception frame and trap frame addresses are only guaranteed
    00049          to be valid if, and only if, the previous mode is user.
    00050
    00051 Arguments:
    00052
    00053     PreviousMode - Supplies the previous processor mode.
    00054
    00055     ExceptionFrame - Supplies a pointer to an exception frame.
    00056
    00057     TrapFrame - Supplies a pointer to a trap frame.
    00058
    00059 Return Value:
    00060
    00061     None.
    00062
    00063 --*/
    00064
    00065 {
    00066
    00067     PKAPC Apc;
    00068     PKKERNEL_ROUTINE KernelRoutine;
    00069     PLIST_ENTRY NextEntry;
    00070     PVOID NormalContext;
    00071     PKNORMAL_ROUTINE NormalRoutine;
    00072     KIRQL OldIrql;
    00073     PVOID SystemArgument1;
    00074     PVOID SystemArgument2;
    00075     PKTHREAD Thread;
    00076
    00077     //
    00078     // Raise IRQL to dispatcher level and lock the APC queue.
    00079     //
    00080
    00081     Thread = KeGetCurrentThread();
    00082     KiLockApcQueue(Thread, &OldIrql);
    00083
    00084     //
    00085     // Get address of current thread object, clear kernel APC pending, and
    00086     // check if any kernel mode APC's can be delivered.
    00087     //
    00088
    00089     Thread->ApcState.KernelApcPending = FALSE;
    00090     while (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]) == FALSE) {
    00091         NextEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
    00092         Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
    00093         KernelRoutine = Apc->KernelRoutine;
    00094         NormalRoutine = Apc->NormalRoutine;
    00095         NormalContext = Apc->NormalContext;
    00096         SystemArgument1 = Apc->SystemArgument1;
    00097         SystemArgument2 = Apc->SystemArgument2;
    00098         if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
    00099
    00100             //
    00101             // First entry in the kernel APC queue is a special kernel APC.
    00102             // Remove the entry from the APC queue, set its inserted state
    00103             // to FALSE, release dispatcher database lock, and call the kernel
    00104             // routine. On return raise IRQL to dispatcher level and lock
    00105             // dispatcher database lock.
    00106             //
    00107
    00108             RemoveEntryList(NextEntry);
    00109             Apc->Inserted = FALSE;
    00110             KiUnlockApcQueue(Thread, OldIrql);
    00111             (KernelRoutine)(Apc, &NormalRoutine, &NormalContext,
    00112                             &SystemArgument1, &SystemArgument2);
    00113
    00114 #if DBG
    00115
    00116                 if (KeGetCurrentIrql() != OldIrql) {
    00117                     KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
    00118                                  KeGetCurrentIrql() << 16 | OldIrql << 8,
    00119                                  (ULONG_PTR)KernelRoutine,
    00120                                  (ULONG_PTR)Apc,
    00121                                  (ULONG_PTR)NormalRoutine);
    00122                 }
    00123
    00124 #endif
    00125
    00126             KiLockApcQueue(Thread, &OldIrql);
    00127
    00128         } else {
    00129
    00130             //
    00131             // First entry in the kernel APC queue is a normal kernel APC.
    00132             // If there is not a normal kernel APC in progress and kernel
    00133             // APC's are not disabled, then remove the entry from the APC
    00134             // queue, set its inserted state to FALSE, release the APC queue
    00135             // lock, call the specified kernel routine, set kernel APC in
    00136             // progress, lower the IRQL to zero, and call the normal kernel
    00137             // APC routine. On return raise IRQL to dispatcher level, lock
    00138             // the APC queue, and clear kernel APC in progress.
    00139             //
    00140
    00141             if ((Thread->ApcState.KernelApcInProgress == FALSE) &&
    00142                (Thread->KernelApcDisable == 0)) {
    00143                 RemoveEntryList(NextEntry);
    00144                 Apc->Inserted = FALSE;
    00145                 KiUnlockApcQueue(Thread, OldIrql);
    00146                 (KernelRoutine)(Apc, &NormalRoutine, &NormalContext,
    00147                                 &SystemArgument1, &SystemArgument2);
    00148
    00149 #if DBG
    00150
    00151                 if (KeGetCurrentIrql() != OldIrql) {
    00152                     KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
    00153                                  KeGetCurrentIrql() << 16 | OldIrql << 8 | 1,
    00154                                  (ULONG_PTR)KernelRoutine,
    00155                                  (ULONG_PTR)Apc,
    00156                                  (ULONG_PTR)NormalRoutine);
    00157                 }
    00158
    00159 #endif
    00160
    00161                 if (NormalRoutine != (PKNORMAL_ROUTINE)NULL) {
    00162                     Thread->ApcState.KernelApcInProgress = TRUE;
    00163                     KeLowerIrql(0);
    00164                     (NormalRoutine)(NormalContext, SystemArgument1,
    00165                                     SystemArgument2);
    00166                     KeRaiseIrql(APC_LEVEL, &OldIrql);
    00167                 }
    00168
    00169                 KiLockApcQueue(Thread, &OldIrql);
    00170                 Thread->ApcState.KernelApcInProgress = FALSE;
    00171
    00172             } else {
    00173                 KiUnlockApcQueue(Thread, OldIrql);
    00174                 return;
    00175             }
    00176         }
    00177     }
    00178
    00179     //
    00180     // Kernel APC queue is empty. If the previous mode is user, user APC
    00181     // pending is set, and the user APC queue is not empty, then remove
    00182     // the first entry from the user APC queue, set its inserted state to
    00183     // FALSE, clear user APC pending, release the dispatcher database lock,
    00184     // and call the specified kernel routine. If the normal routine address
    00185     // is not NULL on return from the kernel routine, then initialize the
    00186     // user mode APC context and return. Otherwise, check to determine if
    00187     // another user mode APC can be processed.
    00188     //
    00189
    00190     if ((IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) == FALSE) &&
    00191        (PreviousMode == UserMode) && (Thread->ApcState.UserApcPending == TRUE)) {
    00192         Thread->ApcState.UserApcPending = FALSE;
    00193         NextEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
    00194         Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
    00195         KernelRoutine = Apc->KernelRoutine;
    00196         NormalRoutine = Apc->NormalRoutine;
    00197         NormalContext = Apc->NormalContext;
    00198         SystemArgument1 = Apc->SystemArgument1;
    00199         SystemArgument2 = Apc->SystemArgument2;
    00200         RemoveEntryList(NextEntry);
    00201         Apc->Inserted = FALSE;
    00202         KiUnlockApcQueue(Thread, OldIrql);
    00203         (KernelRoutine)(Apc, &NormalRoutine, &NormalContext,
    00204                         &SystemArgument1, &SystemArgument2);
    00205
    00206         if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
    00207             KeTestAlertThread(UserMode);
    00208
    00209         } else {
    00210             KiInitializeUserApc(ExceptionFrame, TrapFrame, NormalRoutine,
    00211                                 NormalContext, SystemArgument1, SystemArgument2);
    00212         }
    00213
    00214     } else {
    00215         KiUnlockApcQueue(Thread, OldIrql);
    00216     }
    00217
    00218     return;
    00219 }

BOOLEAN FASTCALL KiInsertQueueApc | ( | IN PKAPC | Apc,
|  | IN KPRIORITY | Increment
| ) |

Definition at line 223 of file apcsup.c.

References _KTHREAD::Alertable, _KAPC_STATE::ApcListHead, _KTHREAD::ApcQueueLock, _KTHREAD::ApcState, _KTHREAD::ApcStateIndex, _KTHREAD::ApcStatePointer, FALSE, Increment, _KTHREAD::KernelApcDisable, _KAPC_STATE::KernelApcInProgress, _KAPC_STATE::KernelApcPending, KernelMode, KiRequestApcInterrupt, KiUnwaitThread(), KPROCESSOR_MODE, _KTHREAD::NextProcessor, _KAPC::NormalRoutine, NULL, PsExitSpecialApc(), Running, _KTHREAD::State, TRUE, _KAPC_STATE::UserApcPending, UserMode, Waiting, _KTHREAD::WaitIrql, and _KTHREAD::WaitMode.

Referenced by Ke386VdmInsertQueueApc(), KeFreezeAllThreads(), KeInsertQueueApc(), and KeSuspendThread().

    00230                    :
    00231
    00232     This function inserts an APC object into a thread's APC queue. The address
    00233     of the thread object, the APC queue, and the type of APC are all derived
    00234     from the APC object. If the APC object is already in an APC queue, then
    00235     no opertion is performed and a function value of FALSE is returned. Else
    00236     the APC is inserted in the specified APC queue, its inserted state is set
    00237     to TRUE, and a function value of TRUE is returned. The APC will actually
    00238     be delivered when proper enabling conditions exist.
    00239
    00240 Arguments:
    00241
    00242     Apc - Supplies a pointer to a control object of type APC.
    00243
    00244     Increment - Supplies the priority increment that is to be applied if
    00245         queuing the APC causes a thread wait to be satisfied.
    00246
    00247 Return Value:
    00248
    00249     If the APC object is already in an APC queue, then a value of FALSE is
    00250     returned. Else a value of TRUE is returned.
    00251
    00252 --*/
    00253
    00254 {
    00255
    00256     KPROCESSOR_MODE ApcMode;
    00257     PKAPC ApcEntry;
    00258     PKAPC_STATE ApcState;
    00259     BOOLEAN Inserted;
    00260     PLIST_ENTRY ListEntry;
    00261     PKTHREAD Thread;
    00262
    00263     //
    00264     // If the APC object is already in an APC queue, then set inserted to
    00265     // FALSE. Else insert the APC object in the proper queue, set the APC
    00266     // inserted state to TRUE, check to determine if the APC should be delivered
    00267     // immediately, and set inserted to TRUE.
    00268     //
    00269     // For multiprocessor performance, the following code utilizes the fact
    00270     // that kernel APC disable count is incremented before checking whether
    00271     // the kernel APC queue is nonempty.
    00272     //
    00273     // See KeLeaveCriticalRegion().
    00274     //
    00275
    00276     Thread = Apc->Thread;
    00277     KiAcquireSpinLock(&Thread->ApcQueueLock);
    00278     if (Apc->Inserted) {
    00279         Inserted = FALSE;
    00280
    00281     } else {
    00282         ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex];
    00283
    00284         //
    00285         // Insert the APC after all other special APC entries selected by
    00286         // the processor mode if the normal routine value is null. Else
    00287         // insert the APC object at the tail of the APC queue selected by
    00288         // the processor mode unless the APC mode is user and the address
    00289         // of the special APC routine is exit thread, in which case insert
    00290         // the APC at the front of the list and set user APC pending.
    00291         //
    00292
    00293         ApcMode = Apc->ApcMode;
    00294         if (Apc->NormalRoutine != NULL) {
    00295             if ((ApcMode != KernelMode) && (Apc->KernelRoutine == PsExitSpecialApc)) {
    00296                 Thread->ApcState.UserApcPending = TRUE;
    00297                 InsertHeadList(&ApcState->ApcListHead[ApcMode],
    00298                                &Apc->ApcListEntry);
    00299
    00300             } else {
    00301                 InsertTailList(&ApcState->ApcListHead[ApcMode],
    00302                                &Apc->ApcListEntry);
    00303             }
    00304
    00305         } else {
    00306             ListEntry = ApcState->ApcListHead[ApcMode].Flink;
    00307             while (ListEntry != &ApcState->ApcListHead[ApcMode]) {
    00308                 ApcEntry = CONTAINING_RECORD(ListEntry, KAPC, ApcListEntry);
    00309                 if (ApcEntry->NormalRoutine != NULL) {
    00310                     break;
    00311                 }
    00312
    00313                 ListEntry = ListEntry->Flink;
    00314             }
    00315
    00316             ListEntry = ListEntry->Blink;
    00317             InsertHeadList(ListEntry, &Apc->ApcListEntry);
    00318         }
    00319
    00320         Apc->Inserted = TRUE;
    00321
    00322         //
    00323         // If the APC index from the APC object matches the APC Index of
    00324         // the thread, then check to determine if the APC should interrupt
    00325         // thread execution or sequence the thread out of a wait state.
    00326         //
    00327
    00328         if (Apc->ApcStateIndex == Thread->ApcStateIndex) {
    00329
    00330             //
    00331             // If the processor mode of the APC is kernel, then check if
    00332             // the APC should either interrupt the thread or sequence the
    00333             // thread out of a Waiting state. Else check if the APC should
    00334             // sequence the thread out of an alertable Waiting state.
    00335             //
    00336
    00337             if (ApcMode == KernelMode) {
    00338                 Thread->ApcState.KernelApcPending = TRUE;
    00339                 if (Thread->State == Running) {
    00340                     KiRequestApcInterrupt(Thread->NextProcessor);
    00341
    00342                 } else if ((Thread->State == Waiting) &&
    00343                           (Thread->WaitIrql == 0) &&
    00344                           ((Apc->NormalRoutine == NULL) ||
    00345                           ((Thread->KernelApcDisable == 0) &&
    00346                           (Thread->ApcState.KernelApcInProgress == FALSE)))) {
    00347                     KiUnwaitThread(Thread, STATUS_KERNEL_APC, Increment);
    00348                 }
    00349
    00350             } else if ((Thread->State == Waiting) &&
    00351                       (Thread->WaitMode == UserMode) &&
    00352                       (Thread->Alertable)) {
    00353                 Thread->ApcState.UserApcPending = TRUE;
    00354                 KiUnwaitThread(Thread, STATUS_USER_APC, Increment);
    00355             }
    00356         }
    00357
    00358         Inserted = TRUE;
    00359     }
    00360
    00361     //
    00362     // Unlock the APC queue lock, and return whether the APC object was
    00363     // inserted in an APC queue.
    00364     //
    00365
    00366     KiReleaseSpinLock(&Thread->ApcQueueLock);
    00367     return Inserted;
    00368 }
    }