Move all temporary import libs to a single directory.
[reactos.git] / 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 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 *--*/
34 VOID
35 NTAPI
36 KiCheckForKernelApcDelivery(VOID)
37 {
38 KIRQL OldIrql;
39
40 /* We should only deliver at passive */
41 if (KeGetCurrentIrql() == PASSIVE_LEVEL)
42 {
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);
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;
58 HalRequestSoftwareInterrupt(APC_LEVEL);
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 *--*/
83 VOID
84 FASTCALL
85 KiInsertQueueApc(IN PKAPC Apc,
86 IN KPRIORITY PriorityBoost)
87 {
88 PKTHREAD Thread = Apc->Thread;
89 PKAPC_STATE ApcState;
90 KPROCESSOR_MODE ApcMode;
91 PLIST_ENTRY ListHead, NextEntry;
92 PKAPC QueuedApc;
93 PKGATE Gate;
94 NTSTATUS Status;
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 */
179 HalRequestSoftwareInterrupt(APC_LEVEL);
180 }
181 }
182 }
183 else
184 {
185 /* Acquire the dispatcher lock */
186 KiAcquireDispatcherLock();
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 */
208 Status = STATUS_KERNEL_APC;
209
210 /* Wake up the thread */
211 KiUnwaitThread(Thread, Status, PriorityBoost);
212 }
213 else if (Thread->State == GateWait)
214 {
215 /* Lock the thread */
216 KiAcquireThreadLock(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 */
233 KiAcquireDispatcherObject(&Gate->Header);
234
235 /* Remove it from the waiters list */
236 RemoveEntryList(&Thread->WaitBlock[0].WaitListEntry);
237
238 /* Unlock the gate */
239 KiReleaseDispatcherObject(&Gate->Header);
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;
246 KiInsertDeferredReadyList(Thread);
247 }
248
249 /* Release the thread lock */
250 KiReleaseThreadLock(Thread);
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;
260 Status = STATUS_USER_APC;
261
262 /* Wake up the thread */
263 KiUnwaitThread(Thread, Status, PriorityBoost);
264 }
265
266 /* Release dispatcher lock */
267 KiReleaseDispatcherLockFromDpcLevel();
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 *--*/
300 VOID
301 NTAPI
302 KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode,
303 IN PKEXCEPTION_FRAME ExceptionFrame,
304 IN PKTRAP_FRAME TrapFrame)
305 {
306 PKTHREAD Thread = KeGetCurrentThread();
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;
315 PVOID SystemArgument1;
316 PVOID SystemArgument2;
317 ASSERT_IRQL_EQUAL(APC_LEVEL);
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 */
333 KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
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 /* Get the next Entry */
344 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
345 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
346
347 /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
348 NormalRoutine = Apc->NormalRoutine;
349 KernelRoutine = Apc->KernelRoutine;
350 NormalContext = Apc->NormalContext;
351 SystemArgument1 = Apc->SystemArgument1;
352 SystemArgument2 = Apc->SystemArgument2;
353
354 /* Special APC */
355 if (!NormalRoutine)
356 {
357 /* Remove the APC from the list */
358 RemoveEntryList(ApcListEntry);
359 Apc->Inserted = FALSE;
360
361 /* Release the APC lock */
362 KiReleaseApcLock(&ApcLock);
363
364 /* Call the Special APC */
365 KernelRoutine(Apc,
366 &NormalRoutine,
367 &NormalContext,
368 &SystemArgument1,
369 &SystemArgument2);
370
371 /* Make sure it returned correctly */
372 if (KeGetCurrentIrql() != ApcLock.OldIrql)
373 {
374 KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
375 (KeGetCurrentIrql() << 16) |
376 (ApcLock.OldIrql << 8),
377 (ULONG_PTR)KernelRoutine,
378 (ULONG_PTR)Apc,
379 (ULONG_PTR)NormalRoutine);
380 }
381 }
382 else
383 {
384 /* Normal Kernel APC, make sure it's safe to deliver */
385 if ((Thread->ApcState.KernelApcInProgress) ||
386 (Thread->KernelApcDisable))
387 {
388 /* Release lock and return */
389 KiReleaseApcLock(&ApcLock);
390 goto Quickie;
391 }
392
393 /* Dequeue the APC */
394 RemoveEntryList(ApcListEntry);
395 Apc->Inserted = FALSE;
396
397 /* Go back to APC_LEVEL */
398 KiReleaseApcLock(&ApcLock);
399
400 /* Call the Kernel APC */
401 KernelRoutine(Apc,
402 &NormalRoutine,
403 &NormalContext,
404 &SystemArgument1,
405 &SystemArgument2);
406
407 /* Make sure it returned correctly */
408 if (KeGetCurrentIrql() != ApcLock.OldIrql)
409 {
410 KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
411 (KeGetCurrentIrql() << 16) |
412 (ApcLock.OldIrql << 8),
413 (ULONG_PTR)KernelRoutine,
414 (ULONG_PTR)Apc,
415 (ULONG_PTR)NormalRoutine);
416 }
417
418 /* Check if there still is a Normal Routine */
419 if (NormalRoutine)
420 {
421 /* At Passive Level, an APC can be prempted by a Special APC */
422 Thread->ApcState.KernelApcInProgress = TRUE;
423 KeLowerIrql(PASSIVE_LEVEL);
424
425 /* Call and Raise IRQL back to APC_LEVEL */
426 NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
427 KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql);
428 }
429
430 /* Set Kernel APC in progress to false and loop again */
431 Thread->ApcState.KernelApcInProgress = FALSE;
432 }
433 }
434
435 /* Now we do the User APCs */
436 if ((DeliveryMode == UserMode) &&
437 !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
438 (Thread->ApcState.UserApcPending))
439 {
440 /* Lock the APC Queue */
441 KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
442
443 /* It's not pending anymore */
444 Thread->ApcState.UserApcPending = FALSE;
445
446 /* Check if the list became empty now */
447 if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
448 {
449 /* It is, release the lock and break out */
450 KiReleaseApcLock(&ApcLock);
451 goto Quickie;
452 }
453
454 /* Get the actual APC object */
455 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
456 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
457
458 /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
459 NormalRoutine = Apc->NormalRoutine;
460 KernelRoutine = Apc->KernelRoutine;
461 NormalContext = Apc->NormalContext;
462 SystemArgument1 = Apc->SystemArgument1;
463 SystemArgument2 = Apc->SystemArgument2;
464
465 /* Remove the APC from Queue, and release the lock */
466 RemoveEntryList(ApcListEntry);
467 Apc->Inserted = FALSE;
468 KiReleaseApcLock(&ApcLock);
469
470 /* Call the kernel routine */
471 KernelRoutine(Apc,
472 &NormalRoutine,
473 &NormalContext,
474 &SystemArgument1,
475 &SystemArgument2);
476
477 /* Check if there's no normal routine */
478 if (!NormalRoutine)
479 {
480 /* Check if more User APCs are Pending */
481 KeTestAlertThread(UserMode);
482 }
483 else
484 {
485 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
486 KiInitializeUserApc(ExceptionFrame,
487 TrapFrame,
488 NormalRoutine,
489 NormalContext,
490 SystemArgument1,
491 SystemArgument2);
492 }
493 }
494
495 Quickie:
496 /* Make sure we're still in the same process */
497 if (Process != Thread->ApcState.Process)
498 {
499 /* Erm, we got attached or something! BAD! */
500 KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
501 (ULONG_PTR)Process,
502 (ULONG_PTR)Thread->ApcState.Process,
503 Thread->ApcStateIndex,
504 KeGetCurrentPrcb()->DpcRoutineActive);
505 }
506
507 /* Restore the trap frame */
508 Thread->TrapFrame = OldTrapFrame;
509 }
510
511 FORCEINLINE
512 VOID
513 RepairList(IN PLIST_ENTRY Original,
514 IN PLIST_ENTRY Copy,
515 IN KPROCESSOR_MODE Mode)
516 {
517 /* Check if the list for this mode is empty */
518 if (IsListEmpty(&Original[Mode]))
519 {
520 /* It is, all we need to do is initialize it */
521 InitializeListHead(&Copy[Mode]);
522 }
523 else
524 {
525 /* Copy the lists */
526 Copy[Mode].Flink = Original[Mode].Flink;
527 Copy[Mode].Blink = Original[Mode].Blink;
528 Original[Mode].Flink->Blink = &Copy[Mode];
529 Original[Mode].Blink->Flink = &Copy[Mode];
530 }
531 }
532
533 VOID
534 NTAPI
535 KiMoveApcState(PKAPC_STATE OldState,
536 PKAPC_STATE NewState)
537 {
538 /* Restore backup of Original Environment */
539 RtlCopyMemory(NewState, OldState, KAPC_STATE_ACTUAL_LENGTH);
540
541 /* Repair Lists */
542 RepairList(OldState->ApcListHead, NewState->ApcListHead, KernelMode);
543 RepairList(OldState->ApcListHead, NewState->ApcListHead, UserMode);
544 }
545
546 /* PUBLIC FUNCTIONS **********************************************************/
547
548 /*++
549 * @name KeEnterCriticalRegion
550 * @implemented NT4
551 *
552 * The KeEnterCriticalRegion routine temporarily disables the delivery of
553 * normal kernel APCs; special kernel-mode APCs are still delivered.
554 *
555 * @param None.
556 *
557 * @return None.
558 *
559 * @remarks Highest-level drivers can call this routine while running in the
560 * context of the thread that requested the current I/O operation.
561 * Any caller of this routine should call KeLeaveCriticalRegion as
562 * quickly as possible.
563 *
564 * Callers of KeEnterCriticalRegion must be running at IRQL <=
565 * APC_LEVEL.
566 *
567 *--*/
568 VOID
569 NTAPI
570 _KeEnterCriticalRegion(VOID)
571 {
572 /* Use inlined function */
573 KeEnterCriticalRegion();
574 }
575
576 /*++
577 * KeLeaveCriticalRegion
578 * @implemented NT4
579 *
580 * The KeLeaveCriticalRegion routine reenables the delivery of normal
581 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
582 *
583 * @param None.
584 *
585 * @return None.
586 *
587 * @remarks Highest-level drivers can call this routine while running in the
588 * context of the thread that requested the current I/O operation.
589 *
590 * Callers of KeLeaveCriticalRegion must be running at IRQL <=
591 * DISPATCH_LEVEL.
592 *
593 *--*/
594 VOID
595 NTAPI
596 _KeLeaveCriticalRegion(VOID)
597 {
598 /* Use inlined version */
599 KeLeaveCriticalRegion();
600 }
601
602 /*++
603 * KeInitializeApc
604 * @implemented NT4
605 *
606 * The KeInitializeApc routine initializes an APC object, and registers
607 * the Kernel, Rundown and Normal routines for that object.
608 *
609 * @param Apc
610 * Pointer to a KAPC structure that represents the APC object to
611 * initialize. The caller must allocate storage for the structure
612 * from resident memory.
613 *
614 * @param Thread
615 * Thread to which to deliver the APC.
616 *
617 * @param TargetEnvironment
618 * APC Environment to be used.
619 *
620 * @param KernelRoutine
621 * Points to the KernelRoutine to associate with the APC.
622 * This routine is executed for all APCs.
623 *
624 * @param RundownRoutine
625 * Points to the RundownRoutine to associate with the APC.
626 * This routine is executed when the Thread exits during APC execution.
627 *
628 * @param NormalRoutine
629 * Points to the NormalRoutine to associate with the APC.
630 * This routine is executed at PASSIVE_LEVEL. If this is not specifed,
631 * the APC becomes a Special APC and the Mode and Context parameters are
632 * ignored.
633 *
634 * @param Mode
635 * Specifies the processor mode at which to run the Normal Routine.
636 *
637 * @param Context
638 * Specifices the value to pass as Context parameter to the registered
639 * routines.
640 *
641 * @return None.
642 *
643 * @remarks The caller can queue an initialized APC with KeInsertQueueApc.
644 *
645 *--*/
646 VOID
647 NTAPI
648 KeInitializeApc(IN PKAPC Apc,
649 IN PKTHREAD Thread,
650 IN KAPC_ENVIRONMENT TargetEnvironment,
651 IN PKKERNEL_ROUTINE KernelRoutine,
652 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
653 IN PKNORMAL_ROUTINE NormalRoutine,
654 IN KPROCESSOR_MODE Mode,
655 IN PVOID Context)
656 {
657 /* Sanity check */
658 ASSERT(TargetEnvironment <= InsertApcEnvironment);
659
660 /* Set up the basic APC Structure Data */
661 Apc->Type = ApcObject;
662 Apc->Size = sizeof(KAPC);
663
664 /* Set the Environment */
665 if (TargetEnvironment == CurrentApcEnvironment)
666 {
667 /* Use the current one for the thread */
668 Apc->ApcStateIndex = Thread->ApcStateIndex;
669 }
670 else
671 {
672 /* Sanity check */
673 ASSERT((TargetEnvironment <= Thread->ApcStateIndex) ||
674 (TargetEnvironment == InsertApcEnvironment));
675
676 /* Use the one that was given */
677 Apc->ApcStateIndex = TargetEnvironment;
678 }
679
680 /* Set the Thread and Routines */
681 Apc->Thread = Thread;
682 Apc->KernelRoutine = KernelRoutine;
683 Apc->RundownRoutine = RundownRoutine;
684 Apc->NormalRoutine = NormalRoutine;
685
686 /* Check if this is a special APC */
687 if (NormalRoutine)
688 {
689 /* It's a normal one. Set the context and mode */
690 Apc->ApcMode = Mode;
691 Apc->NormalContext = Context;
692 }
693 else
694 {
695 /* It's a special APC, which can only be kernel mode */
696 Apc->ApcMode = KernelMode;
697 Apc->NormalContext = NULL;
698 }
699
700 /* The APC is not inserted */
701 Apc->Inserted = FALSE;
702 }
703
704 /*++
705 * @name KeInsertQueueApc
706 * @implemented NT4
707 *
708 * The KeInsertQueueApc routine queues a APC for execution when the right
709 * scheduler environment exists.
710 *
711 * @param Apc
712 * Pointer to an initialized control object of type APC for which the
713 * caller provides the storage.
714 *
715 * @param SystemArgument[1,2]
716 * Pointer to a set of two parameters that contain untyped data.
717 *
718 * @param PriorityBoost
719 * Priority Boost to apply to the Thread.
720 *
721 * @return If the APC is already inserted or APC queueing is disabled, FALSE.
722 * Otherwise, TRUE.
723 *
724 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
725 * and at PASSIVE_LEVEL for the NormalRoutine registered.
726 *
727 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
728 *
729 *--*/
730 BOOLEAN
731 NTAPI
732 KeInsertQueueApc(IN PKAPC Apc,
733 IN PVOID SystemArgument1,
734 IN PVOID SystemArgument2,
735 IN KPRIORITY PriorityBoost)
736 {
737 PKTHREAD Thread = Apc->Thread;
738 KLOCK_QUEUE_HANDLE ApcLock;
739 BOOLEAN State = TRUE;
740 ASSERT_APC(Apc);
741 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
742
743 /* Get the APC lock */
744 KiAcquireApcLock(Thread, &ApcLock);
745
746 /* Make sure we can Queue APCs and that this one isn't already inserted */
747 if (!(Thread->ApcQueueable) || (Apc->Inserted))
748 {
749 /* Fail */
750 State = FALSE;
751 }
752 else
753 {
754 /* Set the System Arguments and set it as inserted */
755 Apc->SystemArgument1 = SystemArgument1;
756 Apc->SystemArgument2 = SystemArgument2;
757 Apc->Inserted = TRUE;
758
759 /* Call the Internal Function */
760 KiInsertQueueApc(Apc, PriorityBoost);
761 }
762
763 /* Release the APC lock and return success */
764 KiReleaseApcLockFromDpcLevel(&ApcLock);
765 KiExitDispatcher(ApcLock.OldIrql);
766 return State;
767 }
768
769 /*++
770 * @name KeFlushQueueApc
771 * @implemented NT4
772 *
773 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
774 * from the specified Thread's APC queue.
775 *
776 * @param Thread
777 * Pointer to the thread whose APC queue will be flushed.
778 *
779 * @param PreviousMode
780 * Specifies which APC Queue to flush.
781 *
782 * @return A pointer to the first entry in the flushed APC queue.
783 *
784 * @remarks If the routine returns NULL, it means that no APCs were flushed.
785 * Callers of this routine must be running at DISPATCH_LEVEL or lower.
786 *
787 *--*/
788 PLIST_ENTRY
789 NTAPI
790 KeFlushQueueApc(IN PKTHREAD Thread,
791 IN KPROCESSOR_MODE PreviousMode)
792 {
793 PKAPC Apc;
794 PLIST_ENTRY FirstEntry, CurrentEntry;
795 KLOCK_QUEUE_HANDLE ApcLock;
796 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
797
798 /* Check if this was user mode */
799 if (PreviousMode == UserMode)
800 {
801 /* Get the APC lock */
802 KiAcquireApcLock(Thread, &ApcLock);
803
804 /* Select user list and check if it's empty */
805 if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
806 {
807 /* Don't return anything */
808 FirstEntry = NULL;
809 goto FlushDone;
810 }
811 }
812 else
813 {
814 /* Select kernel list and check if it's empty */
815 if (IsListEmpty( &Thread->ApcState.ApcListHead[KernelMode]))
816 {
817 /* Don't return anything */
818 return NULL;
819 }
820
821 /* Otherwise, acquire the APC lock */
822 KiAcquireApcLock(Thread, &ApcLock);
823 }
824
825 /* Get the first entry and check if the list is empty now */
826 FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink;
827 if (FirstEntry == &Thread->ApcState.ApcListHead[PreviousMode])
828 {
829 /* It is, clear the returned entry */
830 FirstEntry = NULL;
831 }
832 else
833 {
834 /* It's not, remove the first entry */
835 RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]);
836
837 /* Loop all the entries */
838 CurrentEntry = FirstEntry;
839 do
840 {
841 /* Get the APC and make it un-inserted */
842 Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
843 Apc->Inserted = FALSE;
844
845 /* Get the next entry */
846 CurrentEntry = CurrentEntry->Flink;
847 } while (CurrentEntry != FirstEntry);
848
849 /* Re-initialize the list */
850 InitializeListHead(&Thread->ApcState.ApcListHead[PreviousMode]);
851 }
852
853 /* Release the lock */
854 FlushDone:
855 KiReleaseApcLock(&ApcLock);
856
857 /* Return the first entry */
858 return FirstEntry;
859 }
860
861 /*++
862 * @name KeRemoveQueueApc
863 * @implemented NT4
864 *
865 * The KeRemoveQueueApc routine removes a given APC object from the system
866 * APC queue.
867 *
868 * @param Apc
869 * Pointer to an initialized APC object that was queued by calling
870 * KeInsertQueueApc.
871 *
872 * @return TRUE if the APC Object is in the APC Queue. Otherwise, no operation
873 * is performed and FALSE is returned.
874 *
875 * @remarks If the given APC Object is currently queued, it is removed from the
876 * queue and any calls to the registered routines are cancelled.
877 *
878 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
879 *
880 *--*/
881 BOOLEAN
882 NTAPI
883 KeRemoveQueueApc(IN PKAPC Apc)
884 {
885 PKTHREAD Thread = Apc->Thread;
886 PKAPC_STATE ApcState;
887 BOOLEAN Inserted;
888 KLOCK_QUEUE_HANDLE ApcLock;
889 ASSERT_APC(Apc);
890 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
891
892 /* Get the APC lock */
893 KiAcquireApcLock(Thread, &ApcLock);
894
895 /* Check if it's inserted */
896 Inserted = Apc->Inserted;
897 if (Inserted)
898 {
899 /* Set it as non-inserted and get the APC state */
900 Apc->Inserted = FALSE;
901 ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex];
902
903 /* Acquire the dispatcher lock and remove it from the list */
904 KiAcquireDispatcherLockAtDpcLevel();
905 if (RemoveEntryList(&Apc->ApcListEntry))
906 {
907 /* Set the correct state based on the APC Mode */
908 if (Apc->ApcMode == KernelMode)
909 {
910 /* No more pending kernel APCs */
911 ApcState->KernelApcPending = FALSE;
912 }
913 else
914 {
915 /* No more pending user APCs */
916 ApcState->UserApcPending = FALSE;
917 }
918 }
919
920 /* Release dispatcher lock */
921 KiReleaseDispatcherLockFromDpcLevel();
922 }
923
924 /* Release the lock and return */
925 KiReleaseApcLock(&ApcLock);
926 return Inserted;
927 }
928
929 /*++
930 * @name KeAreApcsDisabled
931 * @implemented NT4
932 *
933 * The KeAreApcsDisabled routine returns whether kernel APC delivery is
934 * disabled for the current thread.
935 *
936 * @param None.
937 *
938 * @return KeAreApcsDisabled returns TRUE if the thread is within a critical
939 * region or a guarded region, and FALSE otherwise.
940 *
941 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled
942 * to determine if normal kernel APCs are disabled.
943 *
944 * A thread that is inside critical region has both user APCs and
945 * normal kernel APCs disabled, but not special kernel APCs.
946 *
947 * A thread that is inside a guarded region has all APCs disabled,
948 * including special kernel APCs.
949 *
950 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
951 *
952 *--*/
953 BOOLEAN
954 NTAPI
955 KeAreApcsDisabled(VOID)
956 {
957 /* Return the Kernel APC State */
958 return KeGetCurrentThread()->CombinedApcDisable ? TRUE : FALSE;
959 }
960
961 /*++
962 * @name KeAreAllApcsDisabled
963 * @implemented NT5.1
964 *
965 * The KeAreAllApcsDisabled routine returns whether the calling thread is
966 * inside a guarded region or running at IRQL >= APC_LEVEL, which disables
967 * all APC delivery.
968 *
969 * @param None.
970 *
971 * @return KeAreAllApcsDisabled returns TRUE if the thread is within a guarded
972 * guarded region or running at IRQL >= APC_LEVEL, and FALSE otherwise.
973 *
974 * @remarks A thread running at IRQL = PASSIVE_LEVEL can use this routine to
975 * determine if all APC delivery is disabled.
976 *
977 * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
978 *
979 *--*/
980 BOOLEAN
981 NTAPI
982 KeAreAllApcsDisabled(VOID)
983 {
984 /* Return the Special APC State */
985 return ((KeGetCurrentThread()->SpecialApcDisable) ||
986 (KeGetCurrentIrql() >= APC_LEVEL)) ? TRUE : FALSE;
987 }