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 /* Kernel APC is not pending anymore */
344 Thread
->ApcState
.KernelApcPending
= FALSE
;
346 /* Get the next Entry */
347 ApcListEntry
= Thread
->ApcState
.ApcListHead
[KernelMode
].Flink
;
348 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
350 /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
351 NormalRoutine
= Apc
->NormalRoutine
;
352 KernelRoutine
= Apc
->KernelRoutine
;
353 NormalContext
= Apc
->NormalContext
;
354 SystemArgument1
= Apc
->SystemArgument1
;
355 SystemArgument2
= Apc
->SystemArgument2
;
360 /* Remove the APC from the list */
361 RemoveEntryList(ApcListEntry
);
362 Apc
->Inserted
= FALSE
;
364 /* Release the APC lock */
365 KiReleaseApcLock(&ApcLock
);
367 /* Call the Special APC */
374 /* Make sure it returned correctly */
375 if (KeGetCurrentIrql() != ApcLock
.OldIrql
)
377 KeBugCheckEx(IRQL_UNEXPECTED_VALUE
,
378 (KeGetCurrentIrql() << 16) |
379 (ApcLock
.OldIrql
<< 8),
380 (ULONG_PTR
)KernelRoutine
,
382 (ULONG_PTR
)NormalRoutine
);
387 /* Normal Kernel APC, make sure it's safe to deliver */
388 if ((Thread
->ApcState
.KernelApcInProgress
) ||
389 (Thread
->KernelApcDisable
))
391 /* Release lock and return */
392 KiReleaseApcLock(&ApcLock
);
396 /* Dequeue the APC */
397 RemoveEntryList(ApcListEntry
);
398 Apc
->Inserted
= FALSE
;
400 /* Go back to APC_LEVEL */
401 KiReleaseApcLock(&ApcLock
);
403 /* Call the Kernel APC */
410 /* Make sure it returned correctly */
411 if (KeGetCurrentIrql() != ApcLock
.OldIrql
)
413 KeBugCheckEx(IRQL_UNEXPECTED_VALUE
,
414 (KeGetCurrentIrql() << 16) |
415 (ApcLock
.OldIrql
<< 8),
416 (ULONG_PTR
)KernelRoutine
,
418 (ULONG_PTR
)NormalRoutine
);
421 /* Check if there still is a Normal Routine */
424 /* At Passive Level, an APC can be prempted by a Special APC */
425 Thread
->ApcState
.KernelApcInProgress
= TRUE
;
426 KeLowerIrql(PASSIVE_LEVEL
);
428 /* Call and Raise IRQL back to APC_LEVEL */
429 NormalRoutine(NormalContext
, SystemArgument1
, SystemArgument2
);
430 KeRaiseIrql(APC_LEVEL
, &ApcLock
.OldIrql
);
433 /* Set Kernel APC in progress to false and loop again */
434 Thread
->ApcState
.KernelApcInProgress
= FALSE
;
438 /* Now we do the User APCs */
439 if ((DeliveryMode
== UserMode
) &&
440 !(IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])) &&
441 (Thread
->ApcState
.UserApcPending
))
443 /* Lock the APC Queue */
444 KiAcquireApcLockAtApcLevel(Thread
, &ApcLock
);
446 /* It's not pending anymore */
447 Thread
->ApcState
.UserApcPending
= FALSE
;
449 /* Check if the list became empty now */
450 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))
452 /* It is, release the lock and break out */
453 KiReleaseApcLock(&ApcLock
);
457 /* Get the actual APC object */
458 ApcListEntry
= Thread
->ApcState
.ApcListHead
[UserMode
].Flink
;
459 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
461 /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
462 NormalRoutine
= Apc
->NormalRoutine
;
463 KernelRoutine
= Apc
->KernelRoutine
;
464 NormalContext
= Apc
->NormalContext
;
465 SystemArgument1
= Apc
->SystemArgument1
;
466 SystemArgument2
= Apc
->SystemArgument2
;
468 /* Remove the APC from Queue, and release the lock */
469 RemoveEntryList(ApcListEntry
);
470 Apc
->Inserted
= FALSE
;
471 KiReleaseApcLock(&ApcLock
);
473 /* Call the kernel routine */
480 /* Check if there's no normal routine */
483 /* Check if more User APCs are Pending */
484 KeTestAlertThread(UserMode
);
488 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
489 KiInitializeUserApc(ExceptionFrame
,
499 /* Make sure we're still in the same process */
500 if (Process
!= Thread
->ApcState
.Process
)
502 /* Erm, we got attached or something! BAD! */
503 KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT
,
505 (ULONG_PTR
)Thread
->ApcState
.Process
,
506 Thread
->ApcStateIndex
,
507 KeGetCurrentPrcb()->DpcRoutineActive
);
510 /* Restore the trap frame */
511 Thread
->TrapFrame
= OldTrapFrame
;
516 RepairList(IN PLIST_ENTRY Original
,
518 IN KPROCESSOR_MODE Mode
)
520 /* Check if the list for this mode is empty */
521 if (IsListEmpty(&Original
[Mode
]))
523 /* It is, all we need to do is initialize it */
524 InitializeListHead(&Copy
[Mode
]);
529 Copy
[Mode
].Flink
= Original
[Mode
].Flink
;
530 Copy
[Mode
].Blink
= Original
[Mode
].Blink
;
531 Original
[Mode
].Flink
->Blink
= &Copy
[Mode
];
532 Original
[Mode
].Blink
->Flink
= &Copy
[Mode
];
538 KiMoveApcState(PKAPC_STATE OldState
,
539 PKAPC_STATE NewState
)
541 /* Restore backup of Original Environment */
542 RtlCopyMemory(NewState
, OldState
, KAPC_STATE_ACTUAL_LENGTH
);
545 RepairList(OldState
->ApcListHead
, NewState
->ApcListHead
, KernelMode
);
546 RepairList(OldState
->ApcListHead
, NewState
->ApcListHead
, UserMode
);
549 /* PUBLIC FUNCTIONS **********************************************************/
552 * @name KeEnterCriticalRegion
555 * The KeEnterCriticalRegion routine temporarily disables the delivery of
556 * normal kernel APCs; special kernel-mode APCs are still delivered.
562 * @remarks Highest-level drivers can call this routine while running in the
563 * context of the thread that requested the current I/O operation.
564 * Any caller of this routine should call KeLeaveCriticalRegion as
565 * quickly as possible.
567 * Callers of KeEnterCriticalRegion must be running at IRQL <=
573 _KeEnterCriticalRegion(VOID
)
575 /* Use inlined function */
576 KeEnterCriticalRegion();
580 * KeLeaveCriticalRegion
583 * The KeLeaveCriticalRegion routine reenables the delivery of normal
584 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
590 * @remarks Highest-level drivers can call this routine while running in the
591 * context of the thread that requested the current I/O operation.
593 * Callers of KeLeaveCriticalRegion must be running at IRQL <=
599 _KeLeaveCriticalRegion(VOID
)
601 /* Use inlined version */
602 KeLeaveCriticalRegion();
609 * The KeInitializeApc routine initializes an APC object, and registers
610 * the Kernel, Rundown and Normal routines for that object.
613 * Pointer to a KAPC structure that represents the APC object to
614 * initialize. The caller must allocate storage for the structure
615 * from resident memory.
618 * Thread to which to deliver the APC.
620 * @param TargetEnvironment
621 * APC Environment to be used.
623 * @param KernelRoutine
624 * Points to the KernelRoutine to associate with the APC.
625 * This routine is executed for all APCs.
627 * @param RundownRoutine
628 * Points to the RundownRoutine to associate with the APC.
629 * This routine is executed when the Thread exits during APC execution.
631 * @param NormalRoutine
632 * Points to the NormalRoutine to associate with the APC.
633 * This routine is executed at PASSIVE_LEVEL. If this is not specifed,
634 * the APC becomes a Special APC and the Mode and Context parameters are
638 * Specifies the processor mode at which to run the Normal Routine.
641 * Specifices the value to pass as Context parameter to the registered
646 * @remarks The caller can queue an initialized APC with KeInsertQueueApc.
651 KeInitializeApc(IN PKAPC Apc
,
653 IN KAPC_ENVIRONMENT TargetEnvironment
,
654 IN PKKERNEL_ROUTINE KernelRoutine
,
655 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL
,
656 IN PKNORMAL_ROUTINE NormalRoutine
,
657 IN KPROCESSOR_MODE Mode
,
661 ASSERT(TargetEnvironment
<= InsertApcEnvironment
);
663 /* Set up the basic APC Structure Data */
664 Apc
->Type
= ApcObject
;
665 Apc
->Size
= sizeof(KAPC
);
667 /* Set the Environment */
668 if (TargetEnvironment
== CurrentApcEnvironment
)
670 /* Use the current one for the thread */
671 Apc
->ApcStateIndex
= Thread
->ApcStateIndex
;
676 ASSERT((TargetEnvironment
<= Thread
->ApcStateIndex
) ||
677 (TargetEnvironment
== InsertApcEnvironment
));
679 /* Use the one that was given */
680 Apc
->ApcStateIndex
= TargetEnvironment
;
683 /* Set the Thread and Routines */
684 Apc
->Thread
= Thread
;
685 Apc
->KernelRoutine
= KernelRoutine
;
686 Apc
->RundownRoutine
= RundownRoutine
;
687 Apc
->NormalRoutine
= NormalRoutine
;
689 /* Check if this is a special APC */
692 /* It's a normal one. Set the context and mode */
694 Apc
->NormalContext
= Context
;
698 /* It's a special APC, which can only be kernel mode */
699 Apc
->ApcMode
= KernelMode
;
700 Apc
->NormalContext
= NULL
;
703 /* The APC is not inserted */
704 Apc
->Inserted
= FALSE
;
708 * @name KeInsertQueueApc
711 * The KeInsertQueueApc routine queues a APC for execution when the right
712 * scheduler environment exists.
715 * Pointer to an initialized control object of type APC for which the
716 * caller provides the storage.
718 * @param SystemArgument[1,2]
719 * Pointer to a set of two parameters that contain untyped data.
721 * @param PriorityBoost
722 * Priority Boost to apply to the Thread.
724 * @return If the APC is already inserted or APC queueing is disabled, FALSE.
727 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
728 * and at PASSIVE_LEVEL for the NormalRoutine registered.
730 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
735 KeInsertQueueApc(IN PKAPC Apc
,
736 IN PVOID SystemArgument1
,
737 IN PVOID SystemArgument2
,
738 IN KPRIORITY PriorityBoost
)
740 PKTHREAD Thread
= Apc
->Thread
;
741 KLOCK_QUEUE_HANDLE ApcLock
;
742 BOOLEAN State
= TRUE
;
744 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
746 /* Get the APC lock */
747 KiAcquireApcLock(Thread
, &ApcLock
);
749 /* Make sure we can Queue APCs and that this one isn't already inserted */
750 if (!(Thread
->ApcQueueable
) || (Apc
->Inserted
))
757 /* Set the System Arguments and set it as inserted */
758 Apc
->SystemArgument1
= SystemArgument1
;
759 Apc
->SystemArgument2
= SystemArgument2
;
760 Apc
->Inserted
= TRUE
;
762 /* Call the Internal Function */
763 KiInsertQueueApc(Apc
, PriorityBoost
);
766 /* Release the APC lock and return success */
767 KiReleaseApcLockFromDpcLevel(&ApcLock
);
768 KiExitDispatcher(ApcLock
.OldIrql
);
773 * @name KeFlushQueueApc
776 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
777 * from the specified Thread's APC queue.
780 * Pointer to the thread whose APC queue will be flushed.
782 * @param PreviousMode
783 * Specifies which APC Queue to flush.
785 * @return A pointer to the first entry in the flushed APC queue.
787 * @remarks If the routine returns NULL, it means that no APCs were flushed.
788 * Callers of this routine must be running at DISPATCH_LEVEL or lower.
793 KeFlushQueueApc(IN PKTHREAD Thread
,
794 IN KPROCESSOR_MODE PreviousMode
)
797 PLIST_ENTRY FirstEntry
, CurrentEntry
;
798 KLOCK_QUEUE_HANDLE ApcLock
;
799 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
801 /* Check if this was user mode */
802 if (PreviousMode
== UserMode
)
804 /* Get the APC lock */
805 KiAcquireApcLock(Thread
, &ApcLock
);
807 /* Select user list and check if it's empty */
808 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))
810 /* Don't return anything */
817 /* Select kernel list and check if it's empty */
818 if (IsListEmpty( &Thread
->ApcState
.ApcListHead
[KernelMode
]))
820 /* Don't return anything */
824 /* Otherwise, acquire the APC lock */
825 KiAcquireApcLock(Thread
, &ApcLock
);
828 /* Get the first entry and check if the list is empty now */
829 FirstEntry
= Thread
->ApcState
.ApcListHead
[PreviousMode
].Flink
;
830 if (FirstEntry
== &Thread
->ApcState
.ApcListHead
[PreviousMode
])
832 /* It is, clear the returned entry */
837 /* It's not, remove the first entry */
838 RemoveEntryList(&Thread
->ApcState
.ApcListHead
[PreviousMode
]);
840 /* Loop all the entries */
841 CurrentEntry
= FirstEntry
;
844 /* Get the APC and make it un-inserted */
845 Apc
= CONTAINING_RECORD(CurrentEntry
, KAPC
, ApcListEntry
);
846 Apc
->Inserted
= FALSE
;
848 /* Get the next entry */
849 CurrentEntry
= CurrentEntry
->Flink
;
850 } while (CurrentEntry
!= FirstEntry
);
852 /* Re-initialize the list */
853 InitializeListHead(&Thread
->ApcState
.ApcListHead
[PreviousMode
]);
856 /* Release the lock */
858 KiReleaseApcLock(&ApcLock
);
860 /* Return the first entry */
865 * @name KeRemoveQueueApc
868 * The KeRemoveQueueApc routine removes a given APC object from the system
872 * Pointer to an initialized APC object that was queued by calling
875 * @return TRUE if the APC Object is in the APC Queue. Otherwise, no operation
876 * is performed and FALSE is returned.
878 * @remarks If the given APC Object is currently queued, it is removed from the
879 * queue and any calls to the registered routines are cancelled.
881 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
886 KeRemoveQueueApc(IN PKAPC Apc
)
888 PKTHREAD Thread
= Apc
->Thread
;
889 PKAPC_STATE ApcState
;
891 KLOCK_QUEUE_HANDLE ApcLock
;
893 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
895 /* Get the APC lock */
896 KiAcquireApcLock(Thread
, &ApcLock
);
898 /* Check if it's inserted */
899 Inserted
= Apc
->Inserted
;
902 /* Set it as non-inserted and get the APC state */
903 Apc
->Inserted
= FALSE
;
904 ApcState
= Thread
->ApcStatePointer
[(UCHAR
)Apc
->ApcStateIndex
];
906 /* Acquire the dispatcher lock and remove it from the list */
907 KiAcquireDispatcherLockAtDpcLevel();
908 if (RemoveEntryList(&Apc
->ApcListEntry
))
910 /* Set the correct state based on the APC Mode */
911 if (Apc
->ApcMode
== KernelMode
)
913 /* No more pending kernel APCs */
914 ApcState
->KernelApcPending
= FALSE
;
918 /* No more pending user APCs */
919 ApcState
->UserApcPending
= FALSE
;
923 /* Release dispatcher lock */
924 KiReleaseDispatcherLockFromDpcLevel();
927 /* Release the lock and return */
928 KiReleaseApcLock(&ApcLock
);
933 * @name KeAreApcsDisabled
936 * The KeAreApcsDisabled routine returns whether kernel APC delivery is
937 * disabled for the current thread.
941 * @return KeAreApcsDisabled returns TRUE if the thread is within a critical
942 * region or a guarded region, and FALSE otherwise.
944 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled
945 * to determine if normal kernel APCs are disabled.
947 * A thread that is inside critical region has both user APCs and
948 * normal kernel APCs disabled, but not special kernel APCs.
950 * A thread that is inside a guarded region has all APCs disabled,
951 * including special kernel APCs.
953 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
958 KeAreApcsDisabled(VOID
)
960 /* Return the Kernel APC State */
961 return KeGetCurrentThread()->CombinedApcDisable
? TRUE
: FALSE
;
965 * @name KeAreAllApcsDisabled
968 * The KeAreAllApcsDisabled routine returns whether the calling thread is
969 * inside a guarded region or running at IRQL >= APC_LEVEL, which disables
974 * @return KeAreAllApcsDisabled returns TRUE if the thread is within a guarded
975 * guarded region or running at IRQL >= APC_LEVEL, and FALSE otherwise.
977 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use this routine to
978 * determine if all APC delivery is disabled.
980 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
985 KeAreAllApcsDisabled(VOID
)
987 /* Return the Special APC State */
988 return ((KeGetCurrentThread()->SpecialApcDisable
) ||
989 (KeGetCurrentIrql() >= APC_LEVEL
)) ? TRUE
: FALSE
;