ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

apc.c
Go to the documentation of this file.
00001 /*
00002  * PROJECT:         ReactOS Kernel
00003  * LICENSE:         GPL - See COPYING in the top level directory
00004  * FILE:            ntoskrnl/ke/apc.c
00005  * PURPOSE:         Implements the Asynchronous Procedure Call mechanism
00006  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
00007  */
00008 
00009 /* INCLUDES *****************************************************************/
00010 
00011 #include <ntoskrnl.h>
00012 #define NDEBUG
00013 #include <debug.h>
00014 
00015 /* PRIVATE FUNCTIONS *********************************************************/
00016 
00017 /*++
00018  * @name KiCheckForKernelApcDelivery
00019  * @implemented NT 5.2
00020  *
00021  *     The KiCheckForKernelApcDelivery routine is called whenever APCs have
00022  *     just been re-enabled in Kernel Mode, such as after leaving a Critical or
00023  *     Guarded Region. It delivers APCs if the environment is right.
00024  *
00025  * @param None.
00026  *
00027  * @return None.
00028  *
00029  * @remarks This routine allows KeLeave/EnterCritical/GuardedRegion to be used
00030  *          as macros from inside WIN32K or other Drivers, which will then only
00031  *          have to do an Import API call in the case where APCs are enabled again.
00032  *
00033  *--*/
00034 VOID
00035 NTAPI
00036 KiCheckForKernelApcDelivery(VOID)
00037 {
00038     KIRQL OldIrql;
00039 
00040     /* We should only deliver at passive */
00041     if (KeGetCurrentIrql() == PASSIVE_LEVEL)
00042     {
00043         /* Raise to APC and Deliver APCs, then lower back to Passive */
00044         KeRaiseIrql(APC_LEVEL, &OldIrql);
00045         KiDeliverApc(KernelMode, 0, 0);
00046         KeLowerIrql(PASSIVE_LEVEL);
00047     }
00048     else
00049     {
00050         /*
00051          * If we're not at passive level it means someone raised IRQL
00052          * to APC level before the critical or guarded section was entered
00053          * (e.g) by a fast mutex). This implies that the APCs shouldn't
00054          * be delivered now, but after the IRQL is lowered to passive
00055          * level again.
00056          */
00057         KeGetCurrentThread()->ApcState.KernelApcPending = TRUE;
00058         HalRequestSoftwareInterrupt(APC_LEVEL);
00059     }
00060 }
00061 
00062 /*++
00063  * @name KiInsertQueueApc
00064  *
00065  *     The KiInsertQueueApc routine queues a APC for execution when the right
00066  *     scheduler environment exists.
00067  *
00068  * @param Apc
00069  *        Pointer to an initialized control object of type APC for which the
00070  *        caller provides the storage.
00071  *
00072  * @param PriorityBoost
00073  *        Priority Boost to apply to the Thread.
00074  *
00075  * @return None
00076  *
00077  * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
00078  *          and at PASSIVE_LEVEL for the NormalRoutine registered.
00079  *
00080  *          Callers of this routine must have locked the dipatcher database.
00081  *
00082  *--*/
00083 VOID
00084 FASTCALL
00085 KiInsertQueueApc(IN PKAPC Apc,
00086                  IN KPRIORITY PriorityBoost)
00087 {
00088     PKTHREAD Thread = Apc->Thread;
00089     PKAPC_STATE ApcState;
00090     KPROCESSOR_MODE ApcMode;
00091     PLIST_ENTRY ListHead, NextEntry;
00092     PKAPC QueuedApc;
00093     PKGATE Gate;
00094     NTSTATUS Status;
00095     BOOLEAN RequestInterrupt = FALSE;
00096 
00097     /*
00098      * Check if the caller wanted this APC to use the thread's environment at
00099      * insertion time.
00100      */
00101     if (Apc->ApcStateIndex == InsertApcEnvironment)
00102     {
00103         /* Copy it over */
00104         Apc->ApcStateIndex = Thread->ApcStateIndex;
00105     }
00106 
00107     /* Get the APC State for this Index, and the mode too */
00108     ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex];
00109     ApcMode = Apc->ApcMode;
00110 
00111     /* The APC must be "inserted" already */
00112     ASSERT(Apc->Inserted == TRUE);
00113 
00114     /* Three scenarios:
00115      * 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
00116      * 2) User APC which is PsExitSpecialApc = Put it at the front of the List
00117      * 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
00118      */
00119     if (Apc->NormalRoutine)
00120     {
00121         /* Normal APC; is it the Thread Termination APC? */
00122         if ((ApcMode != KernelMode) &&
00123             (Apc->KernelRoutine == PsExitSpecialApc))
00124         {
00125             /* Set User APC pending to true */
00126             Thread->ApcState.UserApcPending = TRUE;
00127 
00128             /* Insert it at the top of the list */
00129             InsertHeadList(&ApcState->ApcListHead[ApcMode],
00130                            &Apc->ApcListEntry);
00131         }
00132         else
00133         {
00134             /* Regular user or kernel Normal APC */
00135             InsertTailList(&ApcState->ApcListHead[ApcMode],
00136                            &Apc->ApcListEntry);
00137         }
00138     }
00139     else
00140     {
00141         /* Special APC, find the last one in the list */
00142         ListHead = &ApcState->ApcListHead[ApcMode];
00143         NextEntry = ListHead->Blink;
00144         while (NextEntry != ListHead)
00145         {
00146             /* Get the APC */
00147             QueuedApc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
00148 
00149             /* Is this a No-Normal APC? If so, break */
00150             if (!QueuedApc->NormalRoutine) break;
00151 
00152             /* Move to the previous APC in the Queue */
00153             NextEntry = NextEntry->Blink;
00154         }
00155 
00156         /* Insert us here */
00157         InsertHeadList(NextEntry, &Apc->ApcListEntry);
00158     }
00159 
00160     /* Now check if the Apc State Indexes match */
00161     if (Thread->ApcStateIndex == Apc->ApcStateIndex)
00162     {
00163         /* Check that the thread matches */
00164         if (Thread == KeGetCurrentThread())
00165         {
00166             /* Sanity check */
00167             ASSERT(Thread->State == Running);
00168 
00169             /* Check if this is kernel mode */
00170             if (ApcMode == KernelMode)
00171             {
00172                 /* All valid, a Kernel APC is pending now */
00173                 Thread->ApcState.KernelApcPending = TRUE;
00174 
00175                 /* Check if Special APCs are disabled */
00176                 if (!Thread->SpecialApcDisable)
00177                 {
00178                     /* They're not, so request the interrupt */
00179                     HalRequestSoftwareInterrupt(APC_LEVEL);
00180                 }
00181             }
00182         }
00183         else
00184         {
00185             /* Acquire the dispatcher lock */
00186             KiAcquireDispatcherLock();
00187 
00188             /* Check if this is a kernel-mode APC */
00189             if (ApcMode == KernelMode)
00190             {
00191                 /* Kernel-mode APC, set us pending */
00192                 Thread->ApcState.KernelApcPending = TRUE;
00193 
00194                 /* Are we currently running? */
00195                 if (Thread->State == Running)
00196                 {
00197                     /* The thread is running, so remember to send a request */
00198                     RequestInterrupt = TRUE;
00199                 }
00200                 else if ((Thread->State == Waiting) &&
00201                          (Thread->WaitIrql == PASSIVE_LEVEL) &&
00202                          !(Thread->SpecialApcDisable) &&
00203                          (!(Apc->NormalRoutine) ||
00204                           (!(Thread->KernelApcDisable) &&
00205                            !(Thread->ApcState.KernelApcInProgress))))
00206                 {
00207                     /* We'll unwait with this status */
00208                     Status = STATUS_KERNEL_APC;
00209 
00210                     /* Wake up the thread */
00211                     KiUnwaitThread(Thread, Status, PriorityBoost);
00212                 }
00213                 else if (Thread->State == GateWait)
00214                 {
00215                     /* Lock the thread */
00216                     KiAcquireThreadLock(Thread);
00217                     
00218                     /* Essentially do the same check as above */
00219                     if ((Thread->State == GateWait) &&
00220                         (Thread->WaitIrql == PASSIVE_LEVEL) &&
00221                         !(Thread->SpecialApcDisable) &&
00222                         (!(Apc->NormalRoutine) ||
00223                          (!(Thread->KernelApcDisable) &&
00224                           !(Thread->ApcState.KernelApcInProgress))))
00225                     {
00226                         /* We were in a gate wait. Handle this. */
00227                         DPRINT1("A thread was in a gate wait\n");
00228                         
00229                         /* Get the gate */
00230                         Gate = Thread->GateObject;
00231 
00232                         /* Lock the gate */
00233                         KiAcquireDispatcherObject(&Gate->Header);
00234                         
00235                         /* Remove it from the waiters list */
00236                         RemoveEntryList(&Thread->WaitBlock[0].WaitListEntry);
00237                         
00238                         /* Unlock the gate */
00239                         KiReleaseDispatcherObject(&Gate->Header);
00240                         
00241                         /* Increase the queue counter if needed */
00242                         if (Thread->Queue) Thread->Queue->CurrentCount++;
00243                         
00244                         /* Put into deferred ready list with this status */
00245                         Thread->WaitStatus = STATUS_KERNEL_APC;
00246                         KiInsertDeferredReadyList(Thread);
00247                     }
00248                     
00249                     /* Release the thread lock */
00250                     KiReleaseThreadLock(Thread);
00251                 }
00252             }
00253             else if ((Thread->State == Waiting) &&
00254                      (Thread->WaitMode == UserMode) &&
00255                      ((Thread->Alertable) ||
00256                       (Thread->ApcState.UserApcPending)))
00257             {
00258                 /* Set user-mode APC pending */
00259                 Thread->ApcState.UserApcPending = TRUE;
00260                 Status = STATUS_USER_APC;
00261 
00262                 /* Wake up the thread */
00263                 KiUnwaitThread(Thread, Status, PriorityBoost);
00264             }
00265 
00266             /* Release dispatcher lock */
00267             KiReleaseDispatcherLockFromDpcLevel();
00268 
00269             /* Check if an interrupt was requested */
00270             KiRequestApcInterrupt(RequestInterrupt, Thread->NextProcessor);
00271         }
00272     }
00273 }
00274 
00275 /*++
00276  * @name KiDeliverApc
00277  * @implemented @NT4
00278  *
00279  *     The KiDeliverApc routine is called from IRQL switching code if the
00280  *     thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
00281  *     pending.
00282  *
00283  * @param DeliveryMode
00284  *        Specifies the current processor mode.
00285  *
00286  * @param ExceptionFrame
00287  *        Pointer to the Exception Frame on non-i386 builds.
00288  *
00289  * @param TrapFrame
00290  *        Pointer to the Trap Frame.
00291  *
00292  * @return None.
00293  *
00294  * @remarks First, Special APCs are delivered, followed by Kernel-Mode APCs and
00295  *          User-Mode APCs. Note that the TrapFrame is only valid if the
00296  *          delivery mode is User-Mode.
00297  *          Upon entry, this routine executes at APC_LEVEL.
00298  *
00299  *--*/
00300 VOID
00301 NTAPI
00302 KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode,
00303              IN PKEXCEPTION_FRAME ExceptionFrame,
00304              IN PKTRAP_FRAME TrapFrame)
00305 {
00306     PKTHREAD Thread = KeGetCurrentThread();
00307     PKPROCESS Process = Thread->ApcState.Process;
00308     PKTRAP_FRAME OldTrapFrame;
00309     PLIST_ENTRY ApcListEntry;
00310     PKAPC Apc;
00311     KLOCK_QUEUE_HANDLE ApcLock;
00312     PKKERNEL_ROUTINE KernelRoutine;
00313     PVOID NormalContext;
00314     PKNORMAL_ROUTINE NormalRoutine;
00315     PVOID SystemArgument1;
00316     PVOID SystemArgument2;
00317     ASSERT_IRQL_EQUAL(APC_LEVEL);
00318 
00319     /* Save the old trap frame and set current one */
00320     OldTrapFrame = Thread->TrapFrame;
00321     Thread->TrapFrame = TrapFrame;
00322 
00323     /* Clear Kernel APC Pending */
00324     Thread->ApcState.KernelApcPending = FALSE;
00325 
00326     /* Check if Special APCs are disabled */
00327     if (Thread->SpecialApcDisable) goto Quickie;
00328 
00329     /* Do the Kernel APCs first */
00330     while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
00331     {
00332         /* Lock the APC Queue */
00333         KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
00334 
00335         /* Check if the list became empty now */
00336         if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
00337         {
00338             /* It is, release the lock and break out */
00339             KiReleaseApcLock(&ApcLock);
00340             break;
00341         }
00342 
00343         /* Kernel APC is not pending anymore */
00344         Thread->ApcState.KernelApcPending = FALSE;
00345 
00346         /* Get the next Entry */
00347         ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
00348         Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
00349 
00350         /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
00351         NormalRoutine = Apc->NormalRoutine;
00352         KernelRoutine = Apc->KernelRoutine;
00353         NormalContext = Apc->NormalContext;
00354         SystemArgument1 = Apc->SystemArgument1;
00355         SystemArgument2 = Apc->SystemArgument2;
00356 
00357         /* Special APC */
00358         if (!NormalRoutine)
00359         {
00360             /* Remove the APC from the list */
00361             RemoveEntryList(ApcListEntry);
00362             Apc->Inserted = FALSE;
00363 
00364             /* Release the APC lock */
00365             KiReleaseApcLock(&ApcLock);
00366 
00367             /* Call the Special APC */
00368             KernelRoutine(Apc,
00369                           &NormalRoutine,
00370                           &NormalContext,
00371                           &SystemArgument1,
00372                           &SystemArgument2);
00373 
00374             /* Make sure it returned correctly */
00375             if (KeGetCurrentIrql() != ApcLock.OldIrql)
00376             {
00377                 KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
00378                              (KeGetCurrentIrql() << 16) |
00379                              (ApcLock.OldIrql << 8),
00380                              (ULONG_PTR)KernelRoutine,
00381                              (ULONG_PTR)Apc,
00382                              (ULONG_PTR)NormalRoutine);
00383             }
00384         }
00385         else
00386         {
00387             /* Normal Kernel APC, make sure it's safe to deliver */
00388             if ((Thread->ApcState.KernelApcInProgress) ||
00389                 (Thread->KernelApcDisable))
00390             {
00391                 /* Release lock and return */
00392                 KiReleaseApcLock(&ApcLock);
00393                 goto Quickie;
00394             }
00395 
00396             /* Dequeue the APC */
00397             RemoveEntryList(ApcListEntry);
00398             Apc->Inserted = FALSE;
00399 
00400             /* Go back to APC_LEVEL */
00401             KiReleaseApcLock(&ApcLock);
00402 
00403             /* Call the Kernel APC */
00404             KernelRoutine(Apc,
00405                           &NormalRoutine,
00406                           &NormalContext,
00407                           &SystemArgument1,
00408                           &SystemArgument2);
00409 
00410             /* Make sure it returned correctly */
00411             if (KeGetCurrentIrql() != ApcLock.OldIrql)
00412             {
00413                 KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
00414                              (KeGetCurrentIrql() << 16) |
00415                              (ApcLock.OldIrql << 8),
00416                              (ULONG_PTR)KernelRoutine,
00417                              (ULONG_PTR)Apc,
00418                              (ULONG_PTR)NormalRoutine);
00419             }
00420 
00421             /* Check if there still is a Normal Routine */
00422             if (NormalRoutine)
00423             {
00424                 /* At Passive Level, an APC can be prempted by a Special APC */
00425                 Thread->ApcState.KernelApcInProgress = TRUE;
00426                 KeLowerIrql(PASSIVE_LEVEL);
00427 
00428                 /* Call and Raise IRQL back to APC_LEVEL */
00429                 NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
00430                 KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql);
00431             }
00432 
00433             /* Set Kernel APC in progress to false and loop again */
00434             Thread->ApcState.KernelApcInProgress = FALSE;
00435         }
00436     }
00437 
00438     /* Now we do the User APCs */
00439     if ((DeliveryMode == UserMode) &&
00440         !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
00441          (Thread->ApcState.UserApcPending))
00442     {
00443         /* Lock the APC Queue */
00444         KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
00445 
00446         /* It's not pending anymore */
00447         Thread->ApcState.UserApcPending = FALSE;
00448 
00449         /* Check if the list became empty now */
00450         if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
00451         {
00452             /* It is, release the lock and break out */
00453             KiReleaseApcLock(&ApcLock);
00454             goto Quickie;
00455         }
00456 
00457         /* Get the actual APC object */
00458         ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
00459         Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
00460 
00461         /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
00462         NormalRoutine = Apc->NormalRoutine;
00463         KernelRoutine = Apc->KernelRoutine;
00464         NormalContext = Apc->NormalContext;
00465         SystemArgument1 = Apc->SystemArgument1;
00466         SystemArgument2 = Apc->SystemArgument2;
00467 
00468         /* Remove the APC from Queue, and release the lock */
00469         RemoveEntryList(ApcListEntry);
00470         Apc->Inserted = FALSE;
00471         KiReleaseApcLock(&ApcLock);
00472 
00473         /* Call the kernel routine */
00474         KernelRoutine(Apc,
00475                       &NormalRoutine,
00476                       &NormalContext,
00477                       &SystemArgument1,
00478                       &SystemArgument2);
00479 
00480         /* Check if there's no normal routine */
00481         if (!NormalRoutine)
00482         {
00483             /* Check if more User APCs are Pending */
00484             KeTestAlertThread(UserMode);
00485         }
00486         else
00487         {
00488             /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
00489             KiInitializeUserApc(ExceptionFrame,
00490                                 TrapFrame,
00491                                 NormalRoutine,
00492                                 NormalContext,
00493                                 SystemArgument1,
00494                                 SystemArgument2);
00495         }
00496     }
00497 
00498 Quickie:
00499     /* Make sure we're still in the same process */
00500     if (Process != Thread->ApcState.Process)
00501     {
00502         /* Erm, we got attached or something! BAD! */
00503         KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
00504                      (ULONG_PTR)Process,
00505                      (ULONG_PTR)Thread->ApcState.Process,
00506                      Thread->ApcStateIndex,
00507                      KeGetCurrentPrcb()->DpcRoutineActive);
00508     }
00509 
00510     /* Restore the trap frame */
00511     Thread->TrapFrame = OldTrapFrame;
00512 }
00513 
00514 FORCEINLINE
00515 VOID
00516 RepairList(IN PLIST_ENTRY Original,
00517            IN PLIST_ENTRY Copy,
00518            IN KPROCESSOR_MODE Mode)
00519 {
00520     /* Check if the list for this mode is empty */
00521     if (IsListEmpty(&Original[Mode]))
00522     {
00523         /* It is, all we need to do is initialize it */
00524         InitializeListHead(&Copy[Mode]);
00525     }
00526     else
00527     {
00528         /* Copy the lists */
00529         Copy[Mode].Flink = Original[Mode].Flink;
00530         Copy[Mode].Blink = Original[Mode].Blink;
00531         Original[Mode].Flink->Blink = &Copy[Mode];
00532         Original[Mode].Blink->Flink = &Copy[Mode];
00533     }
00534 }
00535 
00536 VOID
00537 NTAPI
00538 KiMoveApcState(PKAPC_STATE OldState,
00539                PKAPC_STATE NewState)
00540 {
00541     /* Restore backup of Original Environment */
00542     RtlCopyMemory(NewState, OldState, KAPC_STATE_ACTUAL_LENGTH);
00543 
00544     /* Repair Lists */
00545     RepairList(OldState->ApcListHead, NewState->ApcListHead, KernelMode);
00546     RepairList(OldState->ApcListHead, NewState->ApcListHead, UserMode);
00547 }
00548 
00549 /* PUBLIC FUNCTIONS **********************************************************/
00550 
00551 /*++
00552  * @name KeEnterCriticalRegion
00553  * @implemented NT4
00554  *
00555  *     The KeEnterCriticalRegion routine temporarily disables the delivery of
00556  *     normal kernel APCs; special kernel-mode APCs are still delivered.
00557  *
00558  * @param None.
00559  *
00560  * @return None.
00561  *
00562  * @remarks Highest-level drivers can call this routine while running in the
00563  *          context of the thread that requested the current I/O operation.
00564  *          Any caller of this routine should call KeLeaveCriticalRegion as
00565  *          quickly as possible.
00566  *
00567  *          Callers of KeEnterCriticalRegion must be running at IRQL <=
00568  *          APC_LEVEL.
00569  *
00570  *--*/
00571 VOID
00572 NTAPI
00573 _KeEnterCriticalRegion(VOID)
00574 {
00575     /* Use inlined function */
00576     KeEnterCriticalRegion();
00577 }
00578 
00579 /*++
00580  * KeLeaveCriticalRegion
00581  * @implemented NT4
00582  *
00583  *     The KeLeaveCriticalRegion routine reenables the delivery of normal
00584  *     kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
00585  *
00586  * @param None.
00587  *
00588  * @return None.
00589  *
00590  * @remarks Highest-level drivers can call this routine while running in the
00591  *          context of the thread that requested the current I/O operation.
00592  *
00593  *          Callers of KeLeaveCriticalRegion must be running at IRQL <=
00594  *          DISPATCH_LEVEL.
00595  *
00596  *--*/
00597 VOID
00598 NTAPI
00599 _KeLeaveCriticalRegion(VOID)
00600 {
00601     /* Use inlined version */
00602     KeLeaveCriticalRegion();
00603 }
00604 
00605 /*++
00606  * KeInitializeApc
00607  * @implemented NT4
00608  *
00609  *     The KeInitializeApc routine initializes an APC object, and registers
00610  *     the Kernel, Rundown and Normal routines for that object.
00611  *
00612  * @param Apc
00613  *        Pointer to a KAPC structure that represents the APC object to
00614  *        initialize. The caller must allocate storage for the structure
00615  *        from resident memory.
00616  *
00617  * @param Thread
00618  *        Thread to which to deliver the APC.
00619  *
00620  * @param TargetEnvironment
00621  *        APC Environment to be used.
00622  *
00623  * @param KernelRoutine
00624  *        Points to the KernelRoutine to associate with the APC.
00625  *        This routine is executed for all APCs.
00626  *
00627  * @param RundownRoutine
00628  *        Points to the RundownRoutine to associate with the APC.
00629  *        This routine is executed when the Thread exits during APC execution.
00630  *
00631  * @param NormalRoutine
00632  *        Points to the NormalRoutine to associate with the APC.
00633  *        This routine is executed at PASSIVE_LEVEL. If this is not specifed,
00634  *        the APC becomes a Special APC and the Mode and Context parameters are
00635  *        ignored.
00636  *
00637  * @param Mode
00638  *        Specifies the processor mode at which to run the Normal Routine.
00639  *
00640  * @param Context
00641  *        Specifices the value to pass as Context parameter to the registered
00642  *        routines.
00643  *
00644  * @return None.
00645  *
00646  * @remarks The caller can queue an initialized APC with KeInsertQueueApc.
00647  *
00648  *--*/
00649 VOID
00650 NTAPI
00651 KeInitializeApc(IN PKAPC Apc,
00652                 IN PKTHREAD Thread,
00653                 IN KAPC_ENVIRONMENT TargetEnvironment,
00654                 IN PKKERNEL_ROUTINE KernelRoutine,
00655                 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
00656                 IN PKNORMAL_ROUTINE NormalRoutine,
00657                 IN KPROCESSOR_MODE Mode,
00658                 IN PVOID Context)
00659 {
00660     /* Sanity check */
00661     ASSERT(TargetEnvironment <= InsertApcEnvironment);
00662 
00663     /* Set up the basic APC Structure Data */
00664     Apc->Type = ApcObject;
00665     Apc->Size = sizeof(KAPC);
00666 
00667     /* Set the Environment */
00668     if (TargetEnvironment == CurrentApcEnvironment)
00669     {
00670         /* Use the current one for the thread */
00671         Apc->ApcStateIndex = Thread->ApcStateIndex;
00672     }
00673     else
00674     {
00675         /* Sanity check */
00676         ASSERT((TargetEnvironment <= Thread->ApcStateIndex) ||
00677                (TargetEnvironment == InsertApcEnvironment));
00678 
00679         /* Use the one that was given */
00680         Apc->ApcStateIndex = TargetEnvironment;
00681     }
00682 
00683     /* Set the Thread and Routines */
00684     Apc->Thread = Thread;
00685     Apc->KernelRoutine = KernelRoutine;
00686     Apc->RundownRoutine = RundownRoutine;
00687     Apc->NormalRoutine = NormalRoutine;
00688 
00689     /* Check if this is a special APC */
00690     if (NormalRoutine)
00691     {
00692         /* It's a normal one. Set the context and mode */
00693         Apc->ApcMode = Mode;
00694         Apc->NormalContext = Context;
00695     }
00696     else
00697     {
00698         /* It's a special APC, which can only be kernel mode */
00699         Apc->ApcMode = KernelMode;
00700         Apc->NormalContext = NULL;
00701     }
00702 
00703     /* The APC is not inserted */
00704     Apc->Inserted = FALSE;
00705 }
00706 
00707 /*++
00708  * @name KeInsertQueueApc
00709  * @implemented NT4
00710  *
00711  *     The KeInsertQueueApc routine queues a APC for execution when the right
00712  *     scheduler environment exists.
00713  *
00714  * @param Apc
00715  *        Pointer to an initialized control object of type APC for which the
00716  *        caller provides the storage.
00717  *
00718  * @param SystemArgument[1,2]
00719  *        Pointer to a set of two parameters that contain untyped data.
00720  *
00721  * @param PriorityBoost
00722  *        Priority Boost to apply to the Thread.
00723  *
00724  * @return If the APC is already inserted or APC queueing is disabled, FALSE.
00725  *         Otherwise, TRUE.
00726  *
00727  * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
00728  *          and at PASSIVE_LEVEL for the NormalRoutine registered.
00729  *
00730  *          Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
00731  *
00732  *--*/
00733 BOOLEAN
00734 NTAPI
00735 KeInsertQueueApc(IN PKAPC Apc,
00736                  IN PVOID SystemArgument1,
00737                  IN PVOID SystemArgument2,
00738                  IN KPRIORITY PriorityBoost)
00739 {
00740     PKTHREAD Thread = Apc->Thread;
00741     KLOCK_QUEUE_HANDLE ApcLock;
00742     BOOLEAN State = TRUE;
00743     ASSERT_APC(Apc);
00744     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
00745 
00746     /* Get the APC lock */
00747     KiAcquireApcLock(Thread, &ApcLock);
00748 
00749     /* Make sure we can Queue APCs and that this one isn't already inserted */
00750     if (!(Thread->ApcQueueable) || (Apc->Inserted))
00751     {
00752         /* Fail */
00753         State = FALSE;
00754     }
00755     else
00756     {
00757         /* Set the System Arguments and set it as inserted */
00758         Apc->SystemArgument1 = SystemArgument1;
00759         Apc->SystemArgument2 = SystemArgument2;
00760         Apc->Inserted = TRUE;
00761 
00762         /* Call the Internal Function */
00763         KiInsertQueueApc(Apc, PriorityBoost);
00764     }
00765 
00766     /* Release the APC lock and return success */
00767     KiReleaseApcLockFromDpcLevel(&ApcLock);
00768     KiExitDispatcher(ApcLock.OldIrql);
00769     return State;
00770 }
00771 
00772 /*++
00773  * @name KeFlushQueueApc
00774  * @implemented NT4
00775  *
00776  *     The KeFlushQueueApc routine flushes all APCs of the given processor mode
00777  *     from the specified Thread's APC queue.
00778  *
00779  * @param Thread
00780  *        Pointer to the thread whose APC queue will be flushed.
00781  *
00782  * @param PreviousMode
00783  *         Specifies which APC Queue to flush.
00784  *
00785  * @return A pointer to the first entry in the flushed APC queue.
00786  *
00787  * @remarks If the routine returns NULL, it means that no APCs were flushed.
00788  *          Callers of this routine must be running at DISPATCH_LEVEL or lower.
00789  *
00790  *--*/
00791 PLIST_ENTRY
00792 NTAPI
00793 KeFlushQueueApc(IN PKTHREAD Thread,
00794                 IN KPROCESSOR_MODE PreviousMode)
00795 {
00796     PKAPC Apc;
00797     PLIST_ENTRY FirstEntry, CurrentEntry;
00798     KLOCK_QUEUE_HANDLE ApcLock;
00799     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
00800 
00801     /* Check if this was user mode */
00802     if (PreviousMode == UserMode)
00803     {
00804         /* Get the APC lock */
00805         KiAcquireApcLock(Thread, &ApcLock);
00806 
00807         /* Select user list and check if it's empty */
00808         if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
00809         {
00810             /* Don't return anything */
00811             FirstEntry = NULL;
00812             goto FlushDone;
00813         }
00814     }
00815     else
00816     {
00817         /* Select kernel list and check if it's empty */
00818         if (IsListEmpty( &Thread->ApcState.ApcListHead[KernelMode]))
00819         {
00820             /* Don't return anything */
00821             return NULL;
00822         }
00823 
00824         /* Otherwise, acquire the APC lock */
00825         KiAcquireApcLock(Thread, &ApcLock);
00826     }
00827 
00828     /* Get the first entry and check if the list is empty now */
00829     FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink;
00830     if (FirstEntry == &Thread->ApcState.ApcListHead[PreviousMode])
00831     {
00832         /* It is, clear the returned entry */
00833         FirstEntry = NULL;
00834     }
00835     else
00836     {
00837         /* It's not, remove the first entry */
00838         RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]);
00839 
00840         /* Loop all the entries */
00841         CurrentEntry = FirstEntry;
00842         do
00843         {
00844             /* Get the APC and make it un-inserted */
00845             Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
00846             Apc->Inserted = FALSE;
00847 
00848             /* Get the next entry */
00849             CurrentEntry = CurrentEntry->Flink;
00850         } while (CurrentEntry != FirstEntry);
00851 
00852         /* Re-initialize the list */
00853         InitializeListHead(&Thread->ApcState.ApcListHead[PreviousMode]);
00854     }
00855 
00856     /* Release the lock */
00857 FlushDone:
00858     KiReleaseApcLock(&ApcLock);
00859 
00860     /* Return the first entry */
00861     return FirstEntry;
00862 }
00863 
00864 /*++
00865  * @name KeRemoveQueueApc
00866  * @implemented NT4
00867  *
00868  *     The KeRemoveQueueApc routine removes a given APC object from the system
00869  *     APC queue.
00870  *
00871  * @param Apc
00872  *         Pointer to an initialized APC object that was queued by calling
00873  *         KeInsertQueueApc.
00874  *
00875  * @return TRUE if the APC Object is in the APC Queue. Otherwise, no operation
00876  *         is performed and FALSE is returned.
00877  *
00878  * @remarks If the given APC Object is currently queued, it is removed from the
00879  *          queue and any calls to the registered routines are cancelled.
00880  *
00881  *          Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
00882  *
00883  *--*/
00884 BOOLEAN
00885 NTAPI
00886 KeRemoveQueueApc(IN PKAPC Apc)
00887 {
00888     PKTHREAD Thread = Apc->Thread;
00889     PKAPC_STATE ApcState;
00890     BOOLEAN Inserted;
00891     KLOCK_QUEUE_HANDLE ApcLock;
00892     ASSERT_APC(Apc);
00893     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
00894 
00895     /* Get the APC lock */
00896     KiAcquireApcLock(Thread, &ApcLock);
00897 
00898     /* Check if it's inserted */
00899     Inserted = Apc->Inserted;
00900     if (Inserted)
00901     {
00902         /* Set it as non-inserted and get the APC state */
00903         Apc->Inserted = FALSE;
00904         ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex];
00905 
00906         /* Acquire the dispatcher lock and remove it from the list */
00907         KiAcquireDispatcherLockAtDpcLevel();
00908         if (RemoveEntryList(&Apc->ApcListEntry))
00909         {
00910             /* Set the correct state based on the APC Mode */
00911             if (Apc->ApcMode == KernelMode)
00912             {
00913                 /* No more pending kernel APCs */
00914                 ApcState->KernelApcPending = FALSE;
00915             }
00916             else
00917             {
00918                 /* No more pending user APCs */
00919                 ApcState->UserApcPending = FALSE;
00920             }
00921         }
00922 
00923         /* Release dispatcher lock */
00924         KiReleaseDispatcherLockFromDpcLevel();
00925     }
00926 
00927     /* Release the lock and return */
00928     KiReleaseApcLock(&ApcLock);
00929     return Inserted;
00930 }
00931 
00932 /*++
00933  * @name KeAreApcsDisabled
00934  * @implemented NT4
00935  *
00936  *     The KeAreApcsDisabled routine returns whether kernel APC delivery is
00937  *     disabled for the current thread.
00938  *
00939  * @param None.
00940  *
00941  * @return KeAreApcsDisabled returns TRUE if the thread is within a critical
00942  *         region or a guarded region, and FALSE otherwise.
00943  *
00944  * @remarks A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled
00945  *          to determine if normal kernel APCs are disabled.
00946  *
00947  *          A thread that is inside critical region has both user APCs and
00948  *          normal kernel APCs disabled, but not special kernel APCs.
00949  *
00950  *          A thread that is inside a guarded region has all APCs disabled,
00951  *          including special kernel APCs.
00952  *
00953  *          Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
00954  *
00955  *--*/
00956 BOOLEAN
00957 NTAPI
00958 KeAreApcsDisabled(VOID)
00959 {
00960     /* Return the Kernel APC State */
00961     return KeGetCurrentThread()->CombinedApcDisable ? TRUE : FALSE;
00962 }
00963 
00964 /*++
00965  * @name KeAreAllApcsDisabled
00966  * @implemented NT5.1
00967  *
00968  *    The KeAreAllApcsDisabled routine returns whether the calling thread is
00969  *    inside a guarded region or running at IRQL >= APC_LEVEL, which disables
00970  *    all APC delivery.
00971  *
00972  * @param None.
00973  *
00974  * @return KeAreAllApcsDisabled returns TRUE if the thread is within a guarded
00975  *         guarded region or running at IRQL >= APC_LEVEL, and FALSE otherwise.
00976  *
00977  * @remarks A thread running at IRQL = PASSIVE_LEVEL can use this routine to
00978  *          determine if all APC delivery is disabled.
00979  *
00980  *          Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
00981  *
00982  *--*/
00983 BOOLEAN
00984 NTAPI
00985 KeAreAllApcsDisabled(VOID)
00986 {
00987     /* Return the Special APC State */
00988     return ((KeGetCurrentThread()->SpecialApcDisable) ||
00989             (KeGetCurrentIrql() >= APC_LEVEL)) ? TRUE : FALSE;
00990 }

Generated on Sun May 27 2012 04:27:11 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.