2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/kthread.c
5 * PURPOSE: Microkernel thread support
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
11 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
18 #define MAXIMUM_SUSPEND_COUNT 0x7F
19 #define THREAD_ALERT_INCREMENT 2
21 extern EX_WORK_QUEUE ExWorkerQueue
[MaximumWorkQueue
];
24 * PURPOSE: List of threads associated with each priority level
26 LIST_ENTRY PriorityListHead
[MAXIMUM_PRIORITY
];
27 static ULONG PriorityListMask
= 0;
28 ULONG IdleProcessorMask
= 0;
29 extern PETHREAD PspReaperList
;
31 /* FUNCTIONS *****************************************************************/
35 KiRequestReschedule(CCHAR Processor
)
39 Pcr
= (PKPCR
)(KPCR_BASE
+ Processor
* PAGE_SIZE
);
40 Pcr
->Prcb
->QuantumEnd
= TRUE
;
41 KiIpiSendRequest(1 << Processor
, IPI_DPC
);
46 KiInsertIntoThreadList(KPRIORITY Priority
,
49 ASSERT(Ready
== Thread
->State
);
50 ASSERT(Thread
->Priority
== Priority
);
52 if (Priority
>= MAXIMUM_PRIORITY
|| Priority
< LOW_PRIORITY
) {
54 DPRINT1("Invalid thread priority (%d)\n", Priority
);
58 InsertTailList(&PriorityListHead
[Priority
], &Thread
->WaitListEntry
);
59 PriorityListMask
|= (1 << Priority
);
64 KiRemoveFromThreadList(PKTHREAD Thread
)
66 ASSERT(Ready
== Thread
->State
);
67 RemoveEntryList(&Thread
->WaitListEntry
);
68 if (IsListEmpty(&PriorityListHead
[(ULONG
)Thread
->Priority
])) {
70 PriorityListMask
&= ~(1 << Thread
->Priority
);
76 KiScanThreadList(KPRIORITY Priority
,
82 Mask
= (1 << Priority
);
84 if (PriorityListMask
& Mask
) {
86 LIST_FOR_EACH(current
, &PriorityListHead
[Priority
], KTHREAD
, WaitListEntry
) {
88 if (current
->State
!= Ready
) {
90 DPRINT1("%d/%d\n", ¤t
, current
->State
);
93 ASSERT(current
->State
== Ready
);
95 if (current
->Affinity
& Affinity
) {
97 KiRemoveFromThreadList(current
);
108 KiDispatchThreadNoLock(ULONG NewThreadStatus
)
110 KPRIORITY CurrentPriority
;
113 PKTHREAD CurrentThread
= KeGetCurrentThread();
115 DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
116 CurrentThread
, NewThreadStatus
, CurrentThread
->State
);
118 CurrentThread
->State
= (UCHAR
)NewThreadStatus
;
120 if (NewThreadStatus
== Ready
) {
122 KiInsertIntoThreadList(CurrentThread
->Priority
,
126 Affinity
= 1 << KeGetCurrentProcessorNumber();
128 for (CurrentPriority
= HIGH_PRIORITY
; CurrentPriority
>= LOW_PRIORITY
; CurrentPriority
--) {
130 Candidate
= KiScanThreadList(CurrentPriority
, Affinity
);
132 if (Candidate
== CurrentThread
) {
134 Candidate
->State
= Running
;
135 KeReleaseDispatcherDatabaseLockFromDpcLevel();
139 if (Candidate
!= NULL
) {
144 DPRINT("Scheduling %x(%d)\n",Candidate
, CurrentPriority
);
146 Candidate
->State
= Running
;
148 OldThread
= CurrentThread
;
149 CurrentThread
= Candidate
;
150 IdleThread
= KeGetCurrentPrcb()->IdleThread
;
152 if (OldThread
== IdleThread
) {
154 IdleProcessorMask
&= ~Affinity
;
156 } else if (CurrentThread
== IdleThread
) {
158 IdleProcessorMask
|= Affinity
;
161 MmUpdatePageDir(PsGetCurrentProcess(),((PETHREAD
)CurrentThread
)->ThreadsProcess
, sizeof(EPROCESS
));
163 /* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */
164 DPRINT("You are : %x, swapping to: %x\n", OldThread
, CurrentThread
);
165 KiArchContextSwitch(CurrentThread
);
166 DPRINT("You are : %x, swapped from: %x\n", OldThread
, CurrentThread
);
171 DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber());
179 PKTHREAD CurrentThread
= KeGetCurrentThread();
181 /* Find a new thread to run */
182 DPRINT("Dispatching Thread as blocked\n");
183 KiDispatchThreadNoLock(Waiting
);
185 /* Lower IRQL back */
186 DPRINT("Lowering IRQL \n");
187 KfLowerIrql(CurrentThread
->WaitIrql
);
189 /* Return the wait status */
190 return CurrentThread
->WaitStatus
;
195 KiDispatchThread(ULONG NewThreadStatus
)
199 if (KeGetCurrentPrcb()->IdleThread
== NULL
) {
203 OldIrql
= KeAcquireDispatcherDatabaseLock();
204 KiDispatchThreadNoLock(NewThreadStatus
);
205 KeLowerIrql(OldIrql
);
210 KiUnblockThread(PKTHREAD Thread
,
211 PNTSTATUS WaitStatus
,
214 if (Terminated
== Thread
->State
) {
216 DPRINT("Can't unblock thread 0x%x because it's terminating\n",
219 } else if (Ready
== Thread
->State
||
220 Running
== Thread
->State
) {
222 DPRINT("Can't unblock thread 0x%x because it's %s\n",
223 Thread
, (Thread
->State
== Ready
? "ready" : "running"));
230 /* FIXME: This propably isn't the right way to do it... */
231 /* No it's not... i'll fix it later-- Alex */
232 if (Thread
->Priority
< LOW_REALTIME_PRIORITY
&&
233 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2) {
235 if (!Thread
->PriorityDecrement
&& !Thread
->DisableBoost
) {
237 Thread
->Priority
= Thread
->BasePriority
+ Increment
;
238 Thread
->PriorityDecrement
= Increment
;
241 /* Also decrease quantum */
246 Thread
->Quantum
= Thread
->QuantumReset
;
249 if (WaitStatus
!= NULL
) {
251 Thread
->WaitStatus
= *WaitStatus
;
254 Thread
->State
= Ready
;
255 KiInsertIntoThreadList(Thread
->Priority
, Thread
);
256 Processor
= KeGetCurrentProcessorNumber();
257 Affinity
= Thread
->Affinity
;
259 if (!(IdleProcessorMask
& (1 << Processor
) & Affinity
) &&
260 (IdleProcessorMask
& ~(1 << Processor
) & Affinity
)) {
264 for (i
= 0; i
< KeNumberProcessors
- 1; i
++) {
268 if (Processor
>= KeNumberProcessors
) {
273 if (IdleProcessorMask
& (1 << Processor
) & Affinity
) {
276 * Reschedule the threads on an other processor
278 KeReleaseDispatcherDatabaseLockFromDpcLevel();
279 KiRequestReschedule(Processor
);
280 KeAcquireDispatcherDatabaseLockAtDpcLevel();
291 KiAdjustQuantumThread(IN PKTHREAD Thread
)
295 /* Don't adjust for RT threads */
296 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
297 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2)
299 /* Decrease Quantum by one and see if we've ran out */
300 if (--Thread
->Quantum
<= 0)
303 Thread
->Quantum
= Thread
->QuantumReset
;
305 /* Calculate new Priority */
306 Priority
= Thread
->Priority
- (Thread
->PriorityDecrement
+ 1);
308 /* Normalize it if we've gone too low */
309 if (Priority
< Thread
->BasePriority
) Priority
= Thread
->BasePriority
;
311 /* Reset the priority decrement, we've done it */
312 Thread
->PriorityDecrement
= 0;
314 /* Set the new priority, if needed */
315 if (Priority
!= Thread
->Priority
)
318 * FIXME: This should be a call to KiSetPriorityThread but
319 * due to the current ""scheduler"" in ROS, it can't be done
320 * cleanly since it actualyl dispatches threads instead.
322 Thread
->Priority
= Priority
;
326 /* FIXME: Priority hasn't changed, find a new thread */
331 /* Nothing to do... */
338 KiSuspendThreadKernelRoutine(PKAPC Apc
,
339 PKNORMAL_ROUTINE
* NormalRoutine
,
340 PVOID
* NormalContext
,
341 PVOID
* SystemArgument1
,
342 PVOID
* SystemArguemnt2
)
348 KiSuspendThreadNormalRoutine(PVOID NormalContext
,
349 PVOID SystemArgument1
,
350 PVOID SystemArgument2
)
352 PKTHREAD CurrentThread
= KeGetCurrentThread();
354 /* Non-alertable kernel-mode suspended wait */
355 DPRINT("Waiting...\n");
356 KeWaitForSingleObject(&CurrentThread
->SuspendSemaphore
,
361 DPRINT("Done Waiting\n");
364 #ifdef KeGetCurrentThread
365 #undef KeGetCurrentThread
372 KeGetCurrentThread(VOID
)
377 Ke386SaveFlags(Flags
);
378 Ke386DisableInterrupts();
379 Thread
= KeGetCurrentPrcb()->CurrentThread
;
380 Ke386RestoreFlags(Flags
);
383 return(KeGetCurrentPrcb()->CurrentThread
);
389 KeSetPreviousMode(ULONG Mode
)
391 PsGetCurrentThread()->Tcb
.PreviousMode
= (UCHAR
)Mode
;
399 KeGetPreviousMode(VOID
)
401 return (ULONG
)PsGetCurrentThread()->Tcb
.PreviousMode
;
406 KeDisableThreadApcQueueing(IN PKTHREAD Thread
)
409 BOOLEAN PreviousState
;
411 /* Lock the Dispatcher Database */
412 OldIrql
= KeAcquireDispatcherDatabaseLock();
415 PreviousState
= Thread
->ApcQueueable
;
418 Thread
->ApcQueueable
= FALSE
;
420 /* Release the Lock */
421 KeReleaseDispatcherDatabaseLock(OldIrql
);
423 /* Return old state */
424 return PreviousState
;
429 KeRundownThread(VOID
)
432 PKTHREAD Thread
= KeGetCurrentThread();
433 PLIST_ENTRY NextEntry
, ListHead
;
435 DPRINT("KeRundownThread: %x\n", Thread
);
437 /* Optimized path if nothing is on the list at the moment */
438 if (IsListEmpty(&Thread
->MutantListHead
)) return;
440 /* Lock the Dispatcher Database */
441 OldIrql
= KeAcquireDispatcherDatabaseLock();
443 /* Get the List Pointers */
444 ListHead
= &Thread
->MutantListHead
;
445 NextEntry
= ListHead
->Flink
;
446 while (NextEntry
!= ListHead
)
449 Mutant
= CONTAINING_RECORD(NextEntry
, KMUTANT
, MutantListEntry
);
450 DPRINT1("Mutant: %p. Type, Size %x %x\n",
453 Mutant
->Header
.Size
);
455 /* Make sure it's not terminating with APCs off */
456 if (Mutant
->ApcDisable
)
458 /* Bugcheck the system */
459 KEBUGCHECKEX(0,//THREAD_TERMINATE_HELD_MUTEX,
466 /* Now we can remove it */
467 RemoveEntryList(&Mutant
->MutantListEntry
);
469 /* Unconditionally abandon it */
470 DPRINT("Abandonning the Mutant\n");
471 Mutant
->Header
.SignalState
= 1;
472 Mutant
->Abandoned
= TRUE
;
473 Mutant
->OwnerThread
= NULL
;
475 /* Check if the Wait List isn't empty */
476 DPRINT("Checking whether to wake the Mutant\n");
477 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
))
479 /* Wake the Mutant */
480 DPRINT("Waking the Mutant\n");
481 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
485 NextEntry
= NextEntry
->Flink
;
488 /* Release the Lock */
489 KeReleaseDispatcherDatabaseLock(OldIrql
);
494 KeResumeThread(PKTHREAD Thread
)
499 DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread
,
500 Thread
->SuspendCount
, Thread
->FreezeCount
);
502 /* Lock the Dispatcher */
503 OldIrql
= KeAcquireDispatcherDatabaseLock();
505 /* Save the Old Count */
506 PreviousCount
= Thread
->SuspendCount
;
508 /* Check if it existed */
511 Thread
->SuspendCount
--;
513 /* Decrease the current Suspend Count and Check Freeze Count */
514 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
)) {
516 /* Signal the Suspend Semaphore */
517 Thread
->SuspendSemaphore
.Header
.SignalState
++;
518 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
522 /* Release Lock and return the Old State */
523 KeReleaseDispatcherDatabaseLock(OldIrql
);
524 return PreviousCount
;
529 KiInsertQueueApc(PKAPC Apc
,
530 KPRIORITY PriorityBoost
);
533 * Used by the debugging code to freeze all the process's threads
534 * while the debugger is examining their state.
538 KeFreezeAllThreads(PKPROCESS Process
)
542 PKTHREAD CurrentThread
= KeGetCurrentThread();
545 OldIrql
= KeAcquireDispatcherDatabaseLock();
547 /* If someone is already trying to free us, try again */
548 while (CurrentThread
->FreezeCount
)
550 /* Release and re-acquire the lock so the APC will go through */
551 KeReleaseDispatcherDatabaseLock(OldIrql
);
552 OldIrql
= KeAcquireDispatcherDatabaseLock();
555 /* Enter a critical region */
556 KeEnterCriticalRegion();
558 /* Loop the Process's Threads */
559 LIST_FOR_EACH(Current
, &Process
->ThreadListHead
, KTHREAD
, ThreadListEntry
)
561 /* Make sure it's not ours */
562 if (Current
!= CurrentThread
)
564 /* Should be bother inserting the APC? */
565 if (Current
->ApcQueueable
)
567 /* Make sure it wasn't already frozen, and that it's not suspended */
568 if (!(++Current
->FreezeCount
) && !(Current
->SuspendCount
))
570 /* Did we already insert it? */
571 if (!Current
->SuspendApc
.Inserted
)
574 Current
->SuspendApc
.Inserted
= TRUE
;
575 KiInsertQueueApc(&Current
->SuspendApc
, IO_NO_INCREMENT
);
579 /* Unsignal the Semaphore, the APC already got inserted */
580 Current
->SuspendSemaphore
.Header
.SignalState
--;
587 /* Release the lock */
588 KeReleaseDispatcherDatabaseLock(OldIrql
);
593 KeSuspendThread(PKTHREAD Thread
)
598 DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread
, Thread
->SuspendCount
, Thread
->FreezeCount
);
600 /* Lock the Dispatcher */
601 OldIrql
= KeAcquireDispatcherDatabaseLock();
603 /* Save the Old Count */
604 PreviousCount
= Thread
->SuspendCount
;
606 /* Handle the maximum */
607 if (PreviousCount
== MAXIMUM_SUSPEND_COUNT
)
609 /* Raise an exception */
610 KeReleaseDispatcherDatabaseLock(OldIrql
);
611 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED
);
614 /* Should we bother to queue at all? */
615 if (Thread
->ApcQueueable
)
617 /* Increment the suspend count */
618 Thread
->SuspendCount
++;
620 /* Check if we should suspend it */
621 if (!PreviousCount
&& !Thread
->FreezeCount
)
623 /* Is the APC already inserted? */
624 if (!Thread
->SuspendApc
.Inserted
)
626 /* Not inserted, insert it */
627 Thread
->SuspendApc
.Inserted
= TRUE
;
628 KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
);
632 /* Unsignal the Semaphore, the APC already got inserted */
633 Thread
->SuspendSemaphore
.Header
.SignalState
--;
638 /* Release Lock and return the Old State */
639 KeReleaseDispatcherDatabaseLock(OldIrql
);
640 return PreviousCount
;
645 KeForceResumeThread(IN PKTHREAD Thread
)
650 /* Lock the Dispatcher Database and the APC Queue */
651 OldIrql
= KeAcquireDispatcherDatabaseLock();
653 /* Save the old Suspend Count */
654 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
656 /* If the thread is suspended, wake it up!!! */
659 /* Unwait it completely */
660 Thread
->SuspendCount
= 0;
661 Thread
->FreezeCount
= 0;
663 /* Signal and satisfy */
664 Thread
->SuspendSemaphore
.Header
.SignalState
++;
665 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
668 /* Release Lock and return the Old State */
669 KeReleaseDispatcherDatabaseLock(OldIrql
);
670 return PreviousCount
;
675 KeAlertResumeThread(IN PKTHREAD Thread
)
680 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
682 /* Lock the Dispatcher Database and the APC Queue */
683 OldIrql
= KeAcquireDispatcherDatabaseLock();
684 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
686 /* Return if Thread is already alerted. */
687 if (Thread
->Alerted
[KernelMode
] == FALSE
) {
689 /* If it's Blocked, unblock if it we should */
690 if (Thread
->State
== Waiting
&& Thread
->Alertable
) {
692 DPRINT("Aborting Wait\n");
693 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
697 /* If not, simply Alert it */
698 Thread
->Alerted
[KernelMode
] = TRUE
;
702 /* Save the old Suspend Count */
703 PreviousCount
= Thread
->SuspendCount
;
705 /* If the thread is suspended, decrease one of the suspend counts */
708 /* Decrease count. If we are now zero, unwait it completely */
709 if (--Thread
->SuspendCount
) {
711 /* Signal and satisfy */
712 Thread
->SuspendSemaphore
.Header
.SignalState
++;
713 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
717 /* Release Locks and return the Old State */
718 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
719 KeReleaseDispatcherDatabaseLock(OldIrql
);
720 return PreviousCount
;
725 KeAlertThread(PKTHREAD Thread
,
726 KPROCESSOR_MODE AlertMode
)
729 BOOLEAN PreviousState
;
731 /* Acquire the Dispatcher Database Lock */
732 OldIrql
= KeAcquireDispatcherDatabaseLock();
734 /* Save the Previous State */
735 PreviousState
= Thread
->Alerted
[AlertMode
];
737 /* Return if Thread is already alerted. */
738 if (PreviousState
== FALSE
) {
740 /* If it's Blocked, unblock if it we should */
741 if (Thread
->State
== Waiting
&&
742 (AlertMode
== KernelMode
|| Thread
->WaitMode
== AlertMode
) &&
745 DPRINT("Aborting Wait\n");
746 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
750 /* If not, simply Alert it */
751 Thread
->Alerted
[AlertMode
] = TRUE
;
755 /* Release the Dispatcher Lock */
756 KeReleaseDispatcherDatabaseLock(OldIrql
);
758 /* Return the old state */
759 return PreviousState
;
767 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
773 IN PVOID ThreadState
)
779 * FUNCTION: Initialize the microkernel state of the thread
783 KeInitializeThread(PKPROCESS Process
,
785 PKSYSTEM_ROUTINE SystemRoutine
,
786 PKSTART_ROUTINE StartRoutine
,
792 /* Initalize the Dispatcher Header */
793 DPRINT("Initializing Dispatcher Header for New Thread: %x in Process: %x\n", Thread
, Process
);
794 KeInitializeDispatcherHeader(&Thread
->DispatcherHeader
,
796 sizeof(KTHREAD
) / sizeof(LONG
),
799 DPRINT("Thread Header Created. SystemRoutine: %x, StartRoutine: %x with Context: %x\n",
800 SystemRoutine
, StartRoutine
, StartContext
);
801 DPRINT("UserMode Information. Context: %x, Teb: %x\n", Context
, Teb
);
803 /* Initialize the Mutant List */
804 InitializeListHead(&Thread
->MutantListHead
);
806 /* Setup the Service Descriptor Table for Native Calls */
807 Thread
->ServiceTable
= KeServiceDescriptorTable
;
809 /* Setup APC Fields */
810 InitializeListHead(&Thread
->ApcState
.ApcListHead
[0]);
811 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
812 Thread
->ApcState
.Process
= Process
;
813 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
814 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
815 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
816 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
818 /* Initialize the Suspend APC */
819 KeInitializeApc(&Thread
->SuspendApc
,
821 OriginalApcEnvironment
,
822 KiSuspendThreadKernelRoutine
,
824 KiSuspendThreadNormalRoutine
,
828 /* Initialize the Suspend Semaphore */
829 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 128);
831 /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
833 Thread
->WaitBlock
[3].Object
= (PVOID
)&Thread
->Timer
;
834 Thread
->WaitBlock
[3].Thread
= Thread
;
835 Thread
->WaitBlock
[3].WaitKey
= STATUS_TIMEOUT
;
836 Thread
->WaitBlock
[3].WaitType
= WaitAny
;
837 Thread
->WaitBlock
[3].NextWaitBlock
= NULL
;
838 InsertTailList(&Thread
->Timer
.Header
.WaitListHead
,
839 &Thread
->WaitBlock
[3].WaitListEntry
);
841 KeInitializeTimer(&Thread
->Timer
);
846 /* Set the Thread Stacks */
847 Thread
->InitialStack
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
848 Thread
->StackBase
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
849 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
;
850 Thread
->KernelStackResident
= TRUE
;
853 * Establish the pde's for the new stack and the thread structure within the
854 * address space of the new process. They are accessed while taskswitching or
855 * while handling page faults. At this point it isn't possible to call the
856 * page fault handler for the missing pde's.
858 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
->StackLimit
, MM_STACK_SIZE
);
859 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
861 /* Initalize the Thread Context */
862 DPRINT("Initializing the Context for the thread: %x\n", Thread
);
863 KiArchInitThreadWithContext(Thread
,
869 /* Setup scheduler Fields based on Parent */
870 DPRINT("Thread context created, setting Scheduler Data\n");
871 Thread
->BasePriority
= Process
->BasePriority
;
872 Thread
->Quantum
= Process
->QuantumReset
;
873 Thread
->QuantumReset
= Process
->QuantumReset
;
874 Thread
->Affinity
= Process
->Affinity
;
875 Thread
->Priority
= Process
->BasePriority
;
876 Thread
->UserAffinity
= Process
->Affinity
;
877 Thread
->DisableBoost
= Process
->DisableBoost
;
878 Thread
->AutoAlignment
= Process
->AutoAlignment
;
879 Thread
->Iopl
= Process
->Iopl
;
881 /* Set the Thread to initalized */
882 Thread
->State
= Initialized
;
885 * Insert the Thread into the Process's Thread List
886 * Note, this is the KTHREAD Thread List. It is removed in
887 * ke/kthread.c!KeTerminateThread.
889 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
890 DPRINT("Thread initalized\n");
899 KeQueryPriorityThread (IN PKTHREAD Thread
)
901 return Thread
->Priority
;
909 KeQueryRuntimeThread(IN PKTHREAD Thread
,
912 /* Return the User Time */
913 *UserTime
= Thread
->UserTime
;
915 /* Return the Kernel Time */
916 return Thread
->KernelTime
;
924 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
926 PKTHREAD Thread
= KeGetCurrentThread();
927 BOOLEAN PreviousState
;
930 /* Lock the Dispatcher Database */
931 OldIrql
= KeAcquireDispatcherDatabaseLock();
934 PreviousState
= Thread
->EnableStackSwap
;
937 Thread
->EnableStackSwap
= Enable
;
939 /* No, Release Lock */
940 KeReleaseDispatcherDatabaseLock(OldIrql
);
942 /* Return Old State */
943 return PreviousState
;
951 KeRevertToUserAffinityThread(VOID
)
953 PKTHREAD CurrentThread
= KeGetCurrentThread();
956 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
958 /* Lock the Dispatcher Database */
959 OldIrql
= KeAcquireDispatcherDatabaseLock();
961 /* Return to User Affinity */
962 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
964 /* Disable System Affinity */
965 CurrentThread
->SystemAffinityActive
= FALSE
;
967 /* Check if we need to Dispatch a New thread */
968 if (CurrentThread
->Affinity
& (1 << KeGetCurrentProcessorNumber())) {
970 /* No, just release */
971 KeReleaseDispatcherDatabaseLock(OldIrql
);
975 /* We need to dispatch a new thread */
976 CurrentThread
->WaitIrql
= OldIrql
;
977 KiDispatchThreadNoLock(Ready
);
978 KeLowerIrql(OldIrql
);
987 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
990 CCHAR PreviousIdealProcessor
;
993 /* Lock the Dispatcher Database */
994 OldIrql
= KeAcquireDispatcherDatabaseLock();
996 /* Save Old Ideal Processor */
997 PreviousIdealProcessor
= Thread
->IdealProcessor
;
999 /* Set New Ideal Processor */
1000 Thread
->IdealProcessor
= Processor
;
1003 KeReleaseDispatcherDatabaseLock(OldIrql
);
1005 /* Return Old Ideal Processor */
1006 return PreviousIdealProcessor
;
1014 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
1016 PKTHREAD CurrentThread
= KeGetCurrentThread();
1019 ASSERT(Affinity
& ((1 << KeNumberProcessors
) - 1));
1021 /* Lock the Dispatcher Database */
1022 OldIrql
= KeAcquireDispatcherDatabaseLock();
1024 /* Set the System Affinity Specified */
1025 CurrentThread
->Affinity
= Affinity
;
1027 /* Enable System Affinity */
1028 CurrentThread
->SystemAffinityActive
= TRUE
;
1030 /* Check if we need to Dispatch a New thread */
1031 if (Affinity
& (1 << KeGetCurrentProcessorNumber())) {
1033 /* No, just release */
1034 KeReleaseDispatcherDatabaseLock(OldIrql
);
1038 /* We need to dispatch a new thread */
1039 CurrentThread
->WaitIrql
= OldIrql
;
1040 KiDispatchThreadNoLock(Ready
);
1041 KeLowerIrql(OldIrql
);
1047 KeQueryBasePriorityThread(IN PKTHREAD Thread
)
1049 LONG BasePriorityIncrement
;
1053 /* Lock the Dispatcher Database */
1054 OldIrql
= KeAcquireDispatcherDatabaseLock();
1056 /* Get the Process */
1057 Process
= Thread
->ApcStatePointer
[0]->Process
;
1059 /* Calculate the BPI */
1060 BasePriorityIncrement
= Thread
->BasePriority
- Process
->BasePriority
;
1062 /* If saturation occured, return the SI instead */
1063 if (Thread
->Saturation
) BasePriorityIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1067 KeReleaseDispatcherDatabaseLock(OldIrql
);
1069 /* Return Increment */
1070 return BasePriorityIncrement
;
1075 KiSetPriorityThread(PKTHREAD Thread
,
1079 KPRIORITY OldPriority
= Thread
->Priority
;
1083 DPRINT("Changing prio to : %lx\n", Priority
);
1085 /* Check if priority changed */
1086 if (OldPriority
!= Priority
)
1089 Thread
->Priority
= Priority
;
1091 /* Choose action based on thread's state */
1092 if (Thread
->State
== Ready
)
1094 /* Remove it from the current queue */
1095 KiRemoveFromThreadList(Thread
);
1097 /* Re-insert it at its current priority */
1098 KiInsertIntoThreadList(Priority
, Thread
);
1100 /* Check if the old priority was lower */
1101 if (KeGetCurrentThread()->Priority
< Priority
)
1103 /* Dispatch it immediately */
1104 KiDispatchThreadNoLock(Ready
);
1109 else if (Thread
->State
== Running
)
1111 /* Check if the new priority is lower */
1112 if (Priority
< OldPriority
)
1114 /* Check for threads with a higher priority */
1115 Mask
= ~((1 << (Priority
+ 1)) - 1);
1116 if (PriorityListMask
& Mask
)
1118 /* Found a thread, is it us? */
1119 if (Thread
== KeGetCurrentThread())
1122 KiDispatchThreadNoLock(Ready
);
1128 /* Loop every CPU */
1129 for (i
= 0; i
< KeNumberProcessors
; i
++)
1131 /* Get the PCR for this CPU */
1132 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1134 /* Reschedule if the new one is already on a CPU */
1135 if (Pcr
->Prcb
->CurrentThread
== Thread
)
1137 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1138 KiRequestReschedule(i
);
1149 /* Return to caller */
1158 KeSetBasePriorityThread(PKTHREAD Thread
,
1164 KPRIORITY CurrentBasePriority
;
1165 KPRIORITY BasePriority
;
1166 BOOLEAN Released
= FALSE
;
1167 LONG CurrentIncrement
;
1169 /* Lock the Dispatcher Database */
1170 OldIrql
= KeAcquireDispatcherDatabaseLock();
1172 /* Get the process and calculate current BP and BPI */
1173 Process
= Thread
->ApcStatePointer
[0]->Process
;
1174 CurrentBasePriority
= Thread
->BasePriority
;
1175 CurrentIncrement
= CurrentBasePriority
- Process
->BasePriority
;
1177 /* Change to use the SI if Saturation was used */
1178 if (Thread
->Saturation
) CurrentIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1181 /* Now check if saturation is being used for the new value */
1182 if (abs(Increment
) >= ((HIGH_PRIORITY
+ 1) / 2))
1184 /* Check if we need positive or negative saturation */
1185 Thread
->Saturation
= (Increment
> 0) ? 1 : -1;
1188 /* Normalize the Base Priority */
1189 BasePriority
= Process
->BasePriority
+ Increment
;
1190 if (Process
->BasePriority
>= LOW_REALTIME_PRIORITY
)
1192 /* Check if it's too low */
1193 if (BasePriority
< LOW_REALTIME_PRIORITY
)
1194 BasePriority
= LOW_REALTIME_PRIORITY
;
1196 /* Check if it's too high */
1197 if (BasePriority
> HIGH_PRIORITY
) BasePriority
= HIGH_PRIORITY
;
1199 /* We are at RTP, so use the raw BP */
1200 Priority
= BasePriority
;
1204 /* Check if it's entering RTP */
1205 if (BasePriority
>= LOW_REALTIME_PRIORITY
)
1206 BasePriority
= LOW_REALTIME_PRIORITY
- 1;
1208 /* Check if it's too low */
1209 if (BasePriority
<= LOW_PRIORITY
)
1212 /* If Saturation is used, then use the raw BP */
1213 if (Thread
->Saturation
)
1215 Priority
= BasePriority
;
1219 /* Calculate the new priority */
1220 Priority
= Thread
->Priority
+ (BasePriority
- CurrentBasePriority
)-
1221 Thread
->PriorityDecrement
;
1223 /* Make sure it won't enter RTP ranges */
1224 if (Priority
>= LOW_REALTIME_PRIORITY
)
1225 Priority
= LOW_REALTIME_PRIORITY
- 1;
1229 /* Finally set the new base priority */
1230 Thread
->BasePriority
= BasePriority
;
1232 /* Reset the decrements */
1233 Thread
->PriorityDecrement
= 0;
1235 /* If the priority will change, reset quantum and change it for real */
1236 if (Priority
!= Thread
->Priority
)
1238 Thread
->Quantum
= Thread
->QuantumReset
;
1239 KiSetPriorityThread(Thread
, Priority
, &Released
);
1242 /* Release Lock if needed */
1245 KeReleaseDispatcherDatabaseLock(OldIrql
);
1249 KeLowerIrql(OldIrql
);
1252 /* Return the Old Increment */
1253 return CurrentIncrement
;
1261 KeSetPriorityThread(PKTHREAD Thread
,
1264 KPRIORITY OldPriority
;
1265 BOOLEAN Released
= FALSE
;
1268 /* Lock the Dispatcher Database */
1269 OldIrql
= KeAcquireDispatcherDatabaseLock();
1271 /* Save the old Priority */
1272 OldPriority
= Thread
->Priority
;
1274 /* Reset the Quantum and Decrements */
1275 Thread
->Quantum
= Thread
->QuantumReset
;
1276 Thread
->PriorityDecrement
= 0;
1278 /* Set the new Priority */
1279 KiSetPriorityThread(Thread
, Priority
, &Released
);
1281 /* Release Lock if needed */
1284 KeReleaseDispatcherDatabaseLock(OldIrql
);
1288 KeLowerIrql(OldIrql
);
1291 /* Return Old Priority */
1298 * Sets thread's affinity
1302 KeSetAffinityThread(PKTHREAD Thread
,
1308 KAFFINITY ProcessorMask
;
1310 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread
, Affinity
);
1312 /* Verify correct affinity */
1313 if ((Affinity
& Thread
->ApcStatePointer
[0]->Process
->Affinity
) !=
1314 Affinity
|| !Affinity
)
1316 KEBUGCHECK(INVALID_AFFINITY_SET
);
1319 OldIrql
= KeAcquireDispatcherDatabaseLock();
1321 Thread
->UserAffinity
= Affinity
;
1323 if (Thread
->SystemAffinityActive
== FALSE
) {
1325 Thread
->Affinity
= Affinity
;
1327 if (Thread
->State
== Running
) {
1329 ProcessorMask
= 1 << KeGetCurrentProcessorNumber();
1330 if (Thread
== KeGetCurrentThread()) {
1332 if (!(Affinity
& ProcessorMask
)) {
1334 KiDispatchThreadNoLock(Ready
);
1335 KeLowerIrql(OldIrql
);
1336 return STATUS_SUCCESS
;
1341 for (i
= 0; i
< KeNumberProcessors
; i
++) {
1343 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1344 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
1346 if (!(Affinity
& ProcessorMask
)) {
1348 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1349 KiRequestReschedule(i
);
1350 KeLowerIrql(OldIrql
);
1351 return STATUS_SUCCESS
;
1358 ASSERT (i
< KeNumberProcessors
);
1363 KeReleaseDispatcherDatabaseLock(OldIrql
);
1364 return STATUS_SUCCESS
;
1370 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1373 KeTerminateThread(IN KPRIORITY Increment
)
1376 PKTHREAD Thread
= KeGetCurrentThread();
1378 /* Lock the Dispatcher Database and the APC Queue */
1379 DPRINT("Terminating\n");
1380 OldIrql
= KeAcquireDispatcherDatabaseLock();
1382 /* Remove the thread from the list */
1383 RemoveEntryList(&Thread
->ThreadListEntry
);
1385 /* Insert into the Reaper List */
1386 DPRINT("List: %p\n", PspReaperList
);
1387 ((PETHREAD
)Thread
)->ReaperLink
= PspReaperList
;
1388 PspReaperList
= (PETHREAD
)Thread
;
1389 DPRINT("List: %p\n", PspReaperList
);
1391 /* Check if it's active */
1392 if (PspReaping
== FALSE
) {
1394 /* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */
1396 DPRINT("Terminating\n");
1397 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1398 &PspReaperWorkItem
.List
,
1402 /* Handle Kernel Queues */
1403 if (Thread
->Queue
) {
1405 DPRINT("Waking Queue\n");
1406 RemoveEntryList(&Thread
->QueueListEntry
);
1407 KiWakeQueue(Thread
->Queue
);
1410 /* Signal the thread */
1411 Thread
->DispatcherHeader
.SignalState
= TRUE
;
1412 if (IsListEmpty(&Thread
->DispatcherHeader
.WaitListHead
) != TRUE
) {
1415 KiWaitTest((PVOID
)Thread
, Increment
);
1418 /* Find a new Thread */
1419 KiDispatchThreadNoLock(Terminated
);
1423 * FUNCTION: Tests whether there are any pending APCs for the current thread
1424 * and if so the APCs will be delivered on exit from kernel mode
1428 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
1431 PKTHREAD Thread
= KeGetCurrentThread();
1434 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1436 /* Lock the Dispatcher Database and the APC Queue */
1437 OldIrql
= KeAcquireDispatcherDatabaseLock();
1438 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
1440 /* Save the old State */
1441 OldState
= Thread
->Alerted
[AlertMode
];
1443 /* If the Thread is Alerted, Clear it */
1446 Thread
->Alerted
[AlertMode
] = FALSE
;
1448 } else if ((AlertMode
!= KernelMode
) && (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))) {
1450 /* If the mode is User and the Queue isn't empty, set Pending */
1451 Thread
->ApcState
.UserApcPending
= TRUE
;
1454 /* Release Locks and return the Old State */
1455 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
1456 KeReleaseDispatcherDatabaseLock(OldIrql
);
1461 KiServiceCheck (VOID
)
1463 PKTHREAD Thread
= KeGetCurrentThread();
1465 /* Check if we need to inialize Win32 for this Thread */
1466 if (Thread
->ServiceTable
!= KeServiceDescriptorTableShadow
) {
1468 /* We do. Initialize it and save the new table */
1469 Thread
->ServiceTable
= KeServiceDescriptorTableShadow
;
1470 PsInitWin32Thread((PETHREAD
)Thread
);
1480 NtAlertResumeThread(IN HANDLE ThreadHandle
,
1481 OUT PULONG SuspendCount
)
1483 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1486 ULONG PreviousState
;
1488 /* Check if parameters are valid */
1489 if(PreviousMode
!= KernelMode
) {
1493 ProbeForWriteUlong(SuspendCount
);
1497 Status
= _SEH_GetExceptionCode();
1502 /* Reference the Object */
1503 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1504 THREAD_SUSPEND_RESUME
,
1510 /* Check for Success */
1511 if (NT_SUCCESS(Status
)) {
1513 /* Call the Kernel Function */
1514 PreviousState
= KeAlertResumeThread(&Thread
->Tcb
);
1516 /* Dereference Object */
1517 ObDereferenceObject(Thread
);
1523 *SuspendCount
= PreviousState
;
1527 Status
= _SEH_GetExceptionCode();
1544 NtAlertThread (IN HANDLE ThreadHandle
)
1546 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1550 /* Reference the Object */
1551 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1552 THREAD_SUSPEND_RESUME
,
1558 /* Check for Success */
1559 if (NT_SUCCESS(Status
)) {
1562 * Do an alert depending on the processor mode. If some kmode code wants to
1563 * enforce a umode alert it should call KeAlertThread() directly. If kmode
1564 * code wants to do a kmode alert it's sufficient to call it with Zw or just
1565 * use KeAlertThread() directly
1567 KeAlertThread(&Thread
->Tcb
, PreviousMode
);
1569 /* Dereference Object */
1570 ObDereferenceObject(Thread
);
1579 NtDelayExecution(IN BOOLEAN Alertable
,
1580 IN PLARGE_INTEGER DelayInterval
)
1582 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1583 LARGE_INTEGER SafeInterval
;
1586 /* Check if parameters are valid */
1587 if(PreviousMode
!= KernelMode
) {
1589 Status
= STATUS_SUCCESS
;
1593 /* make a copy on the kernel stack and let DelayInterval point to it so
1594 we don't need to wrap KeDelayExecutionThread in SEH! */
1595 SafeInterval
= ProbeForReadLargeInteger(DelayInterval
);
1596 DelayInterval
= &SafeInterval
;
1600 Status
= _SEH_GetExceptionCode();
1603 if (!NT_SUCCESS(Status
))
1609 /* Call the Kernel Function */
1610 Status
= KeDelayExecutionThread(PreviousMode
,