ReactOS 0.4.15-dev-8102-g108db8f
apc.c
Go to the documentation of this file.
1/*
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)
7 */
8
9/* INCLUDES *****************************************************************/
10
11#include <ntoskrnl.h>
12#define NDEBUG
13#include <debug.h>
14
15/* PRIVATE FUNCTIONS *********************************************************/
16
17/*++
18 * @name KiCheckForKernelApcDelivery
19 * @implemented NT 5.2
20 *
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.
24 *
25 * @param None.
26 *
27 * @return None.
28 *
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.
32 *
33 *--*/
34VOID
37{
39
40 /* We should only deliver at passive */
42 {
43 /* Raise to APC and Deliver APCs, then lower back to Passive */
47 }
48 else
49 {
50 /*
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
55 * level again.
56 */
57 KeGetCurrentThread()->ApcState.KernelApcPending = TRUE;
59 }
60}
61
62/*++
63 * @name KiInsertQueueApc
64 *
65 * The KiInsertQueueApc routine queues a APC for execution when the right
66 * scheduler environment exists.
67 *
68 * @param Apc
69 * Pointer to an initialized control object of type APC for which the
70 * caller provides the storage.
71 *
72 * @param PriorityBoost
73 * Priority Boost to apply to the Thread.
74 *
75 * @return None
76 *
77 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
78 * and at PASSIVE_LEVEL for the NormalRoutine registered.
79 *
80 * Callers of this routine must have locked the dipatcher database.
81 *
82 *--*/
83VOID
87{
88 PKTHREAD Thread = Apc->Thread;
90 KPROCESSOR_MODE ApcMode;
91 PLIST_ENTRY ListHead, NextEntry;
92 PKAPC QueuedApc;
93 PKGATE Gate;
95 BOOLEAN RequestInterrupt = FALSE;
96
97 /*
98 * Check if the caller wanted this APC to use the thread's environment at
99 * insertion time.
100 */
101 if (Apc->ApcStateIndex == InsertApcEnvironment)
102 {
103 /* Copy it over */
104 Apc->ApcStateIndex = Thread->ApcStateIndex;
105 }
106
107 /* Get the APC State for this Index, and the mode too */
108 ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex];
109 ApcMode = Apc->ApcMode;
110
111 /* The APC must be "inserted" already */
112 ASSERT(Apc->Inserted == TRUE);
113
114 /* Three scenarios:
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
118 */
119 if (Apc->NormalRoutine)
120 {
121 /* Normal APC; is it the Thread Termination APC? */
122 if ((ApcMode != KernelMode) &&
123 (Apc->KernelRoutine == PsExitSpecialApc))
124 {
125 /* Set User APC pending to true */
126 Thread->ApcState.UserApcPending = TRUE;
127
128 /* Insert it at the top of the list */
129 InsertHeadList(&ApcState->ApcListHead[ApcMode],
130 &Apc->ApcListEntry);
131 }
132 else
133 {
134 /* Regular user or kernel Normal APC */
135 InsertTailList(&ApcState->ApcListHead[ApcMode],
136 &Apc->ApcListEntry);
137 }
138 }
139 else
140 {
141 /* Special APC, find the last one in the list */
142 ListHead = &ApcState->ApcListHead[ApcMode];
143 NextEntry = ListHead->Blink;
144 while (NextEntry != ListHead)
145 {
146 /* Get the APC */
147 QueuedApc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
148
149 /* Is this a No-Normal APC? If so, break */
150 if (!QueuedApc->NormalRoutine) break;
151
152 /* Move to the previous APC in the Queue */
153 NextEntry = NextEntry->Blink;
154 }
155
156 /* Insert us here */
157 InsertHeadList(NextEntry, &Apc->ApcListEntry);
158 }
159
160 /* Now check if the Apc State Indexes match */
161 if (Thread->ApcStateIndex == Apc->ApcStateIndex)
162 {
163 /* Check that the thread matches */
164 if (Thread == KeGetCurrentThread())
165 {
166 /* Sanity check */
167 ASSERT(Thread->State == Running);
168
169 /* Check if this is kernel mode */
170 if (ApcMode == KernelMode)
171 {
172 /* All valid, a Kernel APC is pending now */
173 Thread->ApcState.KernelApcPending = TRUE;
174
175 /* Check if Special APCs are disabled */
176 if (!Thread->SpecialApcDisable)
177 {
178 /* They're not, so request the interrupt */
180 }
181 }
182 }
183 else
184 {
185 /* Acquire the dispatcher lock */
187
188 /* Check if this is a kernel-mode APC */
189 if (ApcMode == KernelMode)
190 {
191 /* Kernel-mode APC, set us pending */
192 Thread->ApcState.KernelApcPending = TRUE;
193
194 /* Are we currently running? */
195 if (Thread->State == Running)
196 {
197 /* The thread is running, so remember to send a request */
198 RequestInterrupt = TRUE;
199 }
200 else if ((Thread->State == Waiting) &&
201 (Thread->WaitIrql == PASSIVE_LEVEL) &&
202 !(Thread->SpecialApcDisable) &&
203 (!(Apc->NormalRoutine) ||
204 (!(Thread->KernelApcDisable) &&
205 !(Thread->ApcState.KernelApcInProgress))))
206 {
207 /* We'll unwait with this status */
209
210 /* Wake up the thread */
212 }
213 else if (Thread->State == GateWait)
214 {
215 /* Lock the thread */
217
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))))
225 {
226 /* We were in a gate wait. Handle this. */
227 DPRINT1("A thread was in a gate wait\n");
228
229 /* Get the gate */
230 Gate = Thread->GateObject;
231
232 /* Lock the gate */
234
235 /* Remove it from the waiters list */
236 RemoveEntryList(&Thread->WaitBlock[0].WaitListEntry);
237
238 /* Unlock the gate */
240
241 /* Increase the queue counter if needed */
242 if (Thread->Queue) Thread->Queue->CurrentCount++;
243
244 /* Put into deferred ready list with this status */
245 Thread->WaitStatus = STATUS_KERNEL_APC;
247 }
248
249 /* Release the thread lock */
251 }
252 }
253 else if ((Thread->State == Waiting) &&
254 (Thread->WaitMode == UserMode) &&
255 ((Thread->Alertable) ||
256 (Thread->ApcState.UserApcPending)))
257 {
258 /* Set user-mode APC pending */
259 Thread->ApcState.UserApcPending = TRUE;
261
262 /* Wake up the thread */
264 }
265
266 /* Release dispatcher lock */
268
269 /* Check if an interrupt was requested */
270 KiRequestApcInterrupt(RequestInterrupt, Thread->NextProcessor);
271 }
272 }
273}
274
275/*++
276 * @name KiDeliverApc
277 * @implemented @NT4
278 *
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
281 * pending.
282 *
283 * @param DeliveryMode
284 * Specifies the current processor mode.
285 *
286 * @param ExceptionFrame
287 * Pointer to the Exception Frame on non-i386 builds.
288 *
289 * @param TrapFrame
290 * Pointer to the Trap Frame.
291 *
292 * @return None.
293 *
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.
298 *
299 *--*/
300VOID
301NTAPI
303 IN PKEXCEPTION_FRAME ExceptionFrame,
304 IN PKTRAP_FRAME TrapFrame)
305{
307 PKPROCESS Process = Thread->ApcState.Process;
308 PKTRAP_FRAME OldTrapFrame;
309 PLIST_ENTRY ApcListEntry;
310 PKAPC Apc;
311 KLOCK_QUEUE_HANDLE ApcLock;
312 PKKERNEL_ROUTINE KernelRoutine;
313 PVOID NormalContext;
314 PKNORMAL_ROUTINE NormalRoutine;
318
319 /* Save the old trap frame and set current one */
320 OldTrapFrame = Thread->TrapFrame;
321 Thread->TrapFrame = TrapFrame;
322
323 /* Clear Kernel APC Pending */
324 Thread->ApcState.KernelApcPending = FALSE;
325
326 /* Check if Special APCs are disabled */
327 if (Thread->SpecialApcDisable) goto Quickie;
328
329 /* Do the Kernel APCs first */
330 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
331 {
332 /* Lock the APC Queue */
334
335 /* Check if the list became empty now */
336 if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
337 {
338 /* It is, release the lock and break out */
339 KiReleaseApcLock(&ApcLock);
340 break;
341 }
342
343 /* Kernel APC is not pending anymore */
344 Thread->ApcState.KernelApcPending = FALSE;
345
346 /* Get the next Entry */
347 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
348 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
349
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;
356
357 /* Special APC */
358 if (!NormalRoutine)
359 {
360 /* Remove the APC from the list */
361 RemoveEntryList(ApcListEntry);
362 Apc->Inserted = FALSE;
363
364 /* Release the APC lock */
365 KiReleaseApcLock(&ApcLock);
366
367 /* Call the Special APC */
368 KernelRoutine(Apc,
369 &NormalRoutine,
370 &NormalContext,
373
374 /* Make sure it returned correctly */
375 if (KeGetCurrentIrql() != ApcLock.OldIrql)
376 {
377 KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
378 (KeGetCurrentIrql() << 16) |
379 (ApcLock.OldIrql << 8),
380 (ULONG_PTR)KernelRoutine,
381 (ULONG_PTR)Apc,
382 (ULONG_PTR)NormalRoutine);
383 }
384 }
385 else
386 {
387 /* Normal Kernel APC, make sure it's safe to deliver */
388 if ((Thread->ApcState.KernelApcInProgress) ||
389 (Thread->KernelApcDisable))
390 {
391 /* Release lock and return */
392 KiReleaseApcLock(&ApcLock);
393 goto Quickie;
394 }
395
396 /* Dequeue the APC */
397 RemoveEntryList(ApcListEntry);
398 Apc->Inserted = FALSE;
399
400 /* Go back to APC_LEVEL */
401 KiReleaseApcLock(&ApcLock);
402
403 /* Call the Kernel APC */
404 KernelRoutine(Apc,
405 &NormalRoutine,
406 &NormalContext,
409
410 /* Make sure it returned correctly */
411 if (KeGetCurrentIrql() != ApcLock.OldIrql)
412 {
413 KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
414 (KeGetCurrentIrql() << 16) |
415 (ApcLock.OldIrql << 8),
416 (ULONG_PTR)KernelRoutine,
417 (ULONG_PTR)Apc,
418 (ULONG_PTR)NormalRoutine);
419 }
420
421 /* Check if there still is a Normal Routine */
422 if (NormalRoutine)
423 {
424 /* At Passive Level, an APC can be prempted by a Special APC */
425 Thread->ApcState.KernelApcInProgress = TRUE;
427
428 /* Call and Raise IRQL back to APC_LEVEL */
429 NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
430 KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql);
431 }
432
433 /* Set Kernel APC in progress to false and loop again */
434 Thread->ApcState.KernelApcInProgress = FALSE;
435 }
436 }
437
438 /* Now we do the User APCs */
439 if ((DeliveryMode == UserMode) &&
440 !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
441 (Thread->ApcState.UserApcPending))
442 {
443 /* Lock the APC Queue */
445
446 /* It's not pending anymore */
447 Thread->ApcState.UserApcPending = FALSE;
448
449 /* Check if the list became empty now */
450 if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
451 {
452 /* It is, release the lock and break out */
453 KiReleaseApcLock(&ApcLock);
454 goto Quickie;
455 }
456
457 /* Get the actual APC object */
458 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
459 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
460
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;
467
468 /* Remove the APC from Queue, and release the lock */
469 RemoveEntryList(ApcListEntry);
470 Apc->Inserted = FALSE;
471 KiReleaseApcLock(&ApcLock);
472
473 /* Call the kernel routine */
474 KernelRoutine(Apc,
475 &NormalRoutine,
476 &NormalContext,
479
480 /* Check if there's no normal routine */
481 if (!NormalRoutine)
482 {
483 /* Check if more User APCs are Pending */
485 }
486 else
487 {
488 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
489 KiInitializeUserApc(ExceptionFrame,
490 TrapFrame,
491 NormalRoutine,
492 NormalContext,
495 }
496 }
497
498Quickie:
499 /* Make sure we're still in the same process */
500 if (Process != Thread->ApcState.Process)
501 {
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);
508 }
509
510 /* Restore the trap frame */
511 Thread->TrapFrame = OldTrapFrame;
512}
513
515VOID
519{
520 /* Check if the list for this mode is empty */
521 if (IsListEmpty(&Original[Mode]))
522 {
523 /* It is, all we need to do is initialize it */
525 }
526 else
527 {
528 /* Copy the lists */
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];
533 }
534}
535
536VOID
537NTAPI
539 PKAPC_STATE NewState)
540{
541 /* Restore backup of Original Environment */
542 RtlCopyMemory(NewState, OldState, KAPC_STATE_ACTUAL_LENGTH);
543
544 /* Repair Lists */
545 RepairList(OldState->ApcListHead, NewState->ApcListHead, KernelMode);
546 RepairList(OldState->ApcListHead, NewState->ApcListHead, UserMode);
547}
548
549/* PUBLIC FUNCTIONS **********************************************************/
550
551/*++
552 * @name KeEnterCriticalRegion
553 * @implemented NT4
554 *
555 * The KeEnterCriticalRegion routine temporarily disables the delivery of
556 * normal kernel APCs; special kernel-mode APCs are still delivered.
557 *
558 * @param None.
559 *
560 * @return None.
561 *
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.
566 *
567 * Callers of KeEnterCriticalRegion must be running at IRQL <=
568 * APC_LEVEL.
569 *
570 *--*/
571VOID
572NTAPI
574{
575 /* Use inlined function */
577}
578
579/*++
580 * KeLeaveCriticalRegion
581 * @implemented NT4
582 *
583 * The KeLeaveCriticalRegion routine reenables the delivery of normal
584 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
585 *
586 * @param None.
587 *
588 * @return None.
589 *
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.
592 *
593 * Callers of KeLeaveCriticalRegion must be running at IRQL <=
594 * DISPATCH_LEVEL.
595 *
596 *--*/
597VOID
598NTAPI
600{
601 /* Use inlined version */
603}
604
605/*++
606 * KeInitializeApc
607 * @implemented NT4
608 *
609 * The KeInitializeApc routine initializes an APC object, and registers
610 * the Kernel, Rundown and Normal routines for that object.
611 *
612 * @param Apc
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.
616 *
617 * @param Thread
618 * Thread to which to deliver the APC.
619 *
620 * @param TargetEnvironment
621 * APC Environment to be used.
622 *
623 * @param KernelRoutine
624 * Points to the KernelRoutine to associate with the APC.
625 * This routine is executed for all APCs.
626 *
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.
630 *
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
635 * ignored.
636 *
637 * @param Mode
638 * Specifies the processor mode at which to run the Normal Routine.
639 *
640 * @param Context
641 * Specifices the value to pass as Context parameter to the registered
642 * routines.
643 *
644 * @return None.
645 *
646 * @remarks The caller can queue an initialized APC with KeInsertQueueApc.
647 *
648 *--*/
649VOID
650NTAPI
653 IN KAPC_ENVIRONMENT TargetEnvironment,
654 IN PKKERNEL_ROUTINE KernelRoutine,
655 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
656 IN PKNORMAL_ROUTINE NormalRoutine,
659{
660 /* Sanity check */
661 ASSERT(TargetEnvironment <= InsertApcEnvironment);
662
663 /* Set up the basic APC Structure Data */
664 Apc->Type = ApcObject;
665 Apc->Size = sizeof(KAPC);
666
667 /* Set the Environment */
668 if (TargetEnvironment == CurrentApcEnvironment)
669 {
670 /* Use the current one for the thread */
671 Apc->ApcStateIndex = Thread->ApcStateIndex;
672 }
673 else
674 {
675 /* Sanity check */
676 ASSERT((TargetEnvironment <= Thread->ApcStateIndex) ||
677 (TargetEnvironment == InsertApcEnvironment));
678
679 /* Use the one that was given */
680 Apc->ApcStateIndex = TargetEnvironment;
681 }
682
683 /* Set the Thread and Routines */
684 Apc->Thread = Thread;
685 Apc->KernelRoutine = KernelRoutine;
686 Apc->RundownRoutine = RundownRoutine;
687 Apc->NormalRoutine = NormalRoutine;
688
689 /* Check if this is a special APC */
690 if (NormalRoutine)
691 {
692 /* It's a normal one. Set the context and mode */
693 Apc->ApcMode = Mode;
694 Apc->NormalContext = Context;
695 }
696 else
697 {
698 /* It's a special APC, which can only be kernel mode */
699 Apc->ApcMode = KernelMode;
700 Apc->NormalContext = NULL;
701 }
702
703 /* The APC is not inserted */
704 Apc->Inserted = FALSE;
705}
706
707/*++
708 * @name KeInsertQueueApc
709 * @implemented NT4
710 *
711 * The KeInsertQueueApc routine queues a APC for execution when the right
712 * scheduler environment exists.
713 *
714 * @param Apc
715 * Pointer to an initialized control object of type APC for which the
716 * caller provides the storage.
717 *
718 * @param SystemArgument[1,2]
719 * Pointer to a set of two parameters that contain untyped data.
720 *
721 * @param PriorityBoost
722 * Priority Boost to apply to the Thread.
723 *
724 * @return If the APC is already inserted or APC queueing is disabled, FALSE.
725 * Otherwise, TRUE.
726 *
727 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
728 * and at PASSIVE_LEVEL for the NormalRoutine registered.
729 *
730 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
731 *
732 *--*/
734NTAPI
739{
740 PKTHREAD Thread = Apc->Thread;
741 KLOCK_QUEUE_HANDLE ApcLock;
743 ASSERT_APC(Apc);
745
746 /* Get the APC lock */
748
749 /* Make sure we can Queue APCs and that this one isn't already inserted */
750 if (!(Thread->ApcQueueable) || (Apc->Inserted))
751 {
752 /* Fail */
753 State = FALSE;
754 }
755 else
756 {
757 /* Set the System Arguments and set it as inserted */
758 Apc->SystemArgument1 = SystemArgument1;
759 Apc->SystemArgument2 = SystemArgument2;
760 Apc->Inserted = TRUE;
761
762 /* Call the Internal Function */
764 }
765
766 /* Release the APC lock and return success */
768 KiExitDispatcher(ApcLock.OldIrql);
769 return State;
770}
771
772/*++
773 * @name KeFlushQueueApc
774 * @implemented NT4
775 *
776 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
777 * from the specified Thread's APC queue.
778 *
779 * @param Thread
780 * Pointer to the thread whose APC queue will be flushed.
781 *
782 * @param PreviousMode
783 * Specifies which APC Queue to flush.
784 *
785 * @return A pointer to the first entry in the flushed APC queue.
786 *
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.
789 *
790 *--*/
792NTAPI
795{
796 PKAPC Apc;
797 PLIST_ENTRY FirstEntry, CurrentEntry;
798 KLOCK_QUEUE_HANDLE ApcLock;
800
801 /* Check if this was user mode */
802 if (PreviousMode == UserMode)
803 {
804 /* Get the APC lock */
806
807 /* Select user list and check if it's empty */
808 if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
809 {
810 /* Don't return anything */
811 FirstEntry = NULL;
812 goto FlushDone;
813 }
814 }
815 else
816 {
817 /* Select kernel list and check if it's empty */
818 if (IsListEmpty( &Thread->ApcState.ApcListHead[KernelMode]))
819 {
820 /* Don't return anything */
821 return NULL;
822 }
823
824 /* Otherwise, acquire the APC lock */
826 }
827
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])
831 {
832 /* It is, clear the returned entry */
833 FirstEntry = NULL;
834 }
835 else
836 {
837 /* It's not, remove the first entry */
838 RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]);
839
840 /* Loop all the entries */
841 CurrentEntry = FirstEntry;
842 do
843 {
844 /* Get the APC and make it un-inserted */
845 Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
846 Apc->Inserted = FALSE;
847
848 /* Get the next entry */
849 CurrentEntry = CurrentEntry->Flink;
850 } while (CurrentEntry != FirstEntry);
851
852 /* Re-initialize the list */
853 InitializeListHead(&Thread->ApcState.ApcListHead[PreviousMode]);
854 }
855
856 /* Release the lock */
857FlushDone:
858 KiReleaseApcLock(&ApcLock);
859
860 /* Return the first entry */
861 return FirstEntry;
862}
863
864/*++
865 * @name KeRemoveQueueApc
866 * @implemented NT4
867 *
868 * The KeRemoveQueueApc routine removes a given APC object from the system
869 * APC queue.
870 *
871 * @param Apc
872 * Pointer to an initialized APC object that was queued by calling
873 * KeInsertQueueApc.
874 *
875 * @return TRUE if the APC Object is in the APC Queue. Otherwise, no operation
876 * is performed and FALSE is returned.
877 *
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.
880 *
881 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
882 *
883 *--*/
885NTAPI
887{
888 PKTHREAD Thread = Apc->Thread;
890 BOOLEAN Inserted;
891 KLOCK_QUEUE_HANDLE ApcLock;
892 ASSERT_APC(Apc);
894
895 /* Get the APC lock (this raises IRQL to SYNCH_LEVEL) */
897
898 /* Check if it's inserted */
899 Inserted = Apc->Inserted;
900 if (Inserted)
901 {
902 /* Set it as non-inserted and get the APC state */
903 Apc->Inserted = FALSE;
904 ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex];
905
906 /* Acquire the dispatcher lock and remove it from the list */
908 if (RemoveEntryList(&Apc->ApcListEntry))
909 {
910 /* Set the correct state based on the APC Mode */
911 if (Apc->ApcMode == KernelMode)
912 {
913 /* No more pending kernel APCs */
914 ApcState->KernelApcPending = FALSE;
915 }
916 else
917 {
918 /* No more pending user APCs */
919 ApcState->UserApcPending = FALSE;
920 }
921 }
922
923 /* Release dispatcher lock */
925 }
926
927 /* Release the lock and return */
928 KiReleaseApcLock(&ApcLock);
929 return Inserted;
930}
931
932/*++
933 * @name KeAreApcsDisabled
934 * @implemented NT4
935 *
936 * The KeAreApcsDisabled routine returns whether kernel APC delivery is
937 * disabled for the current thread.
938 *
939 * @param None.
940 *
941 * @return KeAreApcsDisabled returns TRUE if the thread is within a critical
942 * region or a guarded region, and FALSE otherwise.
943 *
944 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled
945 * to determine if normal kernel APCs are disabled.
946 *
947 * A thread that is inside critical region has both user APCs and
948 * normal kernel APCs disabled, but not special kernel APCs.
949 *
950 * A thread that is inside a guarded region has all APCs disabled,
951 * including special kernel APCs.
952 *
953 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
954 *
955 *--*/
957NTAPI
959{
960 /* Return the Kernel APC State */
961 return KeGetCurrentThread()->CombinedApcDisable ? TRUE : FALSE;
962}
963
964/*++
965 * @name KeAreAllApcsDisabled
966 * @implemented NT5.1
967 *
968 * The KeAreAllApcsDisabled routine returns whether the calling thread is
969 * inside a guarded region or running at IRQL >= APC_LEVEL, which disables
970 * all APC delivery.
971 *
972 * @param None.
973 *
974 * @return KeAreAllApcsDisabled returns TRUE if the thread is within a guarded
975 * guarded region or running at IRQL >= APC_LEVEL, and FALSE otherwise.
976 *
977 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use this routine to
978 * determine if all APC delivery is disabled.
979 *
980 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
981 *
982 *--*/
984NTAPI
986{
987 /* Return the Special APC State */
988 return ((KeGetCurrentThread()->SpecialApcDisable) ||
990}
unsigned char BOOLEAN
LONG NTSTATUS
Definition: precomp.h:26
#define DPRINT1
Definition: precomp.h:8
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
LONG KPRIORITY
Definition: compat.h:803
VOID Copy(PVOID Src, PVOID Dst, ULONG NumBytes)
Definition: mmixer.c:126
#define ASSERT_IRQL_EQUAL(x)
Definition: debug.h:43
#define RemoveEntryList(Entry)
Definition: env_spec_w32.h:986
#define InsertTailList(ListHead, Entry)
#define InsertHeadList(ListHead, Entry)
#define IsListEmpty(ListHead)
Definition: env_spec_w32.h:954
#define PASSIVE_LEVEL
Definition: env_spec_w32.h:693
UCHAR KIRQL
Definition: env_spec_w32.h:591
#define KeRaiseIrql(irql, oldIrql)
Definition: env_spec_w32.h:597
#define APC_LEVEL
Definition: env_spec_w32.h:695
#define KeLowerIrql(oldIrql)
Definition: env_spec_w32.h:602
#define KeGetCurrentIrql()
Definition: env_spec_w32.h:706
#define InitializeListHead(ListHead)
Definition: env_spec_w32.h:944
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
_In_opt_ PFILE_OBJECT _In_opt_ PETHREAD Thread
Definition: fltkernel.h:2653
_Must_inspect_result_ _In_ PLARGE_INTEGER _In_ PLARGE_INTEGER _In_ ULONG _In_ PFILE_OBJECT _In_ PVOID Process
Definition: fsrtlfuncs.h:223
Status
Definition: gdiplustypes.h:25
VOID FASTCALL HalRequestSoftwareInterrupt(IN KIRQL Irql)
Definition: pic.c:271
#define KeGetCurrentThread
Definition: hal.h:55
_In_ ULONG Mode
Definition: hubbusif.h:303
FORCEINLINE VOID KiAcquireApcLockRaiseToSynch(IN PKTHREAD Thread, IN PKLOCK_QUEUE_HANDLE Handle)
Definition: ke_x.h:607
FORCEINLINE VOID KiAcquireApcLockRaiseToDpc(IN PKTHREAD Thread, IN PKLOCK_QUEUE_HANDLE Handle)
Definition: ke_x.h:626
FORCEINLINE VOID KiRequestApcInterrupt(IN BOOLEAN NeedApc, IN UCHAR Processor)
Definition: ke_x.h:276
FORCEINLINE VOID KiAcquireThreadLock(IN PKTHREAD Thread)
Definition: ke_x.h:240
#define KeLeaveCriticalRegion()
Definition: ke_x.h:119
FORCEINLINE VOID KiReleaseDispatcherObject(IN DISPATCHER_HEADER *Object)
Definition: ke_x.h:142
FORCEINLINE VOID KiInsertDeferredReadyList(IN PKTHREAD Thread)
Definition: ke_x.h:185
FORCEINLINE VOID KiAcquireDispatcherObject(IN DISPATCHER_HEADER *Object)
Definition: ke_x.h:132
FORCEINLINE VOID KiReleaseDispatcherLockFromSynchLevel(VOID)
Definition: ke_x.h:174
#define KeEnterCriticalRegion()
Definition: ke_x.h:88
FORCEINLINE VOID KiReleaseThreadLock(IN PKTHREAD Thread)
Definition: ke_x.h:250
FORCEINLINE VOID KiAcquireDispatcherLockAtSynchLevel(VOID)
Definition: ke_x.h:165
FORCEINLINE VOID KiReleaseApcLockFromSynchLevel(IN PKLOCK_QUEUE_HANDLE Handle)
Definition: ke_x.h:643
FORCEINLINE KIRQL KiAcquireDispatcherLock(VOID)
Definition: ke_x.h:149
FORCEINLINE VOID KiReleaseApcLock(IN PKLOCK_QUEUE_HANDLE Handle)
Definition: ke_x.h:635
#define ASSERT(a)
Definition: mode.c:44
#define KernelMode
Definition: asm.h:34
#define UserMode
Definition: asm.h:35
FORCEINLINE struct _KPRCB * KeGetCurrentPrcb(VOID)
Definition: ketypes.h:1161
@ ApcObject
Definition: ketypes.h:424
VOID(NTAPI * PKRUNDOWN_ROUTINE)(IN struct _KAPC *Apc)
Definition: ketypes.h:750
@ Running
Definition: ketypes.h:390
@ GateWait
Definition: ketypes.h:397
@ Waiting
Definition: ketypes.h:393
@ CurrentApcEnvironment
Definition: ketypes.h:769
@ InsertApcEnvironment
Definition: ketypes.h:770
VOID(NTAPI * PKKERNEL_ROUTINE)(IN struct _KAPC *Apc, IN OUT PKNORMAL_ROUTINE *NormalRoutine OPTIONAL, IN OUT PVOID *NormalContext OPTIONAL, IN OUT PVOID *SystemArgument1 OPTIONAL, IN OUT PVOID *SystemArgument2 OPTIONAL)
Definition: ketypes.h:754
VOID(NTAPI * PKNORMAL_ROUTINE)(IN PVOID NormalContext OPTIONAL, IN PVOID SystemArgument1 OPTIONAL, IN PVOID SystemArgument2 OPTIONAL)
Definition: ketypes.h:744
enum _KAPC_ENVIRONMENT KAPC_ENVIRONMENT
#define FASTCALL
Definition: nt_native.h:50
VOID FASTCALL KiUnwaitThread(IN PKTHREAD Thread, IN LONG_PTR WaitStatus, IN KPRIORITY Increment)
Definition: wait.c:89
BOOLEAN NTAPI KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
Definition: thrdobj.c:722
VOID NTAPI KiInitializeUserApc(IN PKEXCEPTION_FRAME Reserved, IN PKTRAP_FRAME TrapFrame, IN PKNORMAL_ROUTINE NormalRoutine, IN PVOID NormalContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
Definition: usercall.c:266
VOID FASTCALL KiExitDispatcher(KIRQL OldIrql)
_Out_ PKAPC_STATE ApcState
Definition: mm.h:1765
VOID NTAPI KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame)
Definition: apc.c:302
VOID NTAPI _KeEnterCriticalRegion(VOID)
Definition: apc.c:573
BOOLEAN NTAPI KeInsertQueueApc(IN PKAPC Apc, IN PVOID SystemArgument1, IN PVOID SystemArgument2, IN KPRIORITY PriorityBoost)
Definition: apc.c:735
VOID NTAPI KiCheckForKernelApcDelivery(VOID)
Definition: apc.c:36
VOID NTAPI _KeLeaveCriticalRegion(VOID)
Definition: apc.c:599
BOOLEAN NTAPI KeAreAllApcsDisabled(VOID)
Definition: apc.c:985
PLIST_ENTRY NTAPI KeFlushQueueApc(IN PKTHREAD Thread, IN KPROCESSOR_MODE PreviousMode)
Definition: apc.c:793
BOOLEAN NTAPI KeRemoveQueueApc(IN PKAPC Apc)
Definition: apc.c:886
VOID FASTCALL KiInsertQueueApc(IN PKAPC Apc, IN KPRIORITY PriorityBoost)
Definition: apc.c:85
FORCEINLINE VOID RepairList(IN PLIST_ENTRY Original, IN PLIST_ENTRY Copy, IN KPROCESSOR_MODE Mode)
Definition: apc.c:516
VOID NTAPI KiMoveApcState(PKAPC_STATE OldState, PKAPC_STATE NewState)
Definition: apc.c:538
BOOLEAN NTAPI KeAreApcsDisabled(VOID)
Definition: apc.c:958
VOID NTAPI KeInitializeApc(IN PKAPC Apc, IN PKTHREAD Thread, IN KAPC_ENVIRONMENT TargetEnvironment, IN PKKERNEL_ROUTINE KernelRoutine, IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL, IN PKNORMAL_ROUTINE NormalRoutine, IN KPROCESSOR_MODE Mode, IN PVOID Context)
Definition: apc.c:651
#define STATUS_USER_APC
Definition: ntstatus.h:78
#define STATUS_KERNEL_APC
Definition: ntstatus.h:79
VOID NTAPI PsExitSpecialApc(PKAPC Apc, PKNORMAL_ROUTINE *NormalRoutine, PVOID *NormalContext, PVOID *SystemArgument1, PVOID *SystemArgument2)
VOID NTAPI KeBugCheckEx(_In_ ULONG BugCheckCode, _In_ ULONG_PTR BugCheckParameter1, _In_ ULONG_PTR BugCheckParameter2, _In_ ULONG_PTR BugCheckParameter3, _In_ ULONG_PTR BugCheckParameter4)
Definition: rtlcompat.c:108
#define ASSERT_IRQL_LESS_OR_EQUAL(x)
Definition: debug.h:251
PULONG MinorVersion OPTIONAL
Definition: CrossNt.h:68
Definition: ketypes.h:547
PVOID NormalContext
Definition: ketypes.h:562
BOOLEAN Inserted
Definition: ketypes.h:567
PVOID SystemArgument1
Definition: ketypes.h:563
PVOID SystemArgument2
Definition: ketypes.h:564
DISPATCHER_HEADER Header
Definition: ketypes.h:823
Definition: typedefs.h:120
struct _LIST_ENTRY * Blink
Definition: typedefs.h:122
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
#define NTAPI
Definition: typedefs.h:36
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263
uint32_t ULONG_PTR
Definition: typedefs.h:65
#define IN
Definition: typedefs.h:39
#define CONTAINING_RECORD(address, type, field)
Definition: typedefs.h:260
_In_ WDFREQUEST _In_ NTSTATUS _In_ CCHAR PriorityBoost
Definition: wdfrequest.h:1016
#define FORCEINLINE
Definition: wdftypes.h:67
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:778
CCHAR KPROCESSOR_MODE
Definition: ketypes.h:7
_In_opt_ PVOID _In_opt_ PVOID SystemArgument1
Definition: ketypes.h:688
#define ASSERT_APC(Object)
* PKAPC_STATE
Definition: ketypes.h:1409
struct _KAPC KAPC
#define KAPC_STATE_ACTUAL_LENGTH
Definition: ketypes.h:1411
_In_opt_ PVOID _In_opt_ PVOID _In_opt_ PVOID SystemArgument2
Definition: ketypes.h:689
_In_ KPROCESSOR_MODE PreviousMode
Definition: sefuncs.h:103
unsigned char UCHAR
Definition: xmlstorage.h:181