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
= FALSE
;
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
->Blink
;
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
->Blink
;
154 InsertHeadList(NextEntry
, &Apc
->ApcListEntry
);
157 /* Now check if the Apc State Indexes match */
158 if (Thread
->ApcStateIndex
== Apc
->ApcStateIndex
)
160 /* Check that if the thread matches */
161 if (Thread
== KeGetCurrentThread())
164 ASSERT(Thread
->State
== Running
);
166 /* Check if this is kernel mode */
167 if (ApcMode
== KernelMode
)
169 /* All valid, a Kernel APC is pending now */
170 Thread
->ApcState
.KernelApcPending
= TRUE
;
172 /* Check if Special APCs are disabled */
173 if (!Thread
->SpecialApcDisable
)
175 /* They're not, so request the interrupt */
176 HalRequestSoftwareInterrupt(APC_LEVEL
);
182 /* Acquire the dispatcher lock */
183 KiAcquireDispatcherLock();
185 /* Check if this is a kernel-mode APC */
186 if (ApcMode
== KernelMode
)
188 /* Kernel-mode APC, set us pending */
189 Thread
->ApcState
.KernelApcPending
= TRUE
;
191 /* Are we currently running? */
192 if (Thread
->State
== Running
)
194 /* The thread is running, so remember to send a request */
195 RequestInterrupt
= TRUE
;
197 else if ((Thread
->State
== Waiting
) &&
198 (Thread
->WaitIrql
== PASSIVE_LEVEL
) &&
199 !(Thread
->SpecialApcDisable
) &&
200 (!(Apc
->NormalRoutine
) ||
201 (!(Thread
->KernelApcDisable
) &&
202 !(Thread
->ApcState
.KernelApcInProgress
))))
204 /* We'll unwait with this status */
205 Status
= STATUS_KERNEL_APC
;
207 /* Wake up the thread */
209 KiUnwaitThread(Thread
, Status
, PriorityBoost
);
211 else if (Thread
->State
== GateWait
)
213 /* We were in a gate wait. FIXME: Handle this */
214 DPRINT1("Not yet supported -- Report this to Alex\n");
218 else if ((Thread
->State
== Waiting
) &&
219 (Thread
->WaitMode
== UserMode
) &&
220 ((Thread
->Alertable
) ||
221 (Thread
->ApcState
.UserApcPending
)))
223 /* Set user-mode APC pending */
224 Thread
->ApcState
.UserApcPending
= TRUE
;
225 Status
= STATUS_USER_APC
;
229 /* Release dispatcher lock */
230 KiReleaseDispatcherLockFromDpcLevel();
232 /* Check if an interrupt was requested */
233 KiRequestApcInterrupt(RequestInterrupt
, Thread
->NextProcessor
);
242 * The KiDeliverApc routine is called from IRQL switching code if the
243 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
246 * @param DeliveryMode
247 * Specifies the current processor mode.
249 * @param ExceptionFrame
250 * Pointer to the Exception Frame on non-i386 builds.
253 * Pointer to the Trap Frame.
257 * @remarks First, Special APCs are delivered, followed by Kernel-Mode APCs and
258 * User-Mode APCs. Note that the TrapFrame is only valid if the
259 * delivery mode is User-Mode.
260 * Upon entry, this routine executes at APC_LEVEL.
265 KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode
,
266 IN PKEXCEPTION_FRAME ExceptionFrame
,
267 IN PKTRAP_FRAME TrapFrame
)
269 PKTHREAD Thread
= KeGetCurrentThread();
270 PKPROCESS Process
= Thread
->ApcState
.Process
;
271 PKTRAP_FRAME OldTrapFrame
;
272 PLIST_ENTRY ApcListEntry
;
274 KLOCK_QUEUE_HANDLE ApcLock
;
275 PKKERNEL_ROUTINE KernelRoutine
;
277 PKNORMAL_ROUTINE NormalRoutine
;
278 PVOID SystemArgument1
;
279 PVOID SystemArgument2
;
280 ASSERT_IRQL_EQUAL(APC_LEVEL
);
282 /* Save the old trap frame and set current one */
283 OldTrapFrame
= Thread
->TrapFrame
;
284 Thread
->TrapFrame
= TrapFrame
;
286 /* Clear Kernel APC Pending */
287 Thread
->ApcState
.KernelApcPending
= FALSE
;
289 /* Check if Special APCs are disabled */
290 if (Thread
->SpecialApcDisable
) goto Quickie
;
292 /* Do the Kernel APCs first */
293 while (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
295 /* Lock the APC Queue */
296 KiAcquireApcLockAtApcLevel(Thread
, &ApcLock
);
298 /* Check if the list became empty now */
299 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
301 /* It is, release the lock and break out */
302 KiReleaseApcLock(&ApcLock
);
306 /* Get the next Entry */
307 ApcListEntry
= Thread
->ApcState
.ApcListHead
[KernelMode
].Flink
;
308 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
310 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
311 NormalRoutine
= Apc
->NormalRoutine
;
312 KernelRoutine
= Apc
->KernelRoutine
;
313 NormalContext
= Apc
->NormalContext
;
314 SystemArgument1
= Apc
->SystemArgument1
;
315 SystemArgument2
= Apc
->SystemArgument2
;
320 /* Remove the APC from the list */
321 RemoveEntryList(ApcListEntry
);
322 Apc
->Inserted
= FALSE
;
324 /* Rrelease the APC lock */
325 KiReleaseApcLock(&ApcLock
);
327 /* Call the Special APC */
334 /* Make sure it returned correctly */
335 if (KeGetCurrentIrql() != ApcLock
.OldIrql
)
337 KeBugCheckEx(IRQL_UNEXPECTED_VALUE
,
338 (KeGetCurrentIrql() << 16) |
339 (ApcLock
.OldIrql
<< 8),
340 (ULONG_PTR
)KernelRoutine
,
342 (ULONG_PTR
)NormalRoutine
);
347 /* Normal Kernel APC, make sure it's safe to deliver */
348 if ((Thread
->ApcState
.KernelApcInProgress
) ||
349 (Thread
->KernelApcDisable
))
351 /* Release lock and return */
352 KiReleaseApcLock(&ApcLock
);
356 /* Dequeue the APC */
357 RemoveEntryList(ApcListEntry
);
358 Apc
->Inserted
= FALSE
;
360 /* Go back to APC_LEVEL */
361 KiReleaseApcLock(&ApcLock
);
363 /* Call the Kernel APC */
370 /* Make sure it returned correctly */
371 if (KeGetCurrentIrql() != ApcLock
.OldIrql
)
373 KeBugCheckEx(IRQL_UNEXPECTED_VALUE
,
374 (KeGetCurrentIrql() << 16) |
375 (ApcLock
.OldIrql
<< 8),
376 (ULONG_PTR
)KernelRoutine
,
378 (ULONG_PTR
)NormalRoutine
);
381 /* Check if There still is a Normal Routine */
384 /* At Passive Level, an APC can be prempted by a Special APC */
385 Thread
->ApcState
.KernelApcInProgress
= TRUE
;
386 KeLowerIrql(PASSIVE_LEVEL
);
388 /* Call and Raise IRQ back to APC_LEVEL */
389 NormalRoutine(NormalContext
, SystemArgument1
, SystemArgument2
);
390 KeRaiseIrql(APC_LEVEL
, &ApcLock
.OldIrql
);
393 /* Set Kernel APC in progress to false and loop again */
394 Thread
->ApcState
.KernelApcInProgress
= FALSE
;
398 /* Now we do the User APCs */
399 if ((DeliveryMode
== UserMode
) &&
400 !(IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])) &&
401 (Thread
->ApcState
.UserApcPending
))
403 /* Lock the APC Queue */
404 KiAcquireApcLockAtApcLevel(Thread
, &ApcLock
);
406 /* It's not pending anymore */
407 Thread
->ApcState
.UserApcPending
= FALSE
;
409 /* Check if the list became empty now */
410 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))
412 /* It is, release the lock and break out */
413 KiReleaseApcLock(&ApcLock
);
417 /* Get the actual APC object */
418 ApcListEntry
= Thread
->ApcState
.ApcListHead
[UserMode
].Flink
;
419 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
421 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
422 NormalRoutine
= Apc
->NormalRoutine
;
423 KernelRoutine
= Apc
->KernelRoutine
;
424 NormalContext
= Apc
->NormalContext
;
425 SystemArgument1
= Apc
->SystemArgument1
;
426 SystemArgument2
= Apc
->SystemArgument2
;
428 /* Remove the APC from Queue, and release the lock */
429 RemoveEntryList(ApcListEntry
);
430 Apc
->Inserted
= FALSE
;
431 KiReleaseApcLock(&ApcLock
);
433 /* Call the kernel routine */
440 /* Check if there's no normal routine */
443 /* Check if more User APCs are Pending */
444 KeTestAlertThread(UserMode
);
448 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
449 KiInitializeUserApc(ExceptionFrame
,
459 /* Make sure we're still in the same process */
460 if (Process
!= Thread
->ApcState
.Process
)
462 /* Erm, we got attached or something! BAD! */
463 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
,
465 (ULONG_PTR
)Thread
->ApcState
.Process
,
466 Thread
->ApcStateIndex
,
467 KeGetCurrentPrcb()->DpcRoutineActive
);
470 /* Restore the trap frame */
471 Thread
->TrapFrame
= OldTrapFrame
;
476 RepairList(IN PLIST_ENTRY Original
,
478 IN KPROCESSOR_MODE Mode
)
480 /* Check if the list for this mode is empty */
481 if (IsListEmpty(&Original
[Mode
]))
483 /* It is, all we need to do is initialize it */
484 InitializeListHead(&Copy
[Mode
]);
489 Copy
[Mode
].Flink
= Original
[Mode
].Flink
;
490 Copy
[Mode
].Blink
= Original
[Mode
].Blink
;
491 Original
[Mode
].Flink
->Blink
= &Copy
[Mode
];
492 Original
[Mode
].Blink
->Flink
= &Copy
[Mode
];
498 KiMoveApcState(PKAPC_STATE OldState
,
499 PKAPC_STATE NewState
)
501 /* Restore backup of Original Environment */
502 RtlCopyMemory(NewState
, OldState
, KAPC_STATE_ACTUAL_LENGTH
);
505 RepairList(NewState
->ApcListHead
, OldState
->ApcListHead
, KernelMode
);
506 RepairList(NewState
->ApcListHead
, OldState
->ApcListHead
, UserMode
);
509 /* PUBLIC FUNCTIONS **********************************************************/
512 * @name KeEnterCriticalRegion
515 * The KeEnterCriticalRegion routine temporarily disables the delivery of
516 * normal kernel APCs; special kernel-mode APCs are still delivered.
522 * @remarks Highest-level drivers can call this routine while running in the
523 * context of the thread that requested the current I/O operation.
524 * Any caller of this routine should call KeLeaveCriticalRegion as
525 * quickly as possible.
527 * Callers of KeEnterCriticalRegion must be running at IRQL <=
533 _KeEnterCriticalRegion(VOID
)
535 /* Use inlined function */
536 KeEnterCriticalRegion();
540 * KeLeaveCriticalRegion
543 * The KeLeaveCriticalRegion routine reenables the delivery of normal
544 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
550 * @remarks Highest-level drivers can call this routine while running in the
551 * context of the thread that requested the current I/O operation.
553 * Callers of KeLeaveCriticalRegion must be running at IRQL <=
559 _KeLeaveCriticalRegion(VOID
)
561 /* Use inlined version */
562 KeLeaveCriticalRegion();
569 * The The KeInitializeApc routine initializes an APC object, and registers
570 * the Kernel, Rundown and Normal routines for that object.
573 * Pointer to a KAPC structure that represents the APC object to
574 * initialize. The caller must allocate storage for the structure
575 * from resident memory.
578 * Thread to which to deliver the APC.
580 * @param TargetEnvironment
581 * APC Environment to be used.
583 * @param KernelRoutine
584 * Points to the KernelRoutine to associate with the APC.
585 * This routine is executed for all APCs.
587 * @param RundownRoutine
588 * Points to the RundownRoutine to associate with the APC.
589 * This routine is executed when the Thread exists during APC execution.
591 * @param NormalRoutine
592 * Points to the NormalRoutine to associate with the APC.
593 * This routine is executed at PASSIVE_LEVEL. If this is not specifed,
594 * the APC becomes a Special APC and the Mode and Context parameters are
598 * Specifies the processor mode at which to run the Normal Routine.
601 * Specifices the value to pass as Context parameter to the registered
606 * @remarks The caller can queue an initialized APC with KeInsertQueueApc.
611 KeInitializeApc(IN PKAPC Apc
,
613 IN KAPC_ENVIRONMENT TargetEnvironment
,
614 IN PKKERNEL_ROUTINE KernelRoutine
,
615 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL
,
616 IN PKNORMAL_ROUTINE NormalRoutine
,
617 IN KPROCESSOR_MODE Mode
,
621 ASSERT(TargetEnvironment
<= InsertApcEnvironment
);
623 /* Set up the basic APC Structure Data */
624 Apc
->Type
= ApcObject
;
625 Apc
->Size
= sizeof(KAPC
);
627 /* Set the Environment */
628 if (TargetEnvironment
== CurrentApcEnvironment
)
630 /* Use the current one for the thread */
631 Apc
->ApcStateIndex
= Thread
->ApcStateIndex
;
636 ASSERT((TargetEnvironment
<= Thread
->ApcStateIndex
) ||
637 (TargetEnvironment
== InsertApcEnvironment
));
639 /* Use the one that was given */
640 Apc
->ApcStateIndex
= TargetEnvironment
;
643 /* Set the Thread and Routines */
644 Apc
->Thread
= Thread
;
645 Apc
->KernelRoutine
= KernelRoutine
;
646 Apc
->RundownRoutine
= RundownRoutine
;
647 Apc
->NormalRoutine
= NormalRoutine
;
649 /* Check if this is a special APC */
652 /* It's a normal one. Set the context and mode */
654 Apc
->NormalContext
= Context
;
658 /* It's a special APC, which can only be kernel mode */
659 Apc
->ApcMode
= KernelMode
;
660 Apc
->NormalContext
= NULL
;
663 /* The APC is not inserted*/
664 Apc
->Inserted
= FALSE
;
668 * @name KeInsertQueueApc
671 * The KeInsertQueueApc routine queues a APC for execution when the right
672 * scheduler environment exists.
675 * Pointer to an initialized control object of type DPC for which the
676 * caller provides the storage.
678 * @param SystemArgument[1,2]
679 * Pointer to a set of two parameters that contain untyped data.
681 * @param PriorityBoost
682 * Priority Boost to apply to the Thread.
684 * @return If the APC is already inserted or APC queueing is disabled, FALSE.
687 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
688 * and at PASSIVE_LEVEL for the NormalRoutine registered.
690 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
695 KeInsertQueueApc(IN PKAPC Apc
,
696 IN PVOID SystemArgument1
,
697 IN PVOID SystemArgument2
,
698 IN KPRIORITY PriorityBoost
)
700 PKTHREAD Thread
= Apc
->Thread
;
701 KLOCK_QUEUE_HANDLE ApcLock
;
702 BOOLEAN State
= TRUE
;
704 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
706 /* Get the APC lock */
707 KiAcquireApcLock(Thread
, &ApcLock
);
709 /* Make sure we can Queue APCs and that this one isn't already inserted */
710 if (!(Thread
->ApcQueueable
) && (Apc
->Inserted
))
717 /* Set the System Arguments and set it as inserted */
718 Apc
->SystemArgument1
= SystemArgument1
;
719 Apc
->SystemArgument2
= SystemArgument2
;
720 Apc
->Inserted
= TRUE
;
722 /* Call the Internal Function */
723 KiInsertQueueApc(Apc
, PriorityBoost
);
726 /* Release the APC lock and return success */
727 KiReleaseApcLockFromDpcLevel(&ApcLock
);
728 KiExitDispatcher(ApcLock
.OldIrql
);
733 * @name KeFlushQueueApc
736 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
737 * from the specified Thread's APC queue.
740 * Pointer to the thread whose APC queue will be flushed.
742 * @paramt PreviousMode
743 * Specifies which APC Queue to flush.
745 * @return A pointer to the first entry in the flushed APC queue.
747 * @remarks If the routine returns NULL, it means that no APCs were flushed.
748 * Callers of this routine must be running at DISPATCH_LEVEL or lower.
753 KeFlushQueueApc(IN PKTHREAD Thread
,
754 IN KPROCESSOR_MODE PreviousMode
)
757 PLIST_ENTRY FirstEntry
, CurrentEntry
;
758 KLOCK_QUEUE_HANDLE ApcLock
;
759 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
761 /* Check if this was user mode */
762 if (PreviousMode
== UserMode
)
764 /* Get the APC lock */
765 KiAcquireApcLock(Thread
, &ApcLock
);
767 /* Select user list and check if it's empty */
768 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))
770 /* Don't return anything */
777 /* Select kernel list and check if it's empty */
778 if (IsListEmpty( &Thread
->ApcState
.ApcListHead
[KernelMode
]))
780 /* Don't return anything */
784 /* Otherwise, acquire the APC lock */
785 KiAcquireApcLock(Thread
, &ApcLock
);
788 /* Get the first entry and check if the list is empty now */
789 FirstEntry
= Thread
->ApcState
.ApcListHead
[PreviousMode
].Flink
;
790 if (FirstEntry
== &Thread
->ApcState
.ApcListHead
[PreviousMode
])
792 /* It is, clear the returned entry */
797 /* It's not, remove the first entry */
798 RemoveEntryList(&Thread
->ApcState
.ApcListHead
[PreviousMode
]);
800 /* Loop all the entries */
801 CurrentEntry
= FirstEntry
;
804 /* Get the APC and make it un-inserted */
805 Apc
= CONTAINING_RECORD(CurrentEntry
, KAPC
, ApcListEntry
);
806 Apc
->Inserted
= FALSE
;
808 /* Get the next entry */
809 CurrentEntry
= CurrentEntry
->Flink
;
810 } while (CurrentEntry
!= FirstEntry
);
812 /* Re-initialize the list */
813 InitializeListHead(&Thread
->ApcState
.ApcListHead
[PreviousMode
]);
816 /* Release the lock */
818 KiReleaseApcLock(&ApcLock
);
820 /* Return the first entry */
825 * @name KeRemoveQueueApc
828 * The KeRemoveQueueApc routine removes a given APC object from the system
832 * Pointer to an initialized APC object that was queued by calling
835 * @return TRUE if the APC Object is in the APC Queue. Otherwise, no operation
836 * is performed and FALSE is returned.
838 * @remarks If the given APC Object is currently queued, it is removed from the
839 * queue and any calls to the registered routines are cancelled.
841 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
846 KeRemoveQueueApc(IN PKAPC Apc
)
848 PKTHREAD Thread
= Apc
->Thread
;
849 PKAPC_STATE ApcState
;
851 KLOCK_QUEUE_HANDLE ApcLock
;
853 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
855 /* Get the APC lock */
856 KiAcquireApcLock(Thread
, &ApcLock
);
858 /* Check if it's inserted */
859 Inserted
= Apc
->Inserted
;
862 /* Set it as non-inserted and get the APC state */
863 Apc
->Inserted
= FALSE
;
864 ApcState
= Thread
->ApcStatePointer
[(UCHAR
)Apc
->ApcStateIndex
];
866 /* Acquire the dispatcher lock and remove it from the list */
867 KiAcquireDispatcherLockAtDpcLevel();
868 if (RemoveEntryList(&ApcState
->ApcListHead
[Apc
->ApcMode
]))
870 /* Set the correct state based on the APC Mode */
871 if (Apc
->ApcMode
== KernelMode
)
873 /* No more pending kernel APCs */
874 ApcState
->KernelApcPending
= FALSE
;
878 /* No more pending user APCs */
879 ApcState
->UserApcPending
= FALSE
;
883 /* Release dispatcher lock */
884 KiReleaseDispatcherLockFromDpcLevel();
887 /* Release the lock and return */
888 KiReleaseApcLock(&ApcLock
);
893 * @name KeAreApcsDisabled
896 * The KeAreApcsDisabled routine returns whether kernel APC delivery is
897 * disabled for the current thread.
901 * @return KeAreApcsDisabled returns TRUE if the thread is within a critical
902 * region or a guarded region, and FALSE otherwise.
904 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled
905 * determine if normal kernel APCs are disabled.
907 * A thread that is inside critical region has both user APCs and
908 * normal kernel APCs disabled, but not special kernel APCs.
910 * A thread that is inside a guarded region has all APCs disabled,
911 * including special kernel APCs.
913 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
918 KeAreApcsDisabled(VOID
)
920 /* Return the Kernel APC State */
921 return KeGetCurrentThread()->CombinedApcDisable
? TRUE
: FALSE
;
925 * @name KeAreAllApcsDisabled
928 * The KeAreAllApcsDisabled routine returns whether the calling thread is
929 * inside a guarded region or running at IRQL = APC_LEVEL, which disables
934 * @return KeAreAllApcsDisabled returns TRUE if the thread is within a guarded
935 * guarded region or running at IRQL >= APC_LEVEL, and FALSE otherwise.
937 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use this routine to
938 * determine if all APCs delivery is disabled.
940 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
945 KeAreAllApcsDisabled(VOID
)
947 /* Return the Special APC State */
948 return ((KeGetCurrentThread()->SpecialApcDisable
) ||
949 (KeGetCurrentIrql() >= APC_LEVEL
)) ? TRUE
: FALSE
;