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