Partially fixed up tree after merge from HEAD. More to do.
[reactos.git] / reactos / ntoskrnl / ke / apc.c
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 Asyncronous 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 <internal/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 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.
32 *
33 *--*/
34 VOID
35 NTAPI
36 KiCheckForKernelApcDelivery(VOID)
37 {
38 /* We should only deliver at passive */
39 if (KeGetCurrentIrql() == PASSIVE_LEVEL)
40 {
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);
45 }
46 else
47 {
48 /*
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
53 * level again.
54 */
55 KeGetCurrentThread()->ApcState.KernelApcPending = TRUE;
56 HalRequestSoftwareInterrupt(APC_LEVEL);
57 }
58 }
59
60 /*++
61 * @name KiInsertQueueApc
62 *
63 * The KiInsertQueueApc routine queues a APC for execution when the right
64 * scheduler environment exists.
65 *
66 * @param Apc
67 * Pointer to an initialized control object of type DPC for which the
68 * caller provides the storage.
69 *
70 * @param PriorityBoost
71 * Priority Boost to apply to the Thread.
72 *
73 * @return None
74 *
75 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
76 * and at PASSIVE_LEVEL for the NormalRoutine registered.
77 *
78 * Callers of this routine must have locked the dipatcher database.
79 *
80 *--*/
81 VOID
82 FASTCALL
83 KiInsertQueueApc(IN PKAPC Apc,
84 IN KPRIORITY PriorityBoost)
85 {
86 PKTHREAD Thread = Apc->Thread;
87 PKAPC_STATE ApcState;
88 KPROCESSOR_MODE ApcMode;
89 PLIST_ENTRY ListHead, NextEntry;
90 PKAPC QueuedApc;
91 NTSTATUS Status;
92 BOOLEAN RequestInterrupt;
93
94 /*
95 * Check if the caller wanted this APC to use the thread's environment at
96 * insertion time.
97 */
98 if (Apc->ApcStateIndex == InsertApcEnvironment)
99 {
100 /* Copy it over */
101 Apc->ApcStateIndex = Thread->ApcStateIndex;
102 }
103
104 /* Get the APC State for this Index, and the mode too */
105 ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex];
106 ApcMode = Apc->ApcMode;
107
108 /* The APC must be "inserted" already */
109 ASSERT(Apc->Inserted == TRUE);
110
111 /* Three scenarios:
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
115 */
116 if (Apc->NormalRoutine)
117 {
118 /* Normal APC; is it the Thread Termination APC? */
119 if ((ApcMode != KernelMode) &&
120 (Apc->KernelRoutine == PsExitSpecialApc))
121 {
122 /* Set User APC pending to true */
123 Thread->ApcState.UserApcPending = TRUE;
124
125 /* Insert it at the top of the list */
126 InsertHeadList(&ApcState->ApcListHead[ApcMode],
127 &Apc->ApcListEntry);
128 }
129 else
130 {
131 /* Regular user or kernel Normal APC */
132 InsertTailList(&ApcState->ApcListHead[ApcMode],
133 &Apc->ApcListEntry);
134 }
135 }
136 else
137 {
138 /* Special APC, find the first Normal APC in the list */
139 ListHead = &ApcState->ApcListHead[ApcMode];
140 NextEntry = ListHead->Flink;
141 while(NextEntry != ListHead)
142 {
143 /* Get the APC */
144 QueuedApc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
145
146 /* Is this a Normal APC? If so, break */
147 if (QueuedApc->NormalRoutine) break;
148
149 /* Move to the next APC in the Queue */
150 NextEntry = NextEntry->Flink;
151 }
152
153 /* Move to the APC before this one (ie: the last Special APC) */
154 NextEntry = NextEntry->Blink;
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 if 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 */
179 HalRequestSoftwareInterrupt(APC_LEVEL);
180 }
181 }
182 }
183 else
184 {
185 /* Acquire the dispatcher lock */
186 KiAcquireDispatcherLock();
187
188 /* Check if this is a non-kernel mode APC */
189 if (ApcMode != KernelMode)
190 {
191 /*
192 * Not a Kernel-Mode APC. Are we waiting in user-mode?
193 * If so, then are we alertable or already have an APC pending?
194 */
195 if (((Thread->State == Waiting) && (Thread->WaitMode == UserMode)) &&
196 ((Thread->Alertable) || (Thread->ApcState.UserApcPending)))
197 {
198 /* Set user-mode APC pending */
199 Thread->ApcState.UserApcPending = TRUE;
200 Status = STATUS_USER_APC;
201 goto Unwait;
202 }
203 }
204 else
205 {
206 /* Kernel-mode APC, set us pending */
207 Thread->ApcState.KernelApcPending = TRUE;
208
209 /* Are we currently running? */
210 if (Thread->State == Running)
211 {
212 /* The thread is running, so remember to send a request */
213 RequestInterrupt = TRUE;
214 #ifndef CONFIG_SMP
215 /* On UP systems, request it immediately */
216 HalRequestSoftwareInterrupt(APC_LEVEL);
217 #endif
218 }
219 else
220 {
221 /*
222 * If the thread is Waiting at PASSIVE_LEVEL AND
223 * Special APCs are not disabled AND
224 * He is a Normal APC AND
225 * Kernel APCs are not disabled AND
226 * Kernel APC is not pending OR
227 * He is a Special APC THEN
228 * Unwait thread with STATUS_KERNEL_APC
229 */
230 if ((Thread->State == Waiting) &&
231 (Thread->WaitIrql == PASSIVE_LEVEL) &&
232 !(Thread->SpecialApcDisable) &&
233 (!(Apc->NormalRoutine) ||
234 (!(Thread->KernelApcDisable) &&
235 !(Thread->ApcState.KernelApcInProgress))))
236 {
237 /* We'll unwait with this status */
238 Status = STATUS_KERNEL_APC;
239
240 /* Wake up the thread */
241 Unwait:
242 KiUnwaitThread(Thread, Status, PriorityBoost);
243 }
244 else
245 {
246 /* Check if the thread is in a deferred ready state */
247 if (Thread->State == DeferredReady)
248 {
249 /* FIXME: TODO in new scheduler */
250 }
251 }
252 }
253 }
254
255 /* Release dispatcher lock */
256 KiReleaseDispatcherLockFromDpcLevel();
257
258 /* Check if an interrupt was requested */
259 KiRequestApcInterrupt(RequestInterrupt, Thread->NextProcessor);
260 }
261 }
262 }
263
264 /*++
265 * @name KiDeliverApc
266 * @implemented @NT4
267 *
268 * The KiDeliverApc routine is called from IRQL switching code if the
269 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
270 * pending.
271 *
272 * @param DeliveryMode
273 * Specifies the current processor mode.
274 *
275 * @param ExceptionFrame
276 * Pointer to the Exception Frame on non-i386 builds.
277 *
278 * @param TrapFrame
279 * Pointer to the Trap Frame.
280 *
281 * @return None.
282 *
283 * @remarks First, Special APCs are delivered, followed by Kernel-Mode APCs and
284 * User-Mode APCs. Note that the TrapFrame is only valid if the
285 * delivery mode is User-Mode.
286 * Upon entry, this routine executes at APC_LEVEL.
287 *
288 *--*/
289 VOID
290 NTAPI
291 KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode,
292 IN PKEXCEPTION_FRAME ExceptionFrame,
293 IN PKTRAP_FRAME TrapFrame)
294 {
295 PKTHREAD Thread = KeGetCurrentThread();
296 PKPROCESS Process = Thread->ApcState.Process;
297 PKTRAP_FRAME OldTrapFrame;
298 PLIST_ENTRY ApcListEntry;
299 PKAPC Apc;
300 KLOCK_QUEUE_HANDLE ApcLock;
301 PKKERNEL_ROUTINE KernelRoutine;
302 PVOID NormalContext;
303 PKNORMAL_ROUTINE NormalRoutine;
304 PVOID SystemArgument1;
305 PVOID SystemArgument2;
306 ASSERT_IRQL_EQUAL(APC_LEVEL);
307
308 /* Save the old trap frame and set current one */
309 OldTrapFrame = Thread->TrapFrame;
310 Thread->TrapFrame = TrapFrame;
311
312 /* Clear Kernel APC Pending */
313 Thread->ApcState.KernelApcPending = FALSE;
314
315 /* Check if Special APCs are disabled */
316 if (Thread->SpecialApcDisable) goto Quickie;
317
318 /* Do the Kernel APCs first */
319 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
320 {
321 /* Lock the APC Queue */
322 KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
323
324 /* Check if the list became empty now */
325 if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
326 {
327 /* It is, release the lock and break out */
328 KiReleaseApcLock(&ApcLock);
329 break;
330 }
331
332 /* Get the next Entry */
333 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
334 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
335
336 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
337 NormalRoutine = Apc->NormalRoutine;
338 KernelRoutine = Apc->KernelRoutine;
339 NormalContext = Apc->NormalContext;
340 SystemArgument1 = Apc->SystemArgument1;
341 SystemArgument2 = Apc->SystemArgument2;
342
343 /* Special APC */
344 if (!NormalRoutine)
345 {
346 /* Remove the APC from the list */
347 RemoveEntryList(ApcListEntry);
348 Apc->Inserted = FALSE;
349
350 /* Rrelease the APC lock */
351 KiReleaseApcLock(&ApcLock);
352
353 /* Call the Special APC */
354 KernelRoutine(Apc,
355 &NormalRoutine,
356 &NormalContext,
357 &SystemArgument1,
358 &SystemArgument2);
359
360 /* Make sure it returned correctly */
361 if (KeGetCurrentIrql() != ApcLock.OldIrql)
362 {
363 KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
364 (KeGetCurrentIrql() << 16) |
365 (ApcLock.OldIrql << 8),
366 (ULONG_PTR)KernelRoutine,
367 (ULONG_PTR)Apc,
368 (ULONG_PTR)NormalRoutine);
369 }
370 }
371 else
372 {
373 /* Normal Kernel APC, make sure it's safe to deliver */
374 if ((Thread->ApcState.KernelApcInProgress) ||
375 (Thread->KernelApcDisable))
376 {
377 /* Release lock and return */
378 KiReleaseApcLock(&ApcLock);
379 goto Quickie;
380 }
381
382 /* Dequeue the APC */
383 RemoveEntryList(ApcListEntry);
384 Apc->Inserted = FALSE;
385
386 /* Go back to APC_LEVEL */
387 KiReleaseApcLock(&ApcLock);
388
389 /* Call the Kernel APC */
390 KernelRoutine(Apc,
391 &NormalRoutine,
392 &NormalContext,
393 &SystemArgument1,
394 &SystemArgument2);
395
396 /* Make sure it returned correctly */
397 if (KeGetCurrentIrql() != ApcLock.OldIrql)
398 {
399 KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
400 (KeGetCurrentIrql() << 16) |
401 (ApcLock.OldIrql << 8),
402 (ULONG_PTR)KernelRoutine,
403 (ULONG_PTR)Apc,
404 (ULONG_PTR)NormalRoutine);
405 }
406
407 /* Check if There still is a Normal Routine */
408 if (NormalRoutine)
409 {
410 /* At Passive Level, an APC can be prempted by a Special APC */
411 Thread->ApcState.KernelApcInProgress = TRUE;
412 KeLowerIrql(PASSIVE_LEVEL);
413
414 /* Call and Raise IRQ back to APC_LEVEL */
415 NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
416 KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql);
417 }
418
419 /* Set Kernel APC in progress to false and loop again */
420 Thread->ApcState.KernelApcInProgress = FALSE;
421 }
422 }
423
424 /* Now we do the User APCs */
425 if ((DeliveryMode == UserMode) &&
426 !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
427 (Thread->ApcState.UserApcPending))
428 {
429 /* Lock the APC Queue */
430 KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
431
432 /* It's not pending anymore */
433 Thread->ApcState.UserApcPending = FALSE;
434
435 /* Check if the list became empty now */
436 if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
437 {
438 /* It is, release the lock and break out */
439 KiReleaseApcLock(&ApcLock);
440 goto Quickie;
441 }
442
443 /* Get the actual APC object */
444 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
445 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
446
447 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
448 NormalRoutine = Apc->NormalRoutine;
449 KernelRoutine = Apc->KernelRoutine;
450 NormalContext = Apc->NormalContext;
451 SystemArgument1 = Apc->SystemArgument1;
452 SystemArgument2 = Apc->SystemArgument2;
453
454 /* Remove the APC from Queue, and release the lock */
455 RemoveEntryList(ApcListEntry);
456 Apc->Inserted = FALSE;
457 KiReleaseApcLock(&ApcLock);
458
459 /* Call the kernel routine */
460 KernelRoutine(Apc,
461 &NormalRoutine,
462 &NormalContext,
463 &SystemArgument1,
464 &SystemArgument2);
465
466 /* Check if there's no normal routine */
467 if (!NormalRoutine)
468 {
469 /* Check if more User APCs are Pending */
470 KeTestAlertThread(UserMode);
471 }
472 else
473 {
474 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
475 KiInitializeUserApc(ExceptionFrame,
476 TrapFrame,
477 NormalRoutine,
478 NormalContext,
479 SystemArgument1,
480 SystemArgument2);
481 }
482 }
483
484 Quickie:
485 /* Make sure we're still in the same process */
486 if (Process != Thread->ApcState.Process)
487 {
488 /* Erm, we got attached or something! BAD! */
489 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT,
490 (ULONG_PTR)Process,
491 (ULONG_PTR)Thread->ApcState.Process,
492 Thread->ApcStateIndex,
493 KeGetCurrentPrcb()->DpcRoutineActive);
494 }
495
496 /* Restore the trap frame */
497 Thread->TrapFrame = OldTrapFrame;
498 }
499
500 FORCEINLINE
501 VOID
502 RepairList(IN PLIST_ENTRY Original,
503 IN PLIST_ENTRY Copy,
504 IN KPROCESSOR_MODE Mode)
505 {
506 /* Check if the list for this mode is empty */
507 if (IsListEmpty(&Original[Mode]))
508 {
509 /* It is, all we need to do is initialize it */
510 InitializeListHead(&Copy[Mode]);
511 }
512 else
513 {
514 /* Copy the lists */
515 Copy[Mode].Flink = Original[Mode].Flink;
516 Copy[Mode].Blink = Original[Mode].Blink;
517 Original[Mode].Flink->Blink = &Copy[Mode];
518 Original[Mode].Blink->Flink = &Copy[Mode];
519 }
520 }
521
522 VOID
523 NTAPI
524 KiMoveApcState(PKAPC_STATE OldState,
525 PKAPC_STATE NewState)
526 {
527 /* Restore backup of Original Environment */
528 *NewState = *OldState;
529
530 /* Repair Lists */
531 RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode);
532 RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode);
533 }
534
535 /* PUBLIC FUNCTIONS **********************************************************/
536
537 /*++
538 * @name KeEnterCriticalRegion
539 * @implemented NT4
540 *
541 * The KeEnterCriticalRegion routine temporarily disables the delivery of
542 * normal kernel APCs; special kernel-mode APCs are still delivered.
543 *
544 * @param None.
545 *
546 * @return None.
547 *
548 * @remarks Highest-level drivers can call this routine while running in the
549 * context of the thread that requested the current I/O operation.
550 * Any caller of this routine should call KeLeaveCriticalRegion as
551 * quickly as possible.
552 *
553 * Callers of KeEnterCriticalRegion must be running at IRQL <=
554 * APC_LEVEL.
555 *
556 *--*/
557 VOID
558 NTAPI
559 _KeEnterCriticalRegion(VOID)
560 {
561 /* Use inlined function */
562 KeEnterCriticalRegion();
563 }
564
565 /*++
566 * KeLeaveCriticalRegion
567 * @implemented NT4
568 *
569 * The KeLeaveCriticalRegion routine reenables the delivery of normal
570 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
571 *
572 * @param None.
573 *
574 * @return None.
575 *
576 * @remarks Highest-level drivers can call this routine while running in the
577 * context of the thread that requested the current I/O operation.
578 *
579 * Callers of KeLeaveCriticalRegion must be running at IRQL <=
580 * DISPATCH_LEVEL.
581 *
582 *--*/
583 VOID
584 NTAPI
585 _KeLeaveCriticalRegion(VOID)
586 {
587 /* Use inlined version */
588 KeLeaveCriticalRegion();
589 }
590
591 /*++
592 * KeInitializeApc
593 * @implemented NT4
594 *
595 * The The KeInitializeApc routine initializes an APC object, and registers
596 * the Kernel, Rundown and Normal routines for that object.
597 *
598 * @param Apc
599 * Pointer to a KAPC structure that represents the APC object to
600 * initialize. The caller must allocate storage for the structure
601 * from resident memory.
602 *
603 * @param Thread
604 * Thread to which to deliver the APC.
605 *
606 * @param TargetEnvironment
607 * APC Environment to be used.
608 *
609 * @param KernelRoutine
610 * Points to the KernelRoutine to associate with the APC.
611 * This routine is executed for all APCs.
612 *
613 * @param RundownRoutine
614 * Points to the RundownRoutine to associate with the APC.
615 * This routine is executed when the Thread exists during APC execution.
616 *
617 * @param NormalRoutine
618 * Points to the NormalRoutine to associate with the APC.
619 * This routine is executed at PASSIVE_LEVEL. If this is not specifed,
620 * the APC becomes a Special APC and the Mode and Context parameters are
621 * ignored.
622 *
623 * @param Mode
624 * Specifies the processor mode at which to run the Normal Routine.
625 *
626 * @param Context
627 * Specifices the value to pass as Context parameter to the registered
628 * routines.
629 *
630 * @return None.
631 *
632 * @remarks The caller can queue an initialized APC with KeInsertQueueApc.
633 *
634 *--*/
635 VOID
636 NTAPI
637 KeInitializeApc(IN PKAPC Apc,
638 IN PKTHREAD Thread,
639 IN KAPC_ENVIRONMENT TargetEnvironment,
640 IN PKKERNEL_ROUTINE KernelRoutine,
641 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
642 IN PKNORMAL_ROUTINE NormalRoutine,
643 IN KPROCESSOR_MODE Mode,
644 IN PVOID Context)
645 {
646 /* Sanity check */
647 ASSERT(TargetEnvironment <= InsertApcEnvironment);
648
649 /* Set up the basic APC Structure Data */
650 Apc->Type = ApcObject;
651 Apc->Size = sizeof(KAPC);
652
653 /* Set the Environment */
654 if (TargetEnvironment == CurrentApcEnvironment)
655 {
656 /* Use the current one for the thread */
657 Apc->ApcStateIndex = Thread->ApcStateIndex;
658 }
659 else
660 {
661 /* Sanity check */
662 ASSERT((TargetEnvironment <= Thread->ApcStateIndex) ||
663 (TargetEnvironment == InsertApcEnvironment));
664
665 /* Use the one that was given */
666 Apc->ApcStateIndex = TargetEnvironment;
667 }
668
669 #ifdef _M_IX86
670 /* Set the Thread and Routines */
671 Apc->Thread = Thread;
672 Apc->KernelRoutine = KernelRoutine;
673 Apc->RundownRoutine = RundownRoutine;
674 Apc->NormalRoutine = NormalRoutine;
675 #endif
676
677 /* Check if this is a special APC */
678 if (NormalRoutine)
679 {
680 /* It's a normal one. Set the context and mode */
681 Apc->ApcMode = Mode;
682 Apc->NormalContext = Context;
683 }
684 else
685 {
686 /* It's a special APC, which can only be kernel mode */
687 Apc->ApcMode = KernelMode;
688 Apc->NormalContext = NULL;
689 }
690
691 /* The APC is not inserted*/
692 Apc->Inserted = FALSE;
693 }
694
695 /*++
696 * @name KeInsertQueueApc
697 * @implemented NT4
698 *
699 * The KeInsertQueueApc routine queues a APC for execution when the right
700 * scheduler environment exists.
701 *
702 * @param Apc
703 * Pointer to an initialized control object of type DPC for which the
704 * caller provides the storage.
705 *
706 * @param SystemArgument[1,2]
707 * Pointer to a set of two parameters that contain untyped data.
708 *
709 * @param PriorityBoost
710 * Priority Boost to apply to the Thread.
711 *
712 * @return If the APC is already inserted or APC queueing is disabled, FALSE.
713 * Otherwise, TRUE.
714 *
715 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
716 * and at PASSIVE_LEVEL for the NormalRoutine registered.
717 *
718 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
719 *
720 *--*/
721 BOOLEAN
722 NTAPI
723 KeInsertQueueApc(IN PKAPC Apc,
724 IN PVOID SystemArgument1,
725 IN PVOID SystemArgument2,
726 IN KPRIORITY PriorityBoost)
727 {
728 PKTHREAD Thread = Apc->Thread;
729 KLOCK_QUEUE_HANDLE ApcLock;
730 BOOLEAN State = TRUE;
731 ASSERT_APC(Apc);
732 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
733
734 /* Get the APC lock */
735 KiAcquireApcLock(Thread, &ApcLock);
736
737 /* Make sure we can Queue APCs and that this one isn't already inserted */
738 if (!(Thread->ApcQueueable) && (Apc->Inserted))
739 {
740 /* Fail */
741 State = FALSE;
742 }
743 else
744 {
745 /* Set the System Arguments and set it as inserted */
746 Apc->SystemArgument1 = SystemArgument1;
747 Apc->SystemArgument2 = SystemArgument2;
748 Apc->Inserted = TRUE;
749
750 /* Call the Internal Function */
751 KiInsertQueueApc(Apc, PriorityBoost);
752 }
753
754 /* Release the APC lock and return success */
755 KiReleaseApcLock(&ApcLock);
756 KiExitDispatcher(ApcLock.OldIrql);
757 return State;
758 }
759
760 /*++
761 * @name KeFlushQueueApc
762 * @implemented NT4
763 *
764 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
765 * from the specified Thread's APC queue.
766 *
767 * @param Thread
768 * Pointer to the thread whose APC queue will be flushed.
769 *
770 * @paramt PreviousMode
771 * Specifies which APC Queue to flush.
772 *
773 * @return A pointer to the first entry in the flushed APC queue.
774 *
775 * @remarks If the routine returns NULL, it means that no APCs were flushed.
776 * Callers of this routine must be running at DISPATCH_LEVEL or lower.
777 *
778 *--*/
779 PLIST_ENTRY
780 NTAPI
781 KeFlushQueueApc(IN PKTHREAD Thread,
782 IN KPROCESSOR_MODE PreviousMode)
783 {
784 PKAPC Apc;
785 PLIST_ENTRY FirstEntry, CurrentEntry;
786 KLOCK_QUEUE_HANDLE ApcLock;
787 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
788
789 /* Check if this was user mode */
790 if (PreviousMode == UserMode)
791 {
792 /* Get the APC lock */
793 KiAcquireApcLock(Thread, &ApcLock);
794
795 /* Select user list and check if it's empty */
796 if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
797 {
798 /* Don't return anything */
799 FirstEntry = NULL;
800 goto FlushDone;
801 }
802 }
803 else
804 {
805 /* Select kernel list and check if it's empty */
806 if (IsListEmpty( &Thread->ApcState.ApcListHead[KernelMode]))
807 {
808 /* Don't return anything */
809 return NULL;
810 }
811
812 /* Otherwise, acquire the APC lock */
813 KiAcquireApcLock(Thread, &ApcLock);
814 }
815
816 /* Get the first entry and check if the list is empty now */
817 FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink;
818 if (FirstEntry == &Thread->ApcState.ApcListHead[PreviousMode])
819 {
820 /* It is, clear the returned entry */
821 FirstEntry = NULL;
822 }
823 else
824 {
825 /* It's not, remove the first entry */
826 RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]);
827
828 /* Loop all the entries */
829 CurrentEntry = FirstEntry;
830 do
831 {
832 /* Get the APC and make it un-inserted */
833 Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
834 Apc->Inserted = FALSE;
835
836 /* Get the next entry */
837 CurrentEntry = CurrentEntry->Flink;
838 } while (CurrentEntry != FirstEntry);
839
840 /* Re-initialize the list */
841 InitializeListHead(&Thread->ApcState.ApcListHead[PreviousMode]);
842 }
843
844 /* Release the lock */
845 FlushDone:
846 KiReleaseApcLock(&ApcLock);
847
848 /* Return the first entry */
849 return FirstEntry;
850 }
851
852 /*++
853 * @name KeRemoveQueueApc
854 * @implemented NT4
855 *
856 * The KeRemoveQueueApc routine removes a given APC object from the system
857 * APC queue.
858 *
859 * @params Apc
860 * Pointer to an initialized APC object that was queued by calling
861 * KeInsertQueueApc.
862 *
863 * @return TRUE if the APC Object is in the APC Queue. Otherwise, no operation
864 * is performed and FALSE is returned.
865 *
866 * @remarks If the given APC Object is currently queued, it is removed from the
867 * queue and any calls to the registered routines are cancelled.
868 *
869 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
870 *
871 *--*/
872 BOOLEAN
873 NTAPI
874 KeRemoveQueueApc(IN PKAPC Apc)
875 {
876 PKTHREAD Thread = Apc->Thread;
877 PKAPC_STATE ApcState;
878 BOOLEAN Inserted;
879 KLOCK_QUEUE_HANDLE ApcLock;
880 ASSERT_APC(Apc);
881 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
882
883 /* Get the APC lock */
884 KiAcquireApcLock(Thread, &ApcLock);
885
886 /* Check if it's inserted */
887 Inserted = Apc->Inserted;
888 if (Inserted)
889 {
890 /* Set it as non-inserted and get the APC state */
891 Apc->Inserted = FALSE;
892 ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex];
893
894 /* Acquire the dispatcher lock and remove it from the list */
895 KiAcquireDispatcherLockAtDpcLevel();
896 RemoveEntryList(&Apc->ApcListEntry);
897
898 /* If the Queue is completely empty, then no more APCs are pending */
899 if (IsListEmpty(&ApcState->ApcListHead[Apc->ApcMode]))
900 {
901 /* Set the correct state based on the APC Mode */
902 if (Apc->ApcMode == KernelMode)
903 {
904 /* No more pending kernel APCs */
905 ApcState->KernelApcPending = FALSE;
906 }
907 else
908 {
909 /* No more pending user APCs */
910 ApcState->UserApcPending = FALSE;
911 }
912 }
913
914 /* Release dispatcher lock */
915 KiReleaseDispatcherLockFromDpcLevel();
916 }
917
918 /* Release the lock and return */
919 KiReleaseApcLock(&ApcLock);
920 return Inserted;
921 }
922
923 /*++
924 * @name KeAreApcsDisabled
925 * @implemented NT4
926 *
927 * The KeAreApcsDisabled routine returns whether kernel APC delivery is
928 * disabled for the current thread.
929 *
930 * @param None.
931 *
932 * @return KeAreApcsDisabled returns TRUE if the thread is within a critical
933 * region or a guarded region, and FALSE otherwise.
934 *
935 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled
936 * determine if normal kernel APCs are disabled.
937 *
938 * A thread that is inside critical region has both user APCs and
939 * normal kernel APCs disabled, but not special kernel APCs.
940 *
941 * A thread that is inside a guarded region has all APCs disabled,
942 * including special kernel APCs.
943 *
944 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
945 *
946 *--*/
947 BOOLEAN
948 NTAPI
949 KeAreApcsDisabled(VOID)
950 {
951 /* Return the Kernel APC State */
952 return KeGetCurrentThread()->CombinedApcDisable ? TRUE : FALSE;
953 }
954
955 /*++
956 * @name KeAreAllApcsDisabled
957 * @implemented NT5.1
958 *
959 * The KeAreAllApcsDisabled routine returns whether the calling thread is
960 * inside a guarded region or running at IRQL = APC_LEVEL, which disables
961 * all APC delivery.
962 *
963 * @param None.
964 *
965 * @return KeAreAllApcsDisabled returns TRUE if the thread is within a guarded
966 * guarded region or running at IRQL >= APC_LEVEL, and FALSE otherwise.
967 *
968 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use this routine to
969 * determine if all APCs delivery is disabled.
970 *
971 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
972 *
973 *--*/
974 BOOLEAN
975 NTAPI
976 KeAreAllApcsDisabled(VOID)
977 {
978 /* Return the Special APC State */
979 return ((KeGetCurrentThread()->SpecialApcDisable) ||
980 (KeGetCurrentIrql() >= APC_LEVEL)) ? TRUE : FALSE;
981 }
982
983