2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/include/ke_x.h
5 * PURPOSE: Internal Inlined Functions for the Kernel
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
10 // Enters a Guarded Region
12 #define KeEnterGuardedRegion() \
14 PKTHREAD Thread = KeGetCurrentThread(); \
17 ASSERT_IRQL_LESS_OR_EQUAL(APC_LEVEL); \
18 ASSERT(Thread == KeGetCurrentThread()); \
19 ASSERT((Thread->SpecialApcDisable <= 0) && \
20 (Thread->SpecialApcDisable != -32768)); \
22 /* Disable Special APCs */ \
23 Thread->SpecialApcDisable--; \
27 // Leaves a Guarded Region
29 #define KeLeaveGuardedRegion() \
31 PKTHREAD Thread = KeGetCurrentThread(); \
34 ASSERT_IRQL_LESS_OR_EQUAL(APC_LEVEL); \
35 ASSERT(Thread == KeGetCurrentThread()); \
36 ASSERT(Thread->SpecialApcDisable < 0); \
38 /* Leave region and check if APCs are OK now */ \
39 if (!(++Thread->SpecialApcDisable)) \
41 /* Check for Kernel APCs on the list */ \
42 if (!IsListEmpty(&Thread->ApcState. \
43 ApcListHead[KernelMode])) \
45 /* Check for APC Delivery */ \
46 KiCheckForKernelApcDelivery(); \
52 // TODO: Guarded Mutex Routines
56 // Enters a Critical Region
58 #define KeEnterCriticalRegion() \
60 PKTHREAD Thread = KeGetCurrentThread(); \
64 ASSERT(Thread == KeGetCurrentThread()); \
65 ASSERT((Thread->KernelApcDisable <= 0) && \
66 (Thread->KernelApcDisable != -32768)); \
68 /* Disable Kernel APCs */ \
69 Thread->KernelApcDisable--; \
74 // Leaves a Critical Region
76 #define KeLeaveCriticalRegion() \
78 PKTHREAD Thread = KeGetCurrentThread(); \
82 ASSERT(Thread == KeGetCurrentThread()); \
83 ASSERT(Thread->KernelApcDisable < 0); \
85 /* Enable Kernel APCs */ \
86 Thread->KernelApcDisable++; \
88 /* Check if Kernel APCs are now enabled */ \
89 if (!(Thread->KernelApcDisable)) \
91 /* Check if we need to request an APC Delivery */ \
92 if (!(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) && \
93 !(Thread->KernelApcDisable)) \
95 /* Check for the right environment */ \
96 KiCheckForKernelApcDelivery(); \
103 // Satisfies the wait of any dispatcher object
105 #define KiSatisfyObjectWait(Object, Thread) \
107 /* Special case for Mutants */ \
108 if ((Object)->Header.Type == MutantObject) \
110 /* Decrease the Signal State */ \
111 (Object)->Header.SignalState--; \
113 /* Check if it's now non-signaled */ \
114 if (!(Object)->Header.SignalState) \
116 /* Set the Owner Thread */ \
117 (Object)->OwnerThread = Thread; \
119 /* Disable APCs if needed */ \
120 Thread->KernelApcDisable -= (Object)->ApcDisable; \
122 /* Check if it's abandoned */ \
123 if ((Object)->Abandoned) \
126 (Object)->Abandoned = FALSE; \
128 /* Return Status */ \
129 Thread->WaitStatus = STATUS_ABANDONED; \
132 /* Insert it into the Mutant List */ \
133 InsertHeadList(Thread->MutantListHead.Blink, \
134 &(Object)->MutantListEntry); \
137 else if (((Object)->Header.Type & TIMER_OR_EVENT_TYPE) == \
138 EventSynchronizationObject) \
140 /* Synchronization Timers and Events just get un-signaled */ \
141 (Object)->Header.SignalState = 0; \
143 else if ((Object)->Header.Type == SemaphoreObject) \
145 /* These ones can have multiple states, so we only decrease it */ \
146 (Object)->Header.SignalState--; \
151 // Satisfies the wait of a mutant dispatcher object
153 #define KiSatisfyMutantWait(Object, Thread) \
155 /* Decrease the Signal State */ \
156 (Object)->Header.SignalState--; \
158 /* Check if it's now non-signaled */ \
159 if (!(Object)->Header.SignalState) \
161 /* Set the Owner Thread */ \
162 (Object)->OwnerThread = Thread; \
164 /* Disable APCs if needed */ \
165 Thread->KernelApcDisable -= (Object)->ApcDisable; \
167 /* Check if it's abandoned */ \
168 if ((Object)->Abandoned) \
171 (Object)->Abandoned = FALSE; \
173 /* Return Status */ \
174 Thread->WaitStatus = STATUS_ABANDONED; \
177 /* Insert it into the Mutant List */ \
178 InsertHeadList(Thread->MutantListHead.Blink, \
179 &(Object)->MutantListEntry); \
184 // Satisfies the wait of any nonmutant dispatcher object
186 #define KiSatisfyNonMutantWait(Object, Thread) \
188 if (((Object)->Header.Type & TIMER_OR_EVENT_TYPE) == \
189 EventSynchronizationObject) \
191 /* Synchronization Timers and Events just get un-signaled */ \
192 (Object)->Header.SignalState = 0; \
194 else if ((Object)->Header.Type == SemaphoreObject) \
196 /* These ones can have multiple states, so we only decrease it */ \
197 (Object)->Header.SignalState--; \
202 // Recalculates the due time
206 KiRecalculateDueTime(IN PLARGE_INTEGER OriginalDueTime
,
207 IN PLARGE_INTEGER DueTime
,
208 IN OUT PLARGE_INTEGER NewDueTime
)
210 /* Don't do anything for absolute waits */
211 if (OriginalDueTime
->QuadPart
>= 0) return OriginalDueTime
;
213 /* Otherwise, query the interrupt time and recalculate */
214 NewDueTime
->QuadPart
= KeQueryInterruptTime();
215 NewDueTime
->QuadPart
-= DueTime
->QuadPart
;
220 // Determines wether a thread should be added to the wait list
222 #define KiCheckThreadStackSwap(WaitMode, Thread, Swappable) \
224 /* Check the required conditions */ \
225 if ((WaitMode != KernelMode) && \
226 (Thread->EnableStackSwap) && \
227 (Thread->Priority >= (LOW_REALTIME_PRIORITY + 9))) \
229 /* We are go for swap */ \
234 /* Don't swap the thread */ \
240 // Adds a thread to the wait list
242 #define KiAddThreadToWaitList(Thread, Swappable) \
244 /* Make sure it's swappable */ \
247 /* Insert it into the PRCB's List */ \
248 InsertTailList(&KeGetCurrentPrcb()->WaitListHead, \
249 &Thread->WaitListEntry); \
254 // Rules for checking alertability:
255 // - For Alertable waits ONLY:
256 // * We don't wait and return STATUS_ALERTED if the thread is alerted
257 // in EITHER the specified wait mode OR in Kernel Mode.
258 // - For BOTH Alertable AND Non-Alertable waits:
259 // * We don't want and return STATUS_USER_APC if the User Mode APC list
260 // is not empty AND the wait mode is User Mode.
262 #define KiCheckAlertability() \
266 if (CurrentThread->Alerted[(int)WaitMode]) \
268 CurrentThread->Alerted[(int)WaitMode] = FALSE; \
269 WaitStatus = STATUS_ALERTED; \
272 else if ((WaitMode != KernelMode) && \
273 (!IsListEmpty(&CurrentThread-> \
274 ApcState.ApcListHead[UserMode]))) \
276 CurrentThread->ApcState.UserApcPending = TRUE; \
277 WaitStatus = STATUS_USER_APC; \
280 else if (CurrentThread->Alerted[KernelMode]) \
282 CurrentThread->Alerted[KernelMode] = FALSE; \
283 WaitStatus = STATUS_ALERTED; \
287 else if ((WaitMode != KernelMode) && \
288 (CurrentThread->ApcState.UserApcPending)) \
290 WaitStatus = STATUS_USER_APC; \
296 // Thread Scheduling Routines
301 KiAcquireDispatcherLock(VOID
)
303 /* Raise to DPC level */
304 return KeRaiseIrqlToDpcLevel();
309 KiReleaseDispatcherLock(IN KIRQL OldIrql
)
311 /* Just exit the dispatcher */
312 KiExitDispatcher(OldIrql
);
317 KiAcquireDispatcherLockAtDpcLevel(VOID
)
319 /* This is a no-op at DPC Level for UP systems */
325 KiReleaseDispatcherLockFromDpcLevel(VOID
)
327 /* This is a no-op at DPC Level for UP systems */
332 // This routine makes the thread deferred ready on the boot CPU.
336 KiInsertDeferredReadyList(IN PKTHREAD Thread
)
338 /* Set the thread to deferred state and boot CPU */
339 Thread
->State
= DeferredReady
;
340 Thread
->DeferredProcessor
= 0;
342 /* Make the thread ready immediately */
343 KiDeferredReadyThread(Thread
);
348 KiRescheduleThread(IN BOOLEAN NewThread
,
351 /* This is meaningless on UP systems */
352 UNREFERENCED_PARAMETER(NewThread
);
353 UNREFERENCED_PARAMETER(Cpu
);
357 // This routine protects against multiple CPU acquires, it's meaningless on UP.
361 KiSetThreadSwapBusy(IN PKTHREAD Thread
)
363 UNREFERENCED_PARAMETER(Thread
);
367 // This routine protects against multiple CPU acquires, it's meaningless on UP.
371 KiAcquirePrcbLock(IN PKPRCB Prcb
)
373 UNREFERENCED_PARAMETER(Prcb
);
377 // This routine protects against multiple CPU acquires, it's meaningless on UP.
381 KiReleasePrcbLock(IN PKPRCB Prcb
)
383 UNREFERENCED_PARAMETER(Prcb
);
387 // This routine protects against multiple CPU acquires, it's meaningless on UP.
391 KiAcquireThreadLock(IN PKTHREAD Thread
)
393 UNREFERENCED_PARAMETER(Thread
);
397 // This routine protects against multiple CPU acquires, it's meaningless on UP.
401 KiReleaseThreadLock(IN PKTHREAD Thread
)
403 UNREFERENCED_PARAMETER(Thread
);
408 KiCheckDeferredReadyList(IN PKPRCB Prcb
)
410 /* There are no deferred ready lists on UP systems */
411 UNREFERENCED_PARAMETER(Prcb
);
416 KiRundownThread(IN PKTHREAD Thread
)
418 /* Check if this is the NPX Thread */
419 if (KeGetCurrentPrcb()->NpxThread
== Thread
)
422 KeGetCurrentPrcb()->NpxThread
= NULL
;
424 __asm__("fninit\n\t");
433 KiRequestApcInterrupt(IN UCHAR Processor
)
435 /* Request a software interrupt */
436 HalRequestSoftwareInterrupt(APC_LEVEL
);
443 KiAcquireDispatcherLock(VOID
)
445 /* Raise to synchronization level and acquire the dispatcher lock */
446 return KeAcquireQueuedSpinLockRaiseToSynch(LockQueueDispatcherLock
);
451 KiReleaseDispatcherLock(IN KIRQL OldIrql
)
453 /* First release the lock */
454 KeReleaseQueuedSpinLockFromDpcLevel(&KeGetCurrentPrcb()->
455 LockQueue
[LockQueueDispatcherLock
]);
457 /* Then exit the dispatcher */
458 KiExitDispatcher(OldIrql
);
462 // This routine inserts a thread into the deferred ready list of the given CPU
466 KiInsertDeferredReadyList(IN PKTHREAD Thread
)
468 PKPRCB Prcb
= KeGetCurrentPrcb();
470 /* Set the thread to deferred state and CPU */
471 Thread
->State
= DeferredReady
;
472 Thread
->DeferredProcessor
= Prcb
->Number
;
474 /* Add it on the list */
475 PushEntryList(&Prcb
->DeferredReadyListHead
, &Thread
->SwapListEntry
);
480 KiRescheduleThread(IN BOOLEAN NewThread
,
483 /* Check if a new thread needs to be scheduled on a different CPU */
484 if ((NewThread
) && !(KeGetPcr()->Number
== Cpu
))
486 /* Send an IPI to request delivery */
487 KiIpiSendRequest(AFFINITY_MASK(Cpu
), IPI_DPC
);
492 // This routine sets the current thread in a swap busy state, which ensure that
493 // nobody else tries to swap it concurrently.
497 KiSetThreadSwapBusy(IN PKTHREAD Thread
)
499 /* Make sure nobody already set it */
500 ASSERT(Thread
->SwapBusy
== FALSE
);
502 /* Set it ourselves */
503 Thread
->SwapBusy
= TRUE
;
507 // This routine acquires the PRCB lock so that only one caller can touch
508 // volatile PRCB data.
510 // Since this is a simple optimized spin-lock, it must be be only acquired
511 // at dispatcher level or higher!
515 KiAcquirePrcbLock(IN PKPRCB Prcb
)
517 /* Make sure we're at a safe level to touch the PRCB lock */
518 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
520 /* Start acquire loop */
523 /* Acquire the lock and break out if we acquired it first */
524 if (!InterlockedExchange(&Prcb
->PrcbLock
, 1)) break;
526 /* Loop until the other CPU releases it */
529 /* Let the CPU know that this is a loop */
531 } while (Prcb
->PrcbLock
);
536 // This routine releases the PRCB lock so that other callers can touch
537 // volatile PRCB data.
539 // Since this is a simple optimized spin-lock, it must be be only acquired
540 // at dispatcher level or higher!
544 KiReleasePrcbLock(IN PKPRCB Prcb
)
546 /* Make sure it's acquired! */
547 ASSERT(Prcb
->PrcbLock
!= 0);
550 InterlockedAnd(&Prcb
->PrcbLock
, 0);
554 // This routine acquires the thread lock so that only one caller can touch
555 // volatile thread data.
557 // Since this is a simple optimized spin-lock, it must be be only acquired
558 // at dispatcher level or higher!
562 KiAcquireThreadLock(IN PKTHREAD Thread
)
564 /* Make sure we're at a safe level to touch the thread lock */
565 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
567 /* Start acquire loop */
570 /* Acquire the lock and break out if we acquired it first */
571 if (!InterlockedExchange(&Thread
->ThreadLock
, 1)) break;
573 /* Loop until the other CPU releases it */
576 /* Let the CPU know that this is a loop */
578 } while (Thread
->ThreadLock
);
583 // This routine releases the thread lock so that other callers can touch
584 // volatile thread data.
586 // Since this is a simple optimized spin-lock, it must be be only acquired
587 // at dispatcher level or higher!
591 KiReleaseThreadLock(IN PKTHREAD Thread
)
594 InterlockedAnd(&Thread
->ThreadLock
, 0);
599 KiCheckDeferredReadyList(IN PKPRCB Prcb
)
601 /* Scan the deferred ready lists if required */
602 if (Prcb
->DeferredReadyListHead
.Next
) KiProcessDeferredReadyList(Prcb
);
607 KiRequestApcInterrupt(IN UCHAR Processor
)
609 /* Check if we're on the same CPU */
610 if (KeGetCurrentPrcb()->Number
== Processor
)
612 /* Request a software interrupt */
613 HalRequestSoftwareInterrupt(APC_LEVEL
);
617 KiIpiSendRequest(KeGetCurrentPrcb()->SetMember
, IPI_APC
);
625 KiAcquireApcLock(IN PKTHREAD Thread
,
626 IN PKLOCK_QUEUE_HANDLE Handle
)
628 /* Acquire the lock and raise to synchronization level */
629 KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread
->ApcQueueLock
, Handle
);
634 KiAcquireApcLockAtDpcLevel(IN PKTHREAD Thread
,
635 IN PKLOCK_QUEUE_HANDLE Handle
)
637 /* Acquire the lock */
638 KeAcquireInStackQueuedSpinLockAtDpcLevel(&Thread
->ApcQueueLock
, Handle
);
643 KiReleaseApcLock(IN PKLOCK_QUEUE_HANDLE Handle
)
645 /* Release the lock */
646 KeReleaseInStackQueuedSpinLock(Handle
);
651 KiReleaseApcLockFromDpcLevel(IN PKLOCK_QUEUE_HANDLE Handle
)
653 /* Release the lock */
654 KeReleaseInStackQueuedSpinLockFromDpcLevel(Handle
);
659 KiAcquireProcessLock(IN PKPROCESS Process
,
660 IN PKLOCK_QUEUE_HANDLE Handle
)
662 /* Acquire the lock and raise to synchronization level */
663 KeAcquireInStackQueuedSpinLockRaiseToSynch(&Process
->ProcessLock
, Handle
);
668 KiReleaseProcessLock(IN PKLOCK_QUEUE_HANDLE Handle
)
670 /* Release the lock */
671 KeReleaseInStackQueuedSpinLock(Handle
);
675 // This routine queues a thread that is ready on the PRCB's ready lists.
676 // If this thread cannot currently run on this CPU, then the thread is
677 // added to the deferred ready list instead.
679 // This routine must be entered with the PRCB lock held and it will exit
680 // with the PRCB lock released!
684 KxQueueReadyThread(IN PKTHREAD Thread
,
691 ASSERT(Prcb
== KeGetCurrentPrcb());
692 ASSERT(Thread
->State
== Running
);
693 ASSERT(Thread
->NextProcessor
== Prcb
->Number
);
695 /* Check if this thread is allowed to run in this CPU */
697 if ((Thread
->Affinity
) & (Prcb
->SetMember
))
702 /* Set thread ready for execution */
703 Thread
->State
= Ready
;
705 /* Save current priority and if someone had pre-empted it */
706 Priority
= Thread
->Priority
;
707 Preempted
= Thread
->Preempted
;
709 /* We're not pre-empting now, and set the wait time */
710 Thread
->Preempted
= FALSE
;
711 Thread
->WaitTime
= KeTickCount
.LowPart
;
714 ASSERT((Priority
>= 0) && (Priority
<= HIGH_PRIORITY
));
716 /* Insert this thread in the appropriate order */
717 Preempted
? InsertHeadList(&Prcb
->DispatcherReadyListHead
[Priority
],
718 &Thread
->WaitListEntry
) :
719 InsertTailList(&Prcb
->DispatcherReadyListHead
[Priority
],
720 &Thread
->WaitListEntry
);
722 /* Update the ready summary */
723 Prcb
->ReadySummary
|= PRIORITY_MASK(Priority
);
726 ASSERT(Priority
== Thread
->Priority
);
728 /* Release the PRCB lock */
729 KiReleasePrcbLock(Prcb
);
733 /* Otherwise, prepare this thread to be deferred */
734 Thread
->State
= DeferredReady
;
735 Thread
->DeferredProcessor
= Prcb
->Number
;
737 /* Release the lock and defer scheduling */
738 KiReleasePrcbLock(Prcb
);
739 KiDeferredReadyThread(Thread
);
744 // This routine scans for an appropriate ready thread to select at the
745 // given priority and for the given CPU.
749 KiSelectReadyThread(IN KPRIORITY Priority
,
752 LONG PriorityMask
, PrioritySet
, HighPriority
;
753 PLIST_ENTRY ListEntry
;
756 /* Save the current mask and get the priority set for the CPU */
757 PriorityMask
= Priority
;
758 PrioritySet
= Prcb
->ReadySummary
>> (UCHAR
)Priority
;
759 if (!PrioritySet
) return NULL
;
761 /* Get the highest priority possible */
762 BitScanReverse((PULONG
)&HighPriority
, PrioritySet
);
763 ASSERT((PrioritySet
& PRIORITY_MASK(HighPriority
)) != 0);
764 HighPriority
+= PriorityMask
;
766 /* Make sure the list isn't at highest priority */
767 ASSERT(IsListEmpty(&Prcb
->DispatcherReadyListHead
[HighPriority
]) == FALSE
);
769 /* Get the first thread on the list */
770 ListEntry
= &Prcb
->DispatcherReadyListHead
[HighPriority
];
771 Thread
= CONTAINING_RECORD(ListEntry
, KTHREAD
, WaitListEntry
);
773 /* Make sure this thread is here for a reason */
774 ASSERT(HighPriority
== Thread
->Priority
);
775 ASSERT(Thread
->Affinity
& AFFINITY_MASK(Prcb
->Number
));
776 ASSERT(Thread
->NextProcessor
== Prcb
->Number
);
778 /* Remove it from the list */
779 RemoveEntryList(&Thread
->WaitListEntry
);
780 if (IsListEmpty(&Thread
->WaitListEntry
))
782 /* The list is empty now, reset the ready summary */
783 Prcb
->ReadySummary
^= PRIORITY_MASK(HighPriority
);
786 /* Sanity check and return the thread */
787 ASSERT((Thread
== NULL
) ||
788 (Thread
->BasePriority
== 0) ||
789 (Thread
->Priority
!= 0));
794 // This routine computes the new priority for a thread. It is only valid for
795 // threads with priorities in the dynamic priority range.
799 KiComputeNewPriority(IN PKTHREAD Thread
)
803 /* Priority sanity checks */
804 ASSERT((Thread
->PriorityDecrement
>= 0) &&
805 (Thread
->PriorityDecrement
<= Thread
->Priority
));
806 ASSERT((Thread
->Priority
< LOW_REALTIME_PRIORITY
) ?
807 TRUE
: (Thread
->PriorityDecrement
== 0));
809 /* Get the current priority */
810 Priority
= Thread
->Priority
;
811 if (Priority
< LOW_REALTIME_PRIORITY
)
813 /* Set the New Priority and add the Priority Decrement */
814 Priority
+= (Priority
- Thread
->PriorityDecrement
- 1);
816 /* Don't go out of bounds */
817 if (Priority
< Thread
->BasePriority
) Priority
= Thread
->BasePriority
;
819 /* Reset the priority decrement */
820 Thread
->PriorityDecrement
= 0;
824 ASSERT((Thread
->BasePriority
== 0) || (Priority
!= 0));
826 /* Return the new priority */