ReactOS  r74244
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  *--*/
34 VOID
35 NTAPI
37 {
38  KIRQL OldIrql;
39 
40  /* We should only deliver at passive */
42  {
43  /* Raise to APC and Deliver APCs, then lower back to Passive */
44  KeRaiseIrql(APC_LEVEL, &OldIrql);
45  KiDeliverApc(KernelMode, 0, 0);
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  *--*/
83 VOID
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 */
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 */
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 */
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 */
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
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 */
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  /* 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;
354  SystemArgument1 = Apc->SystemArgument1;
355  SystemArgument2 = Apc->SystemArgument2;
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,
371  &SystemArgument1,
372  &SystemArgument2);
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,
407  &SystemArgument1,
408  &SystemArgument2);
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 */
444  KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
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;
465  SystemArgument1 = Apc->SystemArgument1;
466  SystemArgument2 = Apc->SystemArgument2;
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,
477  &SystemArgument1,
478  &SystemArgument2);
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,
493  SystemArgument1,
494  SystemArgument2);
495  }
496  }
497 
498 Quickie:
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,
504  (ULONG_PTR)Process,
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 
515 VOID
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 */
524  InitializeListHead(&Copy[Mode]);
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 
536 VOID
537 NTAPI
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  *--*/
571 VOID
572 NTAPI
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  *--*/
597 VOID
598 NTAPI
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  *--*/
649 VOID
650 NTAPI
653  IN KAPC_ENVIRONMENT TargetEnvironment,
654  IN PKKERNEL_ROUTINE KernelRoutine,
655  IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
656  IN PKNORMAL_ROUTINE NormalRoutine,
658  IN PVOID Context)
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  *--*/
733 BOOLEAN
734 NTAPI
739 {
740  PKTHREAD Thread = Apc->Thread;
741  KLOCK_QUEUE_HANDLE ApcLock;
742  BOOLEAN State = TRUE;
743  ASSERT_APC(Apc);
745 
746  /* Get the APC lock */
747  KiAcquireApcLock(Thread, &ApcLock);
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 */
763  KiInsertQueueApc(Apc, PriorityBoost);
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  *--*/
792 NTAPI
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 */
805  KiAcquireApcLock(Thread, &ApcLock);
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 */
825  KiAcquireApcLock(Thread, &ApcLock);
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 */
857 FlushDone:
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  *--*/
884 BOOLEAN
885 NTAPI
887 {
888  PKTHREAD Thread = Apc->Thread;
890  BOOLEAN Inserted;
891  KLOCK_QUEUE_HANDLE ApcLock;
892  ASSERT_APC(Apc);
894 
895  /* Get the APC lock */
896  KiAcquireApcLock(Thread, &ApcLock);
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  *--*/
956 BOOLEAN
957 NTAPI
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  *--*/
983 BOOLEAN
984 NTAPI
986 {
987  /* Return the Special APC State */
988  return ((KeGetCurrentThread()->SpecialApcDisable) ||
989  (KeGetCurrentIrql() >= APC_LEVEL)) ? TRUE : FALSE;
990 }
DWORD *typedef PVOID
Definition: winlogon.h:52
#define KeGetCurrentIrql()
Definition: env_spec_w32.h:706
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
FORCEINLINE VOID KiReleaseThreadLock(IN PKTHREAD Thread)
Definition: ke_x.h:244
#define ASSERT_IRQL_LESS_OR_EQUAL(x)
Definition: debug.h:250
#define IN
Definition: typedefs.h:39
VOID NTAPI KiCheckForKernelApcDelivery(VOID)
Definition: apc.c:36
VOID FASTCALL KiUnwaitThread(IN PKTHREAD Thread, IN LONG_PTR WaitStatus, IN KPRIORITY Increment)
Definition: wait.c:89
#define KeRaiseIrql(irql, oldIrql)
Definition: env_spec_w32.h:597
NTSYSAPI VOID NTAPI RtlCopyMemory(VOID UNALIGNED *Destination, CONST VOID UNALIGNED *Source, ULONG Length)
DISPATCHER_HEADER Header
Definition: ketypes.h:796
#define KeLowerIrql(oldIrql)
Definition: env_spec_w32.h:602
struct _KAPC KAPC
#define KAPC_STATE_ACTUAL_LENGTH
Definition: ketypes.h:1260
enum _KAPC_ENVIRONMENT KAPC_ENVIRONMENT
BOOLEAN NTAPI KeAreAllApcsDisabled(VOID)
Definition: apc.c:985
_In_ ULONG Mode
Definition: hubbusif.h:303
SHORT SpecialApcDisable
Definition: ketypes.h:1052
BOOLEAN NTAPI KeRemoveQueueApc(IN PKAPC Apc)
Definition: apc.c:886
ASSERT((InvokeOnSuccess||InvokeOnError||InvokeOnCancel)?(CompletionRoutine!=NULL):TRUE)
struct _LIST_ENTRY * Blink
Definition: typedefs.h:121
FORCEINLINE VOID InsertHeadList(_Inout_ PLIST_ENTRY ListHead, _Inout_ __drv_aliasesMem PLIST_ENTRY Entry)
Definition: rtlfuncs.h:201
#define ASSERT_APC(Object)
FORCEINLINE VOID KiInsertDeferredReadyList(IN PKTHREAD Thread)
Definition: ke_x.h:179
FORCEINLINE struct _KPRCB * KeGetCurrentPrcb(VOID)
Definition: ketypes.h:1054
#define TRUE
Definition: numbers.c:17
FORCEINLINE VOID KiAcquireApcLock(IN PKTHREAD Thread, IN PKLOCK_QUEUE_HANDLE Handle)
Definition: ke_x.h:600
PKTRAP_FRAME TrapFrame
Definition: ketypes.h:1181
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: stubs.c:203
#define InsertTailList(ListHead, Entry)
FORCEINLINE VOID KiReleaseApcLock(IN PKLOCK_QUEUE_HANDLE Handle)
Definition: ke_x.h:627
#define FASTCALL
Definition: nt_native.h:50
_Must_inspect_result_ FORCEINLINE BOOLEAN IsListEmpty(_In_ const LIST_ENTRY *ListHead)
Definition: rtlfuncs.h:57
LONG KPRIORITY
Definition: compat.h:454
VOID FASTCALL KiInsertQueueApc(IN PKAPC Apc, IN KPRIORITY PriorityBoost)
Definition: apc.c:85
uint32_t ULONG_PTR
Definition: typedefs.h:64
FORCEINLINE BOOLEAN RemoveEntryList(_In_ PLIST_ENTRY Entry)
Definition: rtlfuncs.h:105
UCHAR KIRQL
Definition: env_spec_w32.h:591
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
SHORT KernelApcDisable
Definition: ketypes.h:1051
#define ASSERT_IRQL_EQUAL(x)
Definition: debug.h:56
KAPC_STATE ApcState
Definition: ketypes.h:969
_In_ CCHAR PriorityBoost
Definition: iofuncs.h:763
smooth NULL
Definition: ftsmooth.c:464
#define FORCEINLINE
Definition: ntbasedef.h:213
FORCEINLINE VOID KiReleaseApcLockFromDpcLevel(IN PKLOCK_QUEUE_HANDLE Handle)
Definition: ke_x.h:635
#define STATUS_KERNEL_APC
Definition: ntstatus.h:79
VOID NTAPI _KeEnterCriticalRegion(VOID)
Definition: apc.c:573
VOID FASTCALL KiExitDispatcher(KIRQL OldIrql)
ULONG Alertable
Definition: ketypes.h:954
_In_opt_ PVOID _In_opt_ PVOID SystemArgument1
Definition: ketypes.h:660
FORCEINLINE VOID KiAcquireThreadLock(IN PKTHREAD Thread)
Definition: ke_x.h:234
struct _LIST_ENTRY * Flink
Definition: typedefs.h:120
unsigned char BOOLEAN
_In_ KPROCESSOR_MODE PreviousMode
Definition: sefuncs.h:103
PVOID SystemArgument1
Definition: ketypes.h:536
FORCEINLINE VOID KiAcquireDispatcherObject(IN DISPATCHER_HEADER *Object)
Definition: ke_x.h:127
VOID NTAPI KiMoveApcState(PKAPC_STATE OldState, PKAPC_STATE NewState)
Definition: apc.c:538
FORCEINLINE VOID KiRequestApcInterrupt(IN BOOLEAN NeedApc, IN UCHAR Processor)
Definition: ke_x.h:270
PLIST_ENTRY NTAPI KeFlushQueueApc(IN PKTHREAD Thread, IN KPROCESSOR_MODE PreviousMode)
Definition: apc.c:793
FORCEINLINE VOID KiReleaseDispatcherObject(IN DISPATCHER_HEADER *Object)
Definition: ke_x.h:137
VOID(NTAPI * PKRUNDOWN_ROUTINE)(IN struct _KAPC *Apc)
Definition: ketypes.h:638
_In_opt_ PFILE_OBJECT _In_opt_ PETHREAD Thread
Definition: fltkernel.h:2653
PKQUEUE Queue
Definition: ketypes.h:1044
VOID(NTAPI * PKNORMAL_ROUTINE)(IN PVOID NormalContext OPTIONAL, IN PVOID SystemArgument1 OPTIONAL, IN PVOID SystemArgument2 OPTIONAL)
Definition: ketypes.h:632
CCHAR KPROCESSOR_MODE
Definition: ketypes.h:7
LIST_ENTRY WaitListEntry
Definition: ketypes.h:1041
unsigned char UCHAR
Definition: xmlstorage.h:181
PVOID NormalContext
Definition: ketypes.h:535
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:803
#define KeEnterCriticalRegion()
Definition: ke_x.h:83
Definition: ketypes.h:520
Definition: typedefs.h:118
volatile ULONG NextProcessor
Definition: ketypes.h:977
#define PASSIVE_LEVEL
Definition: env_spec_w32.h:693
Status
Definition: gdiplustypes.h:24
_In_opt_ PVOID _In_opt_ PVOID _In_opt_ PVOID SystemArgument2
Definition: ketypes.h:660
LONG_PTR WaitStatus
Definition: ketypes.h:1002
BOOLEAN NTAPI KeAreApcsDisabled(VOID)
Definition: apc.c:958
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
#define STATUS_USER_APC
Definition: ntstatus.h:78
VOID FASTCALL HalRequestSoftwareInterrupt(IN KIRQL Irql)
Definition: pic.c:271
VOID Copy(PVOID Src, PVOID Dst, ULONG NumBytes)
Definition: mmixer.c:131
enum State_ State
Definition: pofuncs.h:54
LONG NTSTATUS
Definition: DriverTester.h:11
#define KeLeaveCriticalRegion()
Definition: ke_x.h:114
FORCEINLINE KIRQL KiAcquireDispatcherLock(VOID)
Definition: ke_x.h:144
#define InitializeListHead(ListHead)
Definition: env_spec_w32.h:944
volatile ULONG CurrentCount
Definition: ketypes.h:1267
_Out_ PKAPC_STATE ApcState
Definition: mm.h:1415
FORCEINLINE VOID RepairList(IN PLIST_ENTRY Original, IN PLIST_ENTRY Copy, IN KPROCESSOR_MODE Mode)
Definition: apc.c:516
BOOLEAN NTAPI KeInsertQueueApc(IN PKAPC Apc, IN PVOID SystemArgument1, IN PVOID SystemArgument2, IN KPRIORITY PriorityBoost)
Definition: apc.c:735
VOID NTAPI _KeLeaveCriticalRegion(VOID)
Definition: apc.c:599
#define DPRINT1
Definition: precomp.h:8
BOOLEAN NTAPI KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
Definition: thrdobj.c:731
FORCEINLINE VOID KiReleaseDispatcherLockFromDpcLevel(VOID)
Definition: ke_x.h:168
FORCEINLINE VOID KiAcquireApcLockAtApcLevel(IN PKTHREAD Thread, IN PKLOCK_QUEUE_HANDLE Handle)
Definition: ke_x.h:618
_Must_inspect_result_ _In_ PLARGE_INTEGER _In_ PLARGE_INTEGER _In_ ULONG _In_ PFILE_OBJECT _In_ PVOID Process
Definition: fsrtlfuncs.h:219
#define FALSE
Definition: numbers.c:16
volatile UCHAR State
Definition: ketypes.h:997
struct tagContext Context
Definition: acpixf.h:1013
#define CONTAINING_RECORD(address, type, field)
Definition: typedefs.h:260
BOOLEAN Inserted
Definition: ketypes.h:540
UCHAR ApcStateIndex
Definition: ketypes.h:1198
ULONG ApcQueueable
Definition: ketypes.h:1084
KPROCESSOR_MODE WaitMode
Definition: ketypes.h:1000
VOID NTAPI PsExitSpecialApc(PKAPC Apc, PKNORMAL_ROUTINE *NormalRoutine, PVOID *NormalContext, PVOID *SystemArgument1, PVOID *SystemArgument2)
#define KeGetCurrentThread
Definition: hal.h:44
PKAPC_STATE ApcStatePointer[2]
Definition: ketypes.h:1241
VOID NTAPI KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame)
Definition: apc.c:302
#define APC_LEVEL
Definition: env_spec_w32.h:695
IN HDEVINFO IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
Definition: devinst.c:44
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:90
* PKAPC_STATE
Definition: ketypes.h:1258
PVOID SystemArgument2
Definition: ketypes.h:537
FORCEINLINE VOID KiAcquireDispatcherLockAtDpcLevel(VOID)
Definition: ke_x.h:160
KIRQL WaitIrql
Definition: ketypes.h:999
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:642