2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/apc.c
5 * PURPOSE: Implements the Asyncronous Procedure Call mechanism
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES *****************************************************************/
13 #include <internal/debug.h>
15 /* PRIVATE FUNCTIONS *********************************************************/
18 * @name KiCheckForKernelApcDelivery
21 * The KiCheckForKernelApcDelivery routine is called whenever APCs have
22 * just been re-enabled in Kernel Mode, such as after leaving a Critical or
23 * Guarded Region. It delivers APCs if the environment is right.
29 * @remarks This routine allows KeLeave/EnterCritical/GuardedRegion to be used
30 * as macro from inside WIN32K or other Drivers, which will then only
31 * have do an Import API call in the case where APCs are enabled again.
36 KiCheckForKernelApcDelivery(VOID
)
38 /* We should only deliver at passive */
39 if (KeGetCurrentIrql() == PASSIVE_LEVEL
)
41 /* Raise to APC and Deliver APCs, then lower back to Passive */
42 KfRaiseIrql(APC_LEVEL
);
43 KiDeliverApc(KernelMode
, 0, 0);
44 KfLowerIrql(PASSIVE_LEVEL
);
49 * If we're not at passive level it means someone raised IRQL
50 * to APC level before the a critical or guarded section was entered
51 * (e.g) by a fast mutex). This implies that the APCs shouldn't
52 * be delivered now, but after the IRQL is lowered to passive
55 KeGetCurrentThread()->ApcState
.KernelApcPending
= TRUE
;
56 HalRequestSoftwareInterrupt(APC_LEVEL
);
61 * @name KiInsertQueueApc
63 * The KiInsertQueueApc routine queues a APC for execution when the right
64 * scheduler environment exists.
67 * Pointer to an initialized control object of type DPC for which the
68 * caller provides the storage.
70 * @param PriorityBoost
71 * Priority Boost to apply to the Thread.
75 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
76 * and at PASSIVE_LEVEL for the NormalRoutine registered.
78 * Callers of this routine must have locked the dipatcher database.
83 KiInsertQueueApc(IN PKAPC Apc
,
84 IN KPRIORITY PriorityBoost
)
86 PKTHREAD Thread
= Apc
->Thread
;
88 KPROCESSOR_MODE ApcMode
;
89 PLIST_ENTRY ListHead
, NextEntry
;
92 BOOLEAN RequestInterrupt
;
95 * Check if the caller wanted this APC to use the thread's environment at
98 if (Apc
->ApcStateIndex
== InsertApcEnvironment
)
101 Apc
->ApcStateIndex
= Thread
->ApcStateIndex
;
104 /* Get the APC State for this Index, and the mode too */
105 ApcState
= Thread
->ApcStatePointer
[(UCHAR
)Apc
->ApcStateIndex
];
106 ApcMode
= Apc
->ApcMode
;
108 /* The APC must be "inserted" already */
109 ASSERT(Apc
->Inserted
== TRUE
);
112 * 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
113 * 2) User APC which is PsExitSpecialApc = Put it at the front of the List
114 * 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
116 if (Apc
->NormalRoutine
)
118 /* Normal APC; is it the Thread Termination APC? */
119 if ((ApcMode
!= KernelMode
) &&
120 (Apc
->KernelRoutine
== PsExitSpecialApc
))
122 /* Set User APC pending to true */
123 Thread
->ApcState
.UserApcPending
= TRUE
;
125 /* Insert it at the top of the list */
126 InsertHeadList(&ApcState
->ApcListHead
[ApcMode
],
131 /* Regular user or kernel Normal APC */
132 InsertTailList(&ApcState
->ApcListHead
[ApcMode
],
138 /* Special APC, find the first Normal APC in the list */
139 ListHead
= &ApcState
->ApcListHead
[ApcMode
];
140 NextEntry
= ListHead
->Flink
;
141 while(NextEntry
!= ListHead
)
144 QueuedApc
= CONTAINING_RECORD(NextEntry
, KAPC
, ApcListEntry
);
146 /* Is this a Normal APC? If so, break */
147 if (QueuedApc
->NormalRoutine
) break;
149 /* Move to the next APC in the Queue */
150 NextEntry
= NextEntry
->Flink
;
153 /* Move to the APC before this one (ie: the last Special APC) */
154 NextEntry
= NextEntry
->Blink
;
157 InsertHeadList(NextEntry
, &Apc
->ApcListEntry
);
160 /* Now check if the Apc State Indexes match */
161 if (Thread
->ApcStateIndex
== Apc
->ApcStateIndex
)
163 /* Check that if the thread matches */
164 if (Thread
== KeGetCurrentThread())
167 ASSERT(Thread
->State
== Running
);
169 /* Check if this is kernel mode */
170 if (ApcMode
== KernelMode
)
172 /* All valid, a Kernel APC is pending now */
173 Thread
->ApcState
.KernelApcPending
= TRUE
;
175 /* Check if Special APCs are disabled */
176 if (!Thread
->SpecialApcDisable
)
178 /* They're not, so request the interrupt */
179 HalRequestSoftwareInterrupt(APC_LEVEL
);
185 /* Acquire the dispatcher lock */
186 KiAcquireDispatcherLock();
188 /* Check if this is a non-kernel mode APC */
189 if (ApcMode
!= KernelMode
)
192 * Not a Kernel-Mode APC. Are we waiting in user-mode?
193 * If so, then are we alertable or already have an APC pending?
195 if (((Thread
->State
== Waiting
) && (Thread
->WaitMode
== UserMode
)) &&
196 ((Thread
->Alertable
) || (Thread
->ApcState
.UserApcPending
)))
198 /* Set user-mode APC pending */
199 Thread
->ApcState
.UserApcPending
= TRUE
;
200 Status
= STATUS_USER_APC
;
206 /* Kernel-mode APC, set us pending */
207 Thread
->ApcState
.KernelApcPending
= TRUE
;
209 /* Are we currently running? */
210 if (Thread
->State
== Running
)
212 /* The thread is running, so remember to send a request */
213 RequestInterrupt
= TRUE
;
215 /* On UP systems, request it immediately */
216 HalRequestSoftwareInterrupt(APC_LEVEL
);
222 * If the thread is Waiting at PASSIVE_LEVEL AND
223 * Special APCs are not disabled AND
224 * He is a Normal APC AND
225 * Kernel APCs are not disabled AND
226 * Kernel APC is not pending OR
227 * He is a Special APC THEN
228 * Unwait thread with STATUS_KERNEL_APC
230 if ((Thread
->State
== Waiting
) &&
231 (Thread
->WaitIrql
== PASSIVE_LEVEL
) &&
232 !(Thread
->SpecialApcDisable
) &&
233 (!(Apc
->NormalRoutine
) ||
234 (!(Thread
->KernelApcDisable
) &&
235 !(Thread
->ApcState
.KernelApcInProgress
))))
237 /* We'll unwait with this status */
238 Status
= STATUS_KERNEL_APC
;
240 /* Wake up the thread */
242 KiUnwaitThread(Thread
, Status
, PriorityBoost
);
246 /* Check if the thread is in a deferred ready state */
247 if (Thread
->State
== DeferredReady
)
249 /* FIXME: TODO in new scheduler */
255 /* Release dispatcher lock */
256 KiReleaseDispatcherLockFromDpcLevel();
258 /* Check if an interrupt was requested */
259 KiRequestApcInterrupt(RequestInterrupt
, Thread
->NextProcessor
);
268 * The KiDeliverApc routine is called from IRQL switching code if the
269 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
272 * @param DeliveryMode
273 * Specifies the current processor mode.
275 * @param ExceptionFrame
276 * Pointer to the Exception Frame on non-i386 builds.
279 * Pointer to the Trap Frame.
283 * @remarks First, Special APCs are delivered, followed by Kernel-Mode APCs and
284 * User-Mode APCs. Note that the TrapFrame is only valid if the
285 * delivery mode is User-Mode.
286 * Upon entry, this routine executes at APC_LEVEL.
291 KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode
,
292 IN PKEXCEPTION_FRAME ExceptionFrame
,
293 IN PKTRAP_FRAME TrapFrame
)
295 PKTHREAD Thread
= KeGetCurrentThread();
296 PKPROCESS Process
= Thread
->ApcState
.Process
;
297 PKTRAP_FRAME OldTrapFrame
;
298 PLIST_ENTRY ApcListEntry
;
300 KLOCK_QUEUE_HANDLE ApcLock
;
301 PKKERNEL_ROUTINE KernelRoutine
;
303 PKNORMAL_ROUTINE NormalRoutine
;
304 PVOID SystemArgument1
;
305 PVOID SystemArgument2
;
306 ASSERT_IRQL_EQUAL(APC_LEVEL
);
308 /* Save the old trap frame and set current one */
309 OldTrapFrame
= Thread
->TrapFrame
;
310 Thread
->TrapFrame
= TrapFrame
;
312 /* Clear Kernel APC Pending */
313 Thread
->ApcState
.KernelApcPending
= FALSE
;
315 /* Check if Special APCs are disabled */
316 if (Thread
->SpecialApcDisable
) goto Quickie
;
318 /* Do the Kernel APCs first */
319 while (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
321 /* Lock the APC Queue */
322 KiAcquireApcLockAtApcLevel(Thread
, &ApcLock
);
324 /* Check if the list became empty now */
325 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
327 /* It is, release the lock and break out */
328 KiReleaseApcLock(&ApcLock
);
332 /* Get the next Entry */
333 ApcListEntry
= Thread
->ApcState
.ApcListHead
[KernelMode
].Flink
;
334 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
336 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
337 NormalRoutine
= Apc
->NormalRoutine
;
338 KernelRoutine
= Apc
->KernelRoutine
;
339 NormalContext
= Apc
->NormalContext
;
340 SystemArgument1
= Apc
->SystemArgument1
;
341 SystemArgument2
= Apc
->SystemArgument2
;
346 /* Remove the APC from the list */
347 RemoveEntryList(ApcListEntry
);
348 Apc
->Inserted
= FALSE
;
350 /* Rrelease the APC lock */
351 KiReleaseApcLock(&ApcLock
);
353 /* Call the Special APC */
360 /* Make sure it returned correctly */
361 if (KeGetCurrentIrql() != ApcLock
.OldIrql
)
363 KeBugCheckEx(IRQL_UNEXPECTED_VALUE
,
364 (KeGetCurrentIrql() << 16) |
365 (ApcLock
.OldIrql
<< 8),
366 (ULONG_PTR
)KernelRoutine
,
368 (ULONG_PTR
)NormalRoutine
);
373 /* Normal Kernel APC, make sure it's safe to deliver */
374 if ((Thread
->ApcState
.KernelApcInProgress
) ||
375 (Thread
->KernelApcDisable
))
377 /* Release lock and return */
378 KiReleaseApcLock(&ApcLock
);
382 /* Dequeue the APC */
383 RemoveEntryList(ApcListEntry
);
384 Apc
->Inserted
= FALSE
;
386 /* Go back to APC_LEVEL */
387 KiReleaseApcLock(&ApcLock
);
389 /* Call the Kernel APC */
396 /* Make sure it returned correctly */
397 if (KeGetCurrentIrql() != ApcLock
.OldIrql
)
399 KeBugCheckEx(IRQL_UNEXPECTED_VALUE
,
400 (KeGetCurrentIrql() << 16) |
401 (ApcLock
.OldIrql
<< 8),
402 (ULONG_PTR
)KernelRoutine
,
404 (ULONG_PTR
)NormalRoutine
);
407 /* Check if There still is a Normal Routine */
410 /* At Passive Level, an APC can be prempted by a Special APC */
411 Thread
->ApcState
.KernelApcInProgress
= TRUE
;
412 KeLowerIrql(PASSIVE_LEVEL
);
414 /* Call and Raise IRQ back to APC_LEVEL */
415 NormalRoutine(NormalContext
, SystemArgument1
, SystemArgument2
);
416 KeRaiseIrql(APC_LEVEL
, &ApcLock
.OldIrql
);
419 /* Set Kernel APC in progress to false and loop again */
420 Thread
->ApcState
.KernelApcInProgress
= FALSE
;
424 /* Now we do the User APCs */
425 if ((DeliveryMode
== UserMode
) &&
426 !(IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])) &&
427 (Thread
->ApcState
.UserApcPending
))
429 /* Lock the APC Queue */
430 KiAcquireApcLockAtApcLevel(Thread
, &ApcLock
);
432 /* It's not pending anymore */
433 Thread
->ApcState
.UserApcPending
= FALSE
;
435 /* Check if the list became empty now */
436 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))
438 /* It is, release the lock and break out */
439 KiReleaseApcLock(&ApcLock
);
443 /* Get the actual APC object */
444 ApcListEntry
= Thread
->ApcState
.ApcListHead
[UserMode
].Flink
;
445 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
447 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
448 NormalRoutine
= Apc
->NormalRoutine
;
449 KernelRoutine
= Apc
->KernelRoutine
;
450 NormalContext
= Apc
->NormalContext
;
451 SystemArgument1
= Apc
->SystemArgument1
;
452 SystemArgument2
= Apc
->SystemArgument2
;
454 /* Remove the APC from Queue, and release the lock */
455 RemoveEntryList(ApcListEntry
);
456 Apc
->Inserted
= FALSE
;
457 KiReleaseApcLock(&ApcLock
);
459 /* Call the kernel routine */
466 /* Check if there's no normal routine */
469 /* Check if more User APCs are Pending */
470 KeTestAlertThread(UserMode
);
474 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
475 KiInitializeUserApc(ExceptionFrame
,
485 /* Make sure we're still in the same process */
486 if (Process
!= Thread
->ApcState
.Process
)
488 /* Erm, we got attached or something! BAD! */
489 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
,
491 (ULONG_PTR
)Thread
->ApcState
.Process
,
492 Thread
->ApcStateIndex
,
493 KeGetCurrentPrcb()->DpcRoutineActive
);
496 /* Restore the trap frame */
497 Thread
->TrapFrame
= OldTrapFrame
;
502 RepairList(IN PLIST_ENTRY Original
,
504 IN KPROCESSOR_MODE Mode
)
506 /* Check if the list for this mode is empty */
507 if (IsListEmpty(&Original
[Mode
]))
509 /* It is, all we need to do is initialize it */
510 InitializeListHead(&Copy
[Mode
]);
515 Copy
[Mode
].Flink
= Original
[Mode
].Flink
;
516 Copy
[Mode
].Blink
= Original
[Mode
].Blink
;
517 Original
[Mode
].Flink
->Blink
= &Copy
[Mode
];
518 Original
[Mode
].Blink
->Flink
= &Copy
[Mode
];
524 KiMoveApcState(PKAPC_STATE OldState
,
525 PKAPC_STATE NewState
)
527 /* Restore backup of Original Environment */
528 *NewState
= *OldState
;
531 RepairList(NewState
->ApcListHead
, OldState
->ApcListHead
, KernelMode
);
532 RepairList(NewState
->ApcListHead
, OldState
->ApcListHead
, UserMode
);
535 /* PUBLIC FUNCTIONS **********************************************************/
538 * @name KeEnterCriticalRegion
541 * The KeEnterCriticalRegion routine temporarily disables the delivery of
542 * normal kernel APCs; special kernel-mode APCs are still delivered.
548 * @remarks Highest-level drivers can call this routine while running in the
549 * context of the thread that requested the current I/O operation.
550 * Any caller of this routine should call KeLeaveCriticalRegion as
551 * quickly as possible.
553 * Callers of KeEnterCriticalRegion must be running at IRQL <=
559 _KeEnterCriticalRegion(VOID
)
561 /* Use inlined function */
562 KeEnterCriticalRegion();
566 * KeLeaveCriticalRegion
569 * The KeLeaveCriticalRegion routine reenables the delivery of normal
570 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
576 * @remarks Highest-level drivers can call this routine while running in the
577 * context of the thread that requested the current I/O operation.
579 * Callers of KeLeaveCriticalRegion must be running at IRQL <=
585 _KeLeaveCriticalRegion(VOID
)
587 /* Use inlined version */
588 KeLeaveCriticalRegion();
595 * The The KeInitializeApc routine initializes an APC object, and registers
596 * the Kernel, Rundown and Normal routines for that object.
599 * Pointer to a KAPC structure that represents the APC object to
600 * initialize. The caller must allocate storage for the structure
601 * from resident memory.
604 * Thread to which to deliver the APC.
606 * @param TargetEnvironment
607 * APC Environment to be used.
609 * @param KernelRoutine
610 * Points to the KernelRoutine to associate with the APC.
611 * This routine is executed for all APCs.
613 * @param RundownRoutine
614 * Points to the RundownRoutine to associate with the APC.
615 * This routine is executed when the Thread exists during APC execution.
617 * @param NormalRoutine
618 * Points to the NormalRoutine to associate with the APC.
619 * This routine is executed at PASSIVE_LEVEL. If this is not specifed,
620 * the APC becomes a Special APC and the Mode and Context parameters are
624 * Specifies the processor mode at which to run the Normal Routine.
627 * Specifices the value to pass as Context parameter to the registered
632 * @remarks The caller can queue an initialized APC with KeInsertQueueApc.
637 KeInitializeApc(IN PKAPC Apc
,
639 IN KAPC_ENVIRONMENT TargetEnvironment
,
640 IN PKKERNEL_ROUTINE KernelRoutine
,
641 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL
,
642 IN PKNORMAL_ROUTINE NormalRoutine
,
643 IN KPROCESSOR_MODE Mode
,
647 ASSERT(TargetEnvironment
<= InsertApcEnvironment
);
649 /* Set up the basic APC Structure Data */
650 Apc
->Type
= ApcObject
;
651 Apc
->Size
= sizeof(KAPC
);
653 /* Set the Environment */
654 if (TargetEnvironment
== CurrentApcEnvironment
)
656 /* Use the current one for the thread */
657 Apc
->ApcStateIndex
= Thread
->ApcStateIndex
;
662 ASSERT((TargetEnvironment
<= Thread
->ApcStateIndex
) ||
663 (TargetEnvironment
== InsertApcEnvironment
));
665 /* Use the one that was given */
666 Apc
->ApcStateIndex
= TargetEnvironment
;
670 /* Set the Thread and Routines */
671 Apc
->Thread
= Thread
;
672 Apc
->KernelRoutine
= KernelRoutine
;
673 Apc
->RundownRoutine
= RundownRoutine
;
674 Apc
->NormalRoutine
= NormalRoutine
;
677 /* Check if this is a special APC */
680 /* It's a normal one. Set the context and mode */
682 Apc
->NormalContext
= Context
;
686 /* It's a special APC, which can only be kernel mode */
687 Apc
->ApcMode
= KernelMode
;
688 Apc
->NormalContext
= NULL
;
691 /* The APC is not inserted*/
692 Apc
->Inserted
= FALSE
;
696 * @name KeInsertQueueApc
699 * The KeInsertQueueApc routine queues a APC for execution when the right
700 * scheduler environment exists.
703 * Pointer to an initialized control object of type DPC for which the
704 * caller provides the storage.
706 * @param SystemArgument[1,2]
707 * Pointer to a set of two parameters that contain untyped data.
709 * @param PriorityBoost
710 * Priority Boost to apply to the Thread.
712 * @return If the APC is already inserted or APC queueing is disabled, FALSE.
715 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
716 * and at PASSIVE_LEVEL for the NormalRoutine registered.
718 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
723 KeInsertQueueApc(IN PKAPC Apc
,
724 IN PVOID SystemArgument1
,
725 IN PVOID SystemArgument2
,
726 IN KPRIORITY PriorityBoost
)
728 PKTHREAD Thread
= Apc
->Thread
;
729 KLOCK_QUEUE_HANDLE ApcLock
;
730 BOOLEAN State
= TRUE
;
732 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
734 /* Get the APC lock */
735 KiAcquireApcLock(Thread
, &ApcLock
);
737 /* Make sure we can Queue APCs and that this one isn't already inserted */
738 if (!(Thread
->ApcQueueable
) && (Apc
->Inserted
))
745 /* Set the System Arguments and set it as inserted */
746 Apc
->SystemArgument1
= SystemArgument1
;
747 Apc
->SystemArgument2
= SystemArgument2
;
748 Apc
->Inserted
= TRUE
;
750 /* Call the Internal Function */
751 KiInsertQueueApc(Apc
, PriorityBoost
);
754 /* Release the APC lock and return success */
755 KiReleaseApcLock(&ApcLock
);
756 KiExitDispatcher(ApcLock
.OldIrql
);
761 * @name KeFlushQueueApc
764 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
765 * from the specified Thread's APC queue.
768 * Pointer to the thread whose APC queue will be flushed.
770 * @paramt PreviousMode
771 * Specifies which APC Queue to flush.
773 * @return A pointer to the first entry in the flushed APC queue.
775 * @remarks If the routine returns NULL, it means that no APCs were flushed.
776 * Callers of this routine must be running at DISPATCH_LEVEL or lower.
781 KeFlushQueueApc(IN PKTHREAD Thread
,
782 IN KPROCESSOR_MODE PreviousMode
)
785 PLIST_ENTRY FirstEntry
, CurrentEntry
;
786 KLOCK_QUEUE_HANDLE ApcLock
;
787 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
789 /* Check if this was user mode */
790 if (PreviousMode
== UserMode
)
792 /* Get the APC lock */
793 KiAcquireApcLock(Thread
, &ApcLock
);
795 /* Select user list and check if it's empty */
796 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))
798 /* Don't return anything */
805 /* Select kernel list and check if it's empty */
806 if (IsListEmpty( &Thread
->ApcState
.ApcListHead
[KernelMode
]))
808 /* Don't return anything */
812 /* Otherwise, acquire the APC lock */
813 KiAcquireApcLock(Thread
, &ApcLock
);
816 /* Get the first entry and check if the list is empty now */
817 FirstEntry
= Thread
->ApcState
.ApcListHead
[PreviousMode
].Flink
;
818 if (FirstEntry
== &Thread
->ApcState
.ApcListHead
[PreviousMode
])
820 /* It is, clear the returned entry */
825 /* It's not, remove the first entry */
826 RemoveEntryList(&Thread
->ApcState
.ApcListHead
[PreviousMode
]);
828 /* Loop all the entries */
829 CurrentEntry
= FirstEntry
;
832 /* Get the APC and make it un-inserted */
833 Apc
= CONTAINING_RECORD(CurrentEntry
, KAPC
, ApcListEntry
);
834 Apc
->Inserted
= FALSE
;
836 /* Get the next entry */
837 CurrentEntry
= CurrentEntry
->Flink
;
838 } while (CurrentEntry
!= FirstEntry
);
840 /* Re-initialize the list */
841 InitializeListHead(&Thread
->ApcState
.ApcListHead
[PreviousMode
]);
844 /* Release the lock */
846 KiReleaseApcLock(&ApcLock
);
848 /* Return the first entry */
853 * @name KeRemoveQueueApc
856 * The KeRemoveQueueApc routine removes a given APC object from the system
860 * Pointer to an initialized APC object that was queued by calling
863 * @return TRUE if the APC Object is in the APC Queue. Otherwise, no operation
864 * is performed and FALSE is returned.
866 * @remarks If the given APC Object is currently queued, it is removed from the
867 * queue and any calls to the registered routines are cancelled.
869 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
874 KeRemoveQueueApc(IN PKAPC Apc
)
876 PKTHREAD Thread
= Apc
->Thread
;
877 PKAPC_STATE ApcState
;
879 KLOCK_QUEUE_HANDLE ApcLock
;
881 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
883 /* Get the APC lock */
884 KiAcquireApcLock(Thread
, &ApcLock
);
886 /* Check if it's inserted */
887 Inserted
= Apc
->Inserted
;
890 /* Set it as non-inserted and get the APC state */
891 Apc
->Inserted
= FALSE
;
892 ApcState
= Thread
->ApcStatePointer
[(UCHAR
)Apc
->ApcStateIndex
];
894 /* Acquire the dispatcher lock and remove it from the list */
895 KiAcquireDispatcherLockAtDpcLevel();
896 RemoveEntryList(&Apc
->ApcListEntry
);
898 /* If the Queue is completely empty, then no more APCs are pending */
899 if (IsListEmpty(&ApcState
->ApcListHead
[Apc
->ApcMode
]))
901 /* Set the correct state based on the APC Mode */
902 if (Apc
->ApcMode
== KernelMode
)
904 /* No more pending kernel APCs */
905 ApcState
->KernelApcPending
= FALSE
;
909 /* No more pending user APCs */
910 ApcState
->UserApcPending
= FALSE
;
914 /* Release dispatcher lock */
915 KiReleaseDispatcherLockFromDpcLevel();
918 /* Release the lock and return */
919 KiReleaseApcLock(&ApcLock
);
924 * @name KeAreApcsDisabled
927 * The KeAreApcsDisabled routine returns whether kernel APC delivery is
928 * disabled for the current thread.
932 * @return KeAreApcsDisabled returns TRUE if the thread is within a critical
933 * region or a guarded region, and FALSE otherwise.
935 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled
936 * determine if normal kernel APCs are disabled.
938 * A thread that is inside critical region has both user APCs and
939 * normal kernel APCs disabled, but not special kernel APCs.
941 * A thread that is inside a guarded region has all APCs disabled,
942 * including special kernel APCs.
944 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
949 KeAreApcsDisabled(VOID
)
951 /* Return the Kernel APC State */
952 return KeGetCurrentThread()->CombinedApcDisable
? TRUE
: FALSE
;
956 * @name KeAreAllApcsDisabled
959 * The KeAreAllApcsDisabled routine returns whether the calling thread is
960 * inside a guarded region or running at IRQL = APC_LEVEL, which disables
965 * @return KeAreAllApcsDisabled returns TRUE if the thread is within a guarded
966 * guarded region or running at IRQL >= APC_LEVEL, and FALSE otherwise.
968 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use this routine to
969 * determine if all APCs delivery is disabled.
971 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
976 KeAreAllApcsDisabled(VOID
)
978 /* Return the Special APC State */
979 return ((KeGetCurrentThread()->SpecialApcDisable
) ||
980 (KeGetCurrentIrql() >= APC_LEVEL
)) ? TRUE
: FALSE
;