2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/apc.c
5 * PURPOSE: Implements the Asynchronous Procedure Call mechanism
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES *****************************************************************/
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 macros from inside WIN32K or other Drivers, which will then only
31 * have to do an Import API call in the case where APCs are enabled again.
36 KiCheckForKernelApcDelivery(VOID
)
40 /* We should only deliver at passive */
41 if (KeGetCurrentIrql() == PASSIVE_LEVEL
)
43 /* Raise to APC and Deliver APCs, then lower back to Passive */
44 KeRaiseIrql(APC_LEVEL
, &OldIrql
);
45 KiDeliverApc(KernelMode
, 0, 0);
46 KeLowerIrql(PASSIVE_LEVEL
);
51 * If we're not at passive level it means someone raised IRQL
52 * to APC level before the critical or guarded section was entered
53 * (e.g) by a fast mutex). This implies that the APCs shouldn't
54 * be delivered now, but after the IRQL is lowered to passive
57 KeGetCurrentThread()->ApcState
.KernelApcPending
= TRUE
;
58 HalRequestSoftwareInterrupt(APC_LEVEL
);
63 * @name KiInsertQueueApc
65 * The KiInsertQueueApc routine queues a APC for execution when the right
66 * scheduler environment exists.
69 * Pointer to an initialized control object of type APC for which the
70 * caller provides the storage.
72 * @param PriorityBoost
73 * Priority Boost to apply to the Thread.
77 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
78 * and at PASSIVE_LEVEL for the NormalRoutine registered.
80 * Callers of this routine must have locked the dipatcher database.
85 KiInsertQueueApc(IN PKAPC Apc
,
86 IN KPRIORITY PriorityBoost
)
88 PKTHREAD Thread
= Apc
->Thread
;
90 KPROCESSOR_MODE ApcMode
;
91 PLIST_ENTRY ListHead
, NextEntry
;
95 BOOLEAN RequestInterrupt
= FALSE
;
98 * Check if the caller wanted this APC to use the thread's environment at
101 if (Apc
->ApcStateIndex
== InsertApcEnvironment
)
104 Apc
->ApcStateIndex
= Thread
->ApcStateIndex
;
107 /* Get the APC State for this Index, and the mode too */
108 ApcState
= Thread
->ApcStatePointer
[(UCHAR
)Apc
->ApcStateIndex
];
109 ApcMode
= Apc
->ApcMode
;
111 /* The APC must be "inserted" already */
112 ASSERT(Apc
->Inserted
== TRUE
);
115 * 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
116 * 2) User APC which is PsExitSpecialApc = Put it at the front of the List
117 * 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
119 if (Apc
->NormalRoutine
)
121 /* Normal APC; is it the Thread Termination APC? */
122 if ((ApcMode
!= KernelMode
) &&
123 (Apc
->KernelRoutine
== PsExitSpecialApc
))
125 /* Set User APC pending to true */
126 Thread
->ApcState
.UserApcPending
= TRUE
;
128 /* Insert it at the top of the list */
129 InsertHeadList(&ApcState
->ApcListHead
[ApcMode
],
134 /* Regular user or kernel Normal APC */
135 InsertTailList(&ApcState
->ApcListHead
[ApcMode
],
141 /* Special APC, find the last one in the list */
142 ListHead
= &ApcState
->ApcListHead
[ApcMode
];
143 NextEntry
= ListHead
->Blink
;
144 while (NextEntry
!= ListHead
)
147 QueuedApc
= CONTAINING_RECORD(NextEntry
, KAPC
, ApcListEntry
);
149 /* Is this a No-Normal APC? If so, break */
150 if (!QueuedApc
->NormalRoutine
) break;
152 /* Move to the previous APC in the Queue */
153 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 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 kernel-mode APC */
189 if (ApcMode
== KernelMode
)
191 /* Kernel-mode APC, set us pending */
192 Thread
->ApcState
.KernelApcPending
= TRUE
;
194 /* Are we currently running? */
195 if (Thread
->State
== Running
)
197 /* The thread is running, so remember to send a request */
198 RequestInterrupt
= TRUE
;
200 else if ((Thread
->State
== Waiting
) &&
201 (Thread
->WaitIrql
== PASSIVE_LEVEL
) &&
202 !(Thread
->SpecialApcDisable
) &&
203 (!(Apc
->NormalRoutine
) ||
204 (!(Thread
->KernelApcDisable
) &&
205 !(Thread
->ApcState
.KernelApcInProgress
))))
207 /* We'll unwait with this status */
208 Status
= STATUS_KERNEL_APC
;
210 /* Wake up the thread */
211 KiUnwaitThread(Thread
, Status
, PriorityBoost
);
213 else if (Thread
->State
== GateWait
)
215 /* Lock the thread */
216 KiAcquireThreadLock(Thread
);
218 /* Essentially do the same check as above */
219 if ((Thread
->State
== GateWait
) &&
220 (Thread
->WaitIrql
== PASSIVE_LEVEL
) &&
221 !(Thread
->SpecialApcDisable
) &&
222 (!(Apc
->NormalRoutine
) ||
223 (!(Thread
->KernelApcDisable
) &&
224 !(Thread
->ApcState
.KernelApcInProgress
))))
226 /* We were in a gate wait. Handle this. */
227 DPRINT1("A thread was in a gate wait\n");
230 Gate
= Thread
->GateObject
;
233 KiAcquireDispatcherObject(&Gate
->Header
);
235 /* Remove it from the waiters list */
236 RemoveEntryList(&Thread
->WaitBlock
[0].WaitListEntry
);
238 /* Unlock the gate */
239 KiReleaseDispatcherObject(&Gate
->Header
);
241 /* Increase the queue counter if needed */
242 if (Thread
->Queue
) Thread
->Queue
->CurrentCount
++;
244 /* Put into deferred ready list with this status */
245 Thread
->WaitStatus
= STATUS_KERNEL_APC
;
246 KiInsertDeferredReadyList(Thread
);
249 /* Release the thread lock */
250 KiReleaseThreadLock(Thread
);
253 else if ((Thread
->State
== Waiting
) &&
254 (Thread
->WaitMode
== UserMode
) &&
255 ((Thread
->Alertable
) ||
256 (Thread
->ApcState
.UserApcPending
)))
258 /* Set user-mode APC pending */
259 Thread
->ApcState
.UserApcPending
= TRUE
;
260 Status
= STATUS_USER_APC
;
262 /* Wake up the thread */
263 KiUnwaitThread(Thread
, Status
, PriorityBoost
);
266 /* Release dispatcher lock */
267 KiReleaseDispatcherLockFromDpcLevel();
269 /* Check if an interrupt was requested */
270 KiRequestApcInterrupt(RequestInterrupt
, Thread
->NextProcessor
);
279 * The KiDeliverApc routine is called from IRQL switching code if the
280 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
283 * @param DeliveryMode
284 * Specifies the current processor mode.
286 * @param ExceptionFrame
287 * Pointer to the Exception Frame on non-i386 builds.
290 * Pointer to the Trap Frame.
294 * @remarks First, Special APCs are delivered, followed by Kernel-Mode APCs and
295 * User-Mode APCs. Note that the TrapFrame is only valid if the
296 * delivery mode is User-Mode.
297 * Upon entry, this routine executes at APC_LEVEL.
302 KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode
,
303 IN PKEXCEPTION_FRAME ExceptionFrame
,
304 IN PKTRAP_FRAME TrapFrame
)
306 PKTHREAD Thread
= KeGetCurrentThread();
307 PKPROCESS Process
= Thread
->ApcState
.Process
;
308 PKTRAP_FRAME OldTrapFrame
;
309 PLIST_ENTRY ApcListEntry
;
311 KLOCK_QUEUE_HANDLE ApcLock
;
312 PKKERNEL_ROUTINE KernelRoutine
;
314 PKNORMAL_ROUTINE NormalRoutine
;
315 PVOID SystemArgument1
;
316 PVOID SystemArgument2
;
317 ASSERT_IRQL_EQUAL(APC_LEVEL
);
319 /* Save the old trap frame and set current one */
320 OldTrapFrame
= Thread
->TrapFrame
;
321 Thread
->TrapFrame
= TrapFrame
;
323 /* Clear Kernel APC Pending */
324 Thread
->ApcState
.KernelApcPending
= FALSE
;
326 /* Check if Special APCs are disabled */
327 if (Thread
->SpecialApcDisable
) goto Quickie
;
329 /* Do the Kernel APCs first */
330 while (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
332 /* Lock the APC Queue */
333 KiAcquireApcLockAtApcLevel(Thread
, &ApcLock
);
335 /* Check if the list became empty now */
336 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
338 /* It is, release the lock and break out */
339 KiReleaseApcLock(&ApcLock
);
343 /* Get the next Entry */
344 ApcListEntry
= Thread
->ApcState
.ApcListHead
[KernelMode
].Flink
;
345 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
347 /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
348 NormalRoutine
= Apc
->NormalRoutine
;
349 KernelRoutine
= Apc
->KernelRoutine
;
350 NormalContext
= Apc
->NormalContext
;
351 SystemArgument1
= Apc
->SystemArgument1
;
352 SystemArgument2
= Apc
->SystemArgument2
;
357 /* Remove the APC from the list */
358 RemoveEntryList(ApcListEntry
);
359 Apc
->Inserted
= FALSE
;
361 /* Release the APC lock */
362 KiReleaseApcLock(&ApcLock
);
364 /* Call the Special APC */
371 /* Make sure it returned correctly */
372 if (KeGetCurrentIrql() != ApcLock
.OldIrql
)
374 KeBugCheckEx(IRQL_UNEXPECTED_VALUE
,
375 (KeGetCurrentIrql() << 16) |
376 (ApcLock
.OldIrql
<< 8),
377 (ULONG_PTR
)KernelRoutine
,
379 (ULONG_PTR
)NormalRoutine
);
384 /* Normal Kernel APC, make sure it's safe to deliver */
385 if ((Thread
->ApcState
.KernelApcInProgress
) ||
386 (Thread
->KernelApcDisable
))
388 /* Release lock and return */
389 KiReleaseApcLock(&ApcLock
);
393 /* Dequeue the APC */
394 RemoveEntryList(ApcListEntry
);
395 Apc
->Inserted
= FALSE
;
397 /* Go back to APC_LEVEL */
398 KiReleaseApcLock(&ApcLock
);
400 /* Call the Kernel APC */
407 /* Make sure it returned correctly */
408 if (KeGetCurrentIrql() != ApcLock
.OldIrql
)
410 KeBugCheckEx(IRQL_UNEXPECTED_VALUE
,
411 (KeGetCurrentIrql() << 16) |
412 (ApcLock
.OldIrql
<< 8),
413 (ULONG_PTR
)KernelRoutine
,
415 (ULONG_PTR
)NormalRoutine
);
418 /* Check if there still is a Normal Routine */
421 /* At Passive Level, an APC can be prempted by a Special APC */
422 Thread
->ApcState
.KernelApcInProgress
= TRUE
;
423 KeLowerIrql(PASSIVE_LEVEL
);
425 /* Call and Raise IRQL back to APC_LEVEL */
426 NormalRoutine(NormalContext
, SystemArgument1
, SystemArgument2
);
427 KeRaiseIrql(APC_LEVEL
, &ApcLock
.OldIrql
);
430 /* Set Kernel APC in progress to false and loop again */
431 Thread
->ApcState
.KernelApcInProgress
= FALSE
;
435 /* Now we do the User APCs */
436 if ((DeliveryMode
== UserMode
) &&
437 !(IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])) &&
438 (Thread
->ApcState
.UserApcPending
))
440 /* Lock the APC Queue */
441 KiAcquireApcLockAtApcLevel(Thread
, &ApcLock
);
443 /* It's not pending anymore */
444 Thread
->ApcState
.UserApcPending
= FALSE
;
446 /* Check if the list became empty now */
447 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))
449 /* It is, release the lock and break out */
450 KiReleaseApcLock(&ApcLock
);
454 /* Get the actual APC object */
455 ApcListEntry
= Thread
->ApcState
.ApcListHead
[UserMode
].Flink
;
456 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
458 /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
459 NormalRoutine
= Apc
->NormalRoutine
;
460 KernelRoutine
= Apc
->KernelRoutine
;
461 NormalContext
= Apc
->NormalContext
;
462 SystemArgument1
= Apc
->SystemArgument1
;
463 SystemArgument2
= Apc
->SystemArgument2
;
465 /* Remove the APC from Queue, and release the lock */
466 RemoveEntryList(ApcListEntry
);
467 Apc
->Inserted
= FALSE
;
468 KiReleaseApcLock(&ApcLock
);
470 /* Call the kernel routine */
477 /* Check if there's no normal routine */
480 /* Check if more User APCs are Pending */
481 KeTestAlertThread(UserMode
);
485 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
486 KiInitializeUserApc(ExceptionFrame
,
496 /* Make sure we're still in the same process */
497 if (Process
!= Thread
->ApcState
.Process
)
499 /* Erm, we got attached or something! BAD! */
500 KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT
,
502 (ULONG_PTR
)Thread
->ApcState
.Process
,
503 Thread
->ApcStateIndex
,
504 KeGetCurrentPrcb()->DpcRoutineActive
);
507 /* Restore the trap frame */
508 Thread
->TrapFrame
= OldTrapFrame
;
513 RepairList(IN PLIST_ENTRY Original
,
515 IN KPROCESSOR_MODE Mode
)
517 /* Check if the list for this mode is empty */
518 if (IsListEmpty(&Original
[Mode
]))
520 /* It is, all we need to do is initialize it */
521 InitializeListHead(&Copy
[Mode
]);
526 Copy
[Mode
].Flink
= Original
[Mode
].Flink
;
527 Copy
[Mode
].Blink
= Original
[Mode
].Blink
;
528 Original
[Mode
].Flink
->Blink
= &Copy
[Mode
];
529 Original
[Mode
].Blink
->Flink
= &Copy
[Mode
];
535 KiMoveApcState(PKAPC_STATE OldState
,
536 PKAPC_STATE NewState
)
538 /* Restore backup of Original Environment */
539 RtlCopyMemory(NewState
, OldState
, KAPC_STATE_ACTUAL_LENGTH
);
542 RepairList(OldState
->ApcListHead
, NewState
->ApcListHead
, KernelMode
);
543 RepairList(OldState
->ApcListHead
, NewState
->ApcListHead
, UserMode
);
546 /* PUBLIC FUNCTIONS **********************************************************/
549 * @name KeEnterCriticalRegion
552 * The KeEnterCriticalRegion routine temporarily disables the delivery of
553 * normal kernel APCs; special kernel-mode APCs are still delivered.
559 * @remarks Highest-level drivers can call this routine while running in the
560 * context of the thread that requested the current I/O operation.
561 * Any caller of this routine should call KeLeaveCriticalRegion as
562 * quickly as possible.
564 * Callers of KeEnterCriticalRegion must be running at IRQL <=
570 _KeEnterCriticalRegion(VOID
)
572 /* Use inlined function */
573 KeEnterCriticalRegion();
577 * KeLeaveCriticalRegion
580 * The KeLeaveCriticalRegion routine reenables the delivery of normal
581 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
587 * @remarks Highest-level drivers can call this routine while running in the
588 * context of the thread that requested the current I/O operation.
590 * Callers of KeLeaveCriticalRegion must be running at IRQL <=
596 _KeLeaveCriticalRegion(VOID
)
598 /* Use inlined version */
599 KeLeaveCriticalRegion();
606 * The KeInitializeApc routine initializes an APC object, and registers
607 * the Kernel, Rundown and Normal routines for that object.
610 * Pointer to a KAPC structure that represents the APC object to
611 * initialize. The caller must allocate storage for the structure
612 * from resident memory.
615 * Thread to which to deliver the APC.
617 * @param TargetEnvironment
618 * APC Environment to be used.
620 * @param KernelRoutine
621 * Points to the KernelRoutine to associate with the APC.
622 * This routine is executed for all APCs.
624 * @param RundownRoutine
625 * Points to the RundownRoutine to associate with the APC.
626 * This routine is executed when the Thread exits during APC execution.
628 * @param NormalRoutine
629 * Points to the NormalRoutine to associate with the APC.
630 * This routine is executed at PASSIVE_LEVEL. If this is not specifed,
631 * the APC becomes a Special APC and the Mode and Context parameters are
635 * Specifies the processor mode at which to run the Normal Routine.
638 * Specifices the value to pass as Context parameter to the registered
643 * @remarks The caller can queue an initialized APC with KeInsertQueueApc.
648 KeInitializeApc(IN PKAPC Apc
,
650 IN KAPC_ENVIRONMENT TargetEnvironment
,
651 IN PKKERNEL_ROUTINE KernelRoutine
,
652 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL
,
653 IN PKNORMAL_ROUTINE NormalRoutine
,
654 IN KPROCESSOR_MODE Mode
,
658 ASSERT(TargetEnvironment
<= InsertApcEnvironment
);
660 /* Set up the basic APC Structure Data */
661 Apc
->Type
= ApcObject
;
662 Apc
->Size
= sizeof(KAPC
);
664 /* Set the Environment */
665 if (TargetEnvironment
== CurrentApcEnvironment
)
667 /* Use the current one for the thread */
668 Apc
->ApcStateIndex
= Thread
->ApcStateIndex
;
673 ASSERT((TargetEnvironment
<= Thread
->ApcStateIndex
) ||
674 (TargetEnvironment
== InsertApcEnvironment
));
676 /* Use the one that was given */
677 Apc
->ApcStateIndex
= TargetEnvironment
;
680 /* Set the Thread and Routines */
681 Apc
->Thread
= Thread
;
682 Apc
->KernelRoutine
= KernelRoutine
;
683 Apc
->RundownRoutine
= RundownRoutine
;
684 Apc
->NormalRoutine
= NormalRoutine
;
686 /* Check if this is a special APC */
689 /* It's a normal one. Set the context and mode */
691 Apc
->NormalContext
= Context
;
695 /* It's a special APC, which can only be kernel mode */
696 Apc
->ApcMode
= KernelMode
;
697 Apc
->NormalContext
= NULL
;
700 /* The APC is not inserted */
701 Apc
->Inserted
= FALSE
;
705 * @name KeInsertQueueApc
708 * The KeInsertQueueApc routine queues a APC for execution when the right
709 * scheduler environment exists.
712 * Pointer to an initialized control object of type APC for which the
713 * caller provides the storage.
715 * @param SystemArgument[1,2]
716 * Pointer to a set of two parameters that contain untyped data.
718 * @param PriorityBoost
719 * Priority Boost to apply to the Thread.
721 * @return If the APC is already inserted or APC queueing is disabled, FALSE.
724 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
725 * and at PASSIVE_LEVEL for the NormalRoutine registered.
727 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
732 KeInsertQueueApc(IN PKAPC Apc
,
733 IN PVOID SystemArgument1
,
734 IN PVOID SystemArgument2
,
735 IN KPRIORITY PriorityBoost
)
737 PKTHREAD Thread
= Apc
->Thread
;
738 KLOCK_QUEUE_HANDLE ApcLock
;
739 BOOLEAN State
= TRUE
;
741 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
743 /* Get the APC lock */
744 KiAcquireApcLock(Thread
, &ApcLock
);
746 /* Make sure we can Queue APCs and that this one isn't already inserted */
747 if (!(Thread
->ApcQueueable
) || (Apc
->Inserted
))
754 /* Set the System Arguments and set it as inserted */
755 Apc
->SystemArgument1
= SystemArgument1
;
756 Apc
->SystemArgument2
= SystemArgument2
;
757 Apc
->Inserted
= TRUE
;
759 /* Call the Internal Function */
760 KiInsertQueueApc(Apc
, PriorityBoost
);
763 /* Release the APC lock and return success */
764 KiReleaseApcLockFromDpcLevel(&ApcLock
);
765 KiExitDispatcher(ApcLock
.OldIrql
);
770 * @name KeFlushQueueApc
773 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
774 * from the specified Thread's APC queue.
777 * Pointer to the thread whose APC queue will be flushed.
779 * @param PreviousMode
780 * Specifies which APC Queue to flush.
782 * @return A pointer to the first entry in the flushed APC queue.
784 * @remarks If the routine returns NULL, it means that no APCs were flushed.
785 * Callers of this routine must be running at DISPATCH_LEVEL or lower.
790 KeFlushQueueApc(IN PKTHREAD Thread
,
791 IN KPROCESSOR_MODE PreviousMode
)
794 PLIST_ENTRY FirstEntry
, CurrentEntry
;
795 KLOCK_QUEUE_HANDLE ApcLock
;
796 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
798 /* Check if this was user mode */
799 if (PreviousMode
== UserMode
)
801 /* Get the APC lock */
802 KiAcquireApcLock(Thread
, &ApcLock
);
804 /* Select user list and check if it's empty */
805 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))
807 /* Don't return anything */
814 /* Select kernel list and check if it's empty */
815 if (IsListEmpty( &Thread
->ApcState
.ApcListHead
[KernelMode
]))
817 /* Don't return anything */
821 /* Otherwise, acquire the APC lock */
822 KiAcquireApcLock(Thread
, &ApcLock
);
825 /* Get the first entry and check if the list is empty now */
826 FirstEntry
= Thread
->ApcState
.ApcListHead
[PreviousMode
].Flink
;
827 if (FirstEntry
== &Thread
->ApcState
.ApcListHead
[PreviousMode
])
829 /* It is, clear the returned entry */
834 /* It's not, remove the first entry */
835 RemoveEntryList(&Thread
->ApcState
.ApcListHead
[PreviousMode
]);
837 /* Loop all the entries */
838 CurrentEntry
= FirstEntry
;
841 /* Get the APC and make it un-inserted */
842 Apc
= CONTAINING_RECORD(CurrentEntry
, KAPC
, ApcListEntry
);
843 Apc
->Inserted
= FALSE
;
845 /* Get the next entry */
846 CurrentEntry
= CurrentEntry
->Flink
;
847 } while (CurrentEntry
!= FirstEntry
);
849 /* Re-initialize the list */
850 InitializeListHead(&Thread
->ApcState
.ApcListHead
[PreviousMode
]);
853 /* Release the lock */
855 KiReleaseApcLock(&ApcLock
);
857 /* Return the first entry */
862 * @name KeRemoveQueueApc
865 * The KeRemoveQueueApc routine removes a given APC object from the system
869 * Pointer to an initialized APC object that was queued by calling
872 * @return TRUE if the APC Object is in the APC Queue. Otherwise, no operation
873 * is performed and FALSE is returned.
875 * @remarks If the given APC Object is currently queued, it is removed from the
876 * queue and any calls to the registered routines are cancelled.
878 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
883 KeRemoveQueueApc(IN PKAPC Apc
)
885 PKTHREAD Thread
= Apc
->Thread
;
886 PKAPC_STATE ApcState
;
888 KLOCK_QUEUE_HANDLE ApcLock
;
890 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
892 /* Get the APC lock */
893 KiAcquireApcLock(Thread
, &ApcLock
);
895 /* Check if it's inserted */
896 Inserted
= Apc
->Inserted
;
899 /* Set it as non-inserted and get the APC state */
900 Apc
->Inserted
= FALSE
;
901 ApcState
= Thread
->ApcStatePointer
[(UCHAR
)Apc
->ApcStateIndex
];
903 /* Acquire the dispatcher lock and remove it from the list */
904 KiAcquireDispatcherLockAtDpcLevel();
905 if (RemoveEntryList(&Apc
->ApcListEntry
))
907 /* Set the correct state based on the APC Mode */
908 if (Apc
->ApcMode
== KernelMode
)
910 /* No more pending kernel APCs */
911 ApcState
->KernelApcPending
= FALSE
;
915 /* No more pending user APCs */
916 ApcState
->UserApcPending
= FALSE
;
920 /* Release dispatcher lock */
921 KiReleaseDispatcherLockFromDpcLevel();
924 /* Release the lock and return */
925 KiReleaseApcLock(&ApcLock
);
930 * @name KeAreApcsDisabled
933 * The KeAreApcsDisabled routine returns whether kernel APC delivery is
934 * disabled for the current thread.
938 * @return KeAreApcsDisabled returns TRUE if the thread is within a critical
939 * region or a guarded region, and FALSE otherwise.
941 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled
942 * to determine if normal kernel APCs are disabled.
944 * A thread that is inside critical region has both user APCs and
945 * normal kernel APCs disabled, but not special kernel APCs.
947 * A thread that is inside a guarded region has all APCs disabled,
948 * including special kernel APCs.
950 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
955 KeAreApcsDisabled(VOID
)
957 /* Return the Kernel APC State */
958 return KeGetCurrentThread()->CombinedApcDisable
? TRUE
: FALSE
;
962 * @name KeAreAllApcsDisabled
965 * The KeAreAllApcsDisabled routine returns whether the calling thread is
966 * inside a guarded region or running at IRQL >= APC_LEVEL, which disables
971 * @return KeAreAllApcsDisabled returns TRUE if the thread is within a guarded
972 * guarded region or running at IRQL >= APC_LEVEL, and FALSE otherwise.
974 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use this routine to
975 * determine if all APC delivery is disabled.
977 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
982 KeAreAllApcsDisabled(VOID
)
984 /* Return the Special APC State */
985 return ((KeGetCurrentThread()->SpecialApcDisable
) ||
986 (KeGetCurrentIrql() >= APC_LEVEL
)) ? TRUE
: FALSE
;