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_REQUEST_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
,
79 PLIST_ENTRY current_entry
;
83 Mask
= (1 << Priority
);
85 if (PriorityListMask
& Mask
) {
87 current_entry
= PriorityListHead
[Priority
].Flink
;
89 while (current_entry
!= &PriorityListHead
[Priority
]) {
91 current
= CONTAINING_RECORD(current_entry
, KTHREAD
, WaitListEntry
);
93 if (current
->State
!= Ready
) {
95 DPRINT1("%d/%d\n", ¤t
, current
->State
);
98 ASSERT(current
->State
== Ready
);
100 if (current
->Affinity
& Affinity
) {
102 KiRemoveFromThreadList(current
);
106 current_entry
= current_entry
->Flink
;
115 KiDispatchThreadNoLock(ULONG NewThreadStatus
)
117 KPRIORITY CurrentPriority
;
120 PKTHREAD CurrentThread
= KeGetCurrentThread();
122 DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
123 CurrentThread
, NewThreadStatus
, CurrentThread
->State
);
125 CurrentThread
->State
= (UCHAR
)NewThreadStatus
;
127 if (NewThreadStatus
== Ready
) {
129 KiInsertIntoThreadList(CurrentThread
->Priority
,
133 Affinity
= 1 << KeGetCurrentProcessorNumber();
135 for (CurrentPriority
= HIGH_PRIORITY
; CurrentPriority
>= LOW_PRIORITY
; CurrentPriority
--) {
137 Candidate
= KiScanThreadList(CurrentPriority
, Affinity
);
139 if (Candidate
== CurrentThread
) {
141 Candidate
->State
= Running
;
142 KeReleaseDispatcherDatabaseLockFromDpcLevel();
146 if (Candidate
!= NULL
) {
151 DPRINT("Scheduling %x(%d)\n",Candidate
, CurrentPriority
);
153 Candidate
->State
= Running
;
155 OldThread
= CurrentThread
;
156 CurrentThread
= Candidate
;
157 IdleThread
= KeGetCurrentPrcb()->IdleThread
;
159 if (OldThread
== IdleThread
) {
161 IdleProcessorMask
&= ~Affinity
;
163 } else if (CurrentThread
== IdleThread
) {
165 IdleProcessorMask
|= Affinity
;
168 MmUpdatePageDir(PsGetCurrentProcess(),((PETHREAD
)CurrentThread
)->ThreadsProcess
, sizeof(EPROCESS
));
170 /* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */
171 DPRINT("You are : %x, swapping to: %x\n", OldThread
, CurrentThread
);
172 KiArchContextSwitch(CurrentThread
);
173 DPRINT("You are : %x, swapped from: %x\n", OldThread
, CurrentThread
);
178 DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber());
184 KiBlockThread(PNTSTATUS Status
,
189 PKTHREAD Thread
= KeGetCurrentThread();
190 PKWAIT_BLOCK WaitBlock
;
192 if (Thread
->ApcState
.KernelApcPending
) {
194 DPRINT("Dispatching Thread as ready (APC!)\n");
197 WaitBlock
= Thread
->WaitBlockList
;
199 RemoveEntryList (&WaitBlock
->WaitListEntry
);
200 WaitBlock
= WaitBlock
->NextWaitBlock
;
201 } while (WaitBlock
!= Thread
->WaitBlockList
);
202 Thread
->WaitBlockList
= NULL
;
204 /* Dispatch it and return status */
205 KiDispatchThreadNoLock (Ready
);
206 if (Status
!= NULL
) *Status
= STATUS_KERNEL_APC
;
210 /* Set the Thread Data as Requested */
211 DPRINT("Dispatching Thread as blocked: %d\n", Thread
->WaitStatus
);
212 Thread
->Alertable
= Alertable
;
213 Thread
->WaitMode
= (UCHAR
)WaitMode
;
214 Thread
->WaitReason
= WaitReason
;
216 /* Dispatch it and return status */
217 KiDispatchThreadNoLock(Waiting
);
218 DPRINT("Dispatching Thread as blocked: %d\n", Thread
->WaitStatus
);
219 if (Status
!= NULL
) *Status
= Thread
->WaitStatus
;
222 DPRINT("Releasing Dispatcher Lock\n");
223 KfLowerIrql(Thread
->WaitIrql
);
228 KiDispatchThread(ULONG NewThreadStatus
)
232 if (KeGetCurrentPrcb()->IdleThread
== NULL
) {
236 OldIrql
= KeAcquireDispatcherDatabaseLock();
237 KiDispatchThreadNoLock(NewThreadStatus
);
238 KeLowerIrql(OldIrql
);
243 KiUnblockThread(PKTHREAD Thread
,
244 PNTSTATUS WaitStatus
,
247 if (Terminated
== Thread
->State
) {
249 DPRINT("Can't unblock thread 0x%x because it's terminating\n",
252 } else if (Ready
== Thread
->State
||
253 Running
== Thread
->State
) {
255 DPRINT("Can't unblock thread 0x%x because it's %s\n",
256 Thread
, (Thread
->State
== Ready
? "ready" : "running"));
263 /* FIXME: This propably isn't the right way to do it... */
264 /* No it's not... i'll fix it later-- Alex */
265 if (Thread
->Priority
< LOW_REALTIME_PRIORITY
&&
266 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2) {
268 if (!Thread
->PriorityDecrement
&& !Thread
->DisableBoost
) {
270 Thread
->Priority
= Thread
->BasePriority
+ Increment
;
271 Thread
->PriorityDecrement
= Increment
;
274 /* Also decrease quantum */
279 Thread
->Quantum
= Thread
->QuantumReset
;
282 if (WaitStatus
!= NULL
) {
284 Thread
->WaitStatus
= *WaitStatus
;
287 Thread
->State
= Ready
;
288 KiInsertIntoThreadList(Thread
->Priority
, Thread
);
289 Processor
= KeGetCurrentProcessorNumber();
290 Affinity
= Thread
->Affinity
;
292 if (!(IdleProcessorMask
& (1 << Processor
) & Affinity
) &&
293 (IdleProcessorMask
& ~(1 << Processor
) & Affinity
)) {
297 for (i
= 0; i
< KeNumberProcessors
- 1; i
++) {
301 if (Processor
>= KeNumberProcessors
) {
306 if (IdleProcessorMask
& (1 << Processor
) & Affinity
) {
309 * Reschedule the threads on an other processor
311 KeReleaseDispatcherDatabaseLockFromDpcLevel();
312 KiRequestReschedule(Processor
);
313 KeAcquireDispatcherDatabaseLockAtDpcLevel();
324 KiAdjustQuantumThread(IN PKTHREAD Thread
)
328 /* Don't adjust for RT threads */
329 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
330 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2)
332 /* Decrease Quantum by one and see if we've ran out */
333 if (--Thread
->Quantum
<= 0)
336 Thread
->Quantum
= Thread
->QuantumReset
;
338 /* Calculate new Priority */
339 Priority
= Thread
->Priority
- (Thread
->PriorityDecrement
+ 1);
341 /* Normalize it if we've gone too low */
342 if (Priority
< Thread
->BasePriority
) Priority
= Thread
->BasePriority
;
344 /* Reset the priority decrement, we've done it */
345 Thread
->PriorityDecrement
= 0;
347 /* Set the new priority, if needed */
348 if (Priority
!= Thread
->Priority
)
351 * FIXME: This should be a call to KiSetPriorityThread but
352 * due to the current ""scheduler"" in ROS, it can't be done
353 * cleanly since it actualyl dispatches threads instead.
355 Thread
->Priority
= Priority
;
359 /* FIXME: Priority hasn't changed, find a new thread */
364 /* Nothing to do... */
371 KiSuspendThreadKernelRoutine(PKAPC Apc
,
372 PKNORMAL_ROUTINE
* NormalRoutine
,
373 PVOID
* NormalContext
,
374 PVOID
* SystemArgument1
,
375 PVOID
* SystemArguemnt2
)
381 KiSuspendThreadNormalRoutine(PVOID NormalContext
,
382 PVOID SystemArgument1
,
383 PVOID SystemArgument2
)
385 PKTHREAD CurrentThread
= KeGetCurrentThread();
387 /* Non-alertable kernel-mode suspended wait */
388 DPRINT("Waiting...\n");
389 KeWaitForSingleObject(&CurrentThread
->SuspendSemaphore
,
394 DPRINT("Done Waiting\n");
397 #ifdef KeGetCurrentThread
398 #undef KeGetCurrentThread
405 KeGetCurrentThread(VOID
)
410 Ke386SaveFlags(Flags
);
411 Ke386DisableInterrupts();
412 Thread
= KeGetCurrentPrcb()->CurrentThread
;
413 Ke386RestoreFlags(Flags
);
416 return(KeGetCurrentPrcb()->CurrentThread
);
422 KeSetPreviousMode(ULONG Mode
)
424 PsGetCurrentThread()->Tcb
.PreviousMode
= (UCHAR
)Mode
;
432 KeGetPreviousMode(VOID
)
434 return (ULONG
)PsGetCurrentThread()->Tcb
.PreviousMode
;
439 KeDisableThreadApcQueueing(IN PKTHREAD Thread
)
442 BOOLEAN PreviousState
;
444 /* Lock the Dispatcher Database */
445 OldIrql
= KeAcquireDispatcherDatabaseLock();
448 PreviousState
= Thread
->ApcQueueable
;
451 Thread
->ApcQueueable
= FALSE
;
453 /* Release the Lock */
454 KeReleaseDispatcherDatabaseLock(OldIrql
);
456 /* Return old state */
457 return PreviousState
;
462 KeRundownThread(VOID
)
465 PKTHREAD Thread
= KeGetCurrentThread();
466 PLIST_ENTRY CurrentEntry
;
469 DPRINT("KeRundownThread: %x\n", Thread
);
471 /* Lock the Dispatcher Database */
472 OldIrql
= KeAcquireDispatcherDatabaseLock();
474 while (!IsListEmpty(&Thread
->MutantListHead
)) {
477 CurrentEntry
= RemoveHeadList(&Thread
->MutantListHead
);
478 Mutant
= CONTAINING_RECORD(CurrentEntry
, KMUTANT
, MutantListEntry
);
479 ASSERT(Mutant
->ApcDisable
== 0);
481 /* Uncondtionally abandon it */
482 DPRINT("Abandonning the Mutant\n");
483 Mutant
->Header
.SignalState
= 1;
484 Mutant
->Abandoned
= TRUE
;
485 Mutant
->OwnerThread
= NULL
;
486 RemoveEntryList(&Mutant
->MutantListEntry
);
488 /* Check if the Wait List isn't empty */
489 DPRINT("Checking whether to wake the Mutant\n");
490 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
)) {
492 /* Wake the Mutant */
493 DPRINT("Waking the Mutant\n");
494 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
498 /* Release the Lock */
499 KeReleaseDispatcherDatabaseLock(OldIrql
);
504 KeResumeThread(PKTHREAD Thread
)
509 DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread
,
510 Thread
->SuspendCount
, Thread
->FreezeCount
);
512 /* Lock the Dispatcher */
513 OldIrql
= KeAcquireDispatcherDatabaseLock();
515 /* Save the Old Count */
516 PreviousCount
= Thread
->SuspendCount
;
518 /* Check if it existed */
521 Thread
->SuspendCount
--;
523 /* Decrease the current Suspend Count and Check Freeze Count */
524 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
)) {
526 /* Signal the Suspend Semaphore */
527 Thread
->SuspendSemaphore
.Header
.SignalState
++;
528 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
532 /* Release Lock and return the Old State */
533 KeReleaseDispatcherDatabaseLock(OldIrql
);
534 return PreviousCount
;
539 KiInsertQueueApc(PKAPC Apc
,
540 KPRIORITY PriorityBoost
);
543 * Used by the debugging code to freeze all the process's threads
544 * while the debugger is examining their state.
548 KeFreezeAllThreads(PKPROCESS Process
)
551 PLIST_ENTRY CurrentEntry
;
553 PKTHREAD CurrentThread
= KeGetCurrentThread();
556 OldIrql
= KeAcquireDispatcherDatabaseLock();
558 /* Loop the Process's Threads */
559 CurrentEntry
= Process
->ThreadListHead
.Flink
;
560 while (CurrentEntry
!= &Process
->ThreadListHead
)
563 Current
= CONTAINING_RECORD(CurrentEntry
, KTHREAD
, ThreadListEntry
);
565 /* Make sure it's not ours */
566 if (Current
== CurrentThread
) continue;
568 /* Make sure it wasn't already frozen, and that it's not suspended */
569 if (!(++Current
->FreezeCount
) && !(Current
->SuspendCount
))
572 if (!KiInsertQueueApc(&Current
->SuspendApc
, IO_NO_INCREMENT
))
574 /* Unsignal the Semaphore, the APC already got inserted */
575 Current
->SuspendSemaphore
.Header
.SignalState
--;
579 CurrentEntry
= CurrentEntry
->Flink
;
582 /* Release the lock */
583 KeReleaseDispatcherDatabaseLock(OldIrql
);
588 KeSuspendThread(PKTHREAD Thread
)
593 DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread
, Thread
->SuspendCount
, Thread
->FreezeCount
);
595 /* Lock the Dispatcher */
596 OldIrql
= KeAcquireDispatcherDatabaseLock();
598 /* Save the Old Count */
599 PreviousCount
= Thread
->SuspendCount
;
601 /* Handle the maximum */
602 if (PreviousCount
== MAXIMUM_SUSPEND_COUNT
)
604 /* Raise an exception */
605 KeReleaseDispatcherDatabaseLock(OldIrql
);
606 ExRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED
);
610 Thread
->SuspendCount
++;
612 /* Check if we should suspend it */
613 if (!PreviousCount
&& !Thread
->FreezeCount
) {
616 if (!KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
)) {
618 /* Unsignal the Semaphore, the APC already got inserted */
619 Thread
->SuspendSemaphore
.Header
.SignalState
--;
623 /* Release Lock and return the Old State */
624 KeReleaseDispatcherDatabaseLock(OldIrql
);
625 return PreviousCount
;
630 KeForceResumeThread(IN PKTHREAD Thread
)
635 /* Lock the Dispatcher Database and the APC Queue */
636 OldIrql
= KeAcquireDispatcherDatabaseLock();
638 /* Save the old Suspend Count */
639 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
641 /* If the thread is suspended, wake it up!!! */
644 /* Unwait it completely */
645 Thread
->SuspendCount
= 0;
646 Thread
->FreezeCount
= 0;
648 /* Signal and satisfy */
649 Thread
->SuspendSemaphore
.Header
.SignalState
++;
650 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
653 /* Release Lock and return the Old State */
654 KeReleaseDispatcherDatabaseLock(OldIrql
);
655 return PreviousCount
;
660 KeAlertResumeThread(IN PKTHREAD Thread
)
665 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
667 /* Lock the Dispatcher Database and the APC Queue */
668 OldIrql
= KeAcquireDispatcherDatabaseLock();
669 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
671 /* Return if Thread is already alerted. */
672 if (Thread
->Alerted
[KernelMode
] == FALSE
) {
674 /* If it's Blocked, unblock if it we should */
675 if (Thread
->State
== Waiting
&& Thread
->Alertable
) {
677 DPRINT("Aborting Wait\n");
678 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
682 /* If not, simply Alert it */
683 Thread
->Alerted
[KernelMode
] = TRUE
;
687 /* Save the old Suspend Count */
688 PreviousCount
= Thread
->SuspendCount
;
690 /* If the thread is suspended, decrease one of the suspend counts */
693 /* Decrease count. If we are now zero, unwait it completely */
694 if (--Thread
->SuspendCount
) {
696 /* Signal and satisfy */
697 Thread
->SuspendSemaphore
.Header
.SignalState
++;
698 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
702 /* Release Locks and return the Old State */
703 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
704 KeReleaseDispatcherDatabaseLock(OldIrql
);
705 return PreviousCount
;
710 KeAlertThread(PKTHREAD Thread
,
711 KPROCESSOR_MODE AlertMode
)
714 BOOLEAN PreviousState
;
716 /* Acquire the Dispatcher Database Lock */
717 OldIrql
= KeAcquireDispatcherDatabaseLock();
719 /* Save the Previous State */
720 PreviousState
= Thread
->Alerted
[AlertMode
];
722 /* Return if Thread is already alerted. */
723 if (PreviousState
== FALSE
) {
725 /* If it's Blocked, unblock if it we should */
726 if (Thread
->State
== Waiting
&&
727 (AlertMode
== KernelMode
|| Thread
->WaitMode
== AlertMode
) &&
730 DPRINT("Aborting Wait\n");
731 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
735 /* If not, simply Alert it */
736 Thread
->Alerted
[AlertMode
] = TRUE
;
740 /* Release the Dispatcher Lock */
741 KeReleaseDispatcherDatabaseLock(OldIrql
);
743 /* Return the old state */
744 return PreviousState
;
752 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
758 IN PVOID ThreadState
)
764 * FUNCTION: Initialize the microkernel state of the thread
768 KeInitializeThread(PKPROCESS Process
,
770 PKSYSTEM_ROUTINE SystemRoutine
,
771 PKSTART_ROUTINE StartRoutine
,
777 /* Initalize the Dispatcher Header */
778 DPRINT("Initializing Dispatcher Header for New Thread: %x in Process: %x\n", Thread
, Process
);
779 KeInitializeDispatcherHeader(&Thread
->DispatcherHeader
,
784 DPRINT("Thread Header Created. SystemRoutine: %x, StartRoutine: %x with Context: %x\n",
785 SystemRoutine
, StartRoutine
, StartContext
);
786 DPRINT("UserMode Information. Context: %x, Teb: %x\n", Context
, Teb
);
788 /* Initialize the Mutant List */
789 InitializeListHead(&Thread
->MutantListHead
);
791 /* Setup the Service Descriptor Table for Native Calls */
792 Thread
->ServiceTable
= KeServiceDescriptorTable
;
794 /* Setup APC Fields */
795 InitializeListHead(&Thread
->ApcState
.ApcListHead
[0]);
796 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
797 Thread
->ApcState
.Process
= Process
;
798 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
799 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
800 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
801 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
803 /* Initialize the Suspend APC */
804 KeInitializeApc(&Thread
->SuspendApc
,
806 OriginalApcEnvironment
,
807 KiSuspendThreadKernelRoutine
,
809 KiSuspendThreadNormalRoutine
,
813 /* Initialize the Suspend Semaphore */
814 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 128);
816 /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
818 Thread
->WaitBlock
[3].Object
= (PVOID
)&Thread
->Timer
;
819 Thread
->WaitBlock
[3].Thread
= Thread
;
820 Thread
->WaitBlock
[3].WaitKey
= STATUS_TIMEOUT
;
821 Thread
->WaitBlock
[3].WaitType
= WaitAny
;
822 Thread
->WaitBlock
[3].NextWaitBlock
= NULL
;
823 InsertTailList(&Thread
->Timer
.Header
.WaitListHead
,
824 &Thread
->WaitBlock
[3].WaitListEntry
);
826 KeInitializeTimer(&Thread
->Timer
);
831 /* Set the Thread Stacks */
832 Thread
->InitialStack
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
833 Thread
->StackBase
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
834 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
;
835 Thread
->KernelStackResident
= TRUE
;
838 * Establish the pde's for the new stack and the thread structure within the
839 * address space of the new process. They are accessed while taskswitching or
840 * while handling page faults. At this point it isn't possible to call the
841 * page fault handler for the missing pde's.
843 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
->StackLimit
, MM_STACK_SIZE
);
844 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
846 /* Initalize the Thread Context */
847 DPRINT("Initializing the Context for the thread: %x\n", Thread
);
848 KiArchInitThreadWithContext(Thread
,
854 /* Setup scheduler Fields based on Parent */
855 DPRINT("Thread context created, setting Scheduler Data\n");
856 Thread
->BasePriority
= Process
->BasePriority
;
857 Thread
->Quantum
= Process
->QuantumReset
;
858 Thread
->QuantumReset
= Process
->QuantumReset
;
859 Thread
->Affinity
= Process
->Affinity
;
860 Thread
->Priority
= Process
->BasePriority
;
861 Thread
->UserAffinity
= Process
->Affinity
;
862 Thread
->DisableBoost
= Process
->DisableBoost
;
863 Thread
->AutoAlignment
= Process
->AutoAlignment
;
864 Thread
->Iopl
= Process
->Iopl
;
866 /* Set the Thread to initalized */
867 Thread
->State
= Initialized
;
870 * Insert the Thread into the Process's Thread List
871 * Note, this is the KTHREAD Thread List. It is removed in
872 * ke/kthread.c!KeTerminateThread.
874 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
875 DPRINT("Thread initalized\n");
884 KeQueryPriorityThread (IN PKTHREAD Thread
)
886 return Thread
->Priority
;
894 KeQueryRuntimeThread(IN PKTHREAD Thread
,
897 /* Return the User Time */
898 *UserTime
= Thread
->UserTime
;
900 /* Return the Kernel Time */
901 return Thread
->KernelTime
;
909 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
911 PKTHREAD Thread
= KeGetCurrentThread();
912 BOOLEAN PreviousState
;
915 /* Lock the Dispatcher Database */
916 OldIrql
= KeAcquireDispatcherDatabaseLock();
919 PreviousState
= Thread
->EnableStackSwap
;
922 Thread
->EnableStackSwap
= Enable
;
924 /* No, Release Lock */
925 KeReleaseDispatcherDatabaseLock(OldIrql
);
927 /* Return Old State */
928 return PreviousState
;
936 KeRevertToUserAffinityThread(VOID
)
938 PKTHREAD CurrentThread
= KeGetCurrentThread();
941 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
943 /* Lock the Dispatcher Database */
944 OldIrql
= KeAcquireDispatcherDatabaseLock();
946 /* Return to User Affinity */
947 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
949 /* Disable System Affinity */
950 CurrentThread
->SystemAffinityActive
= FALSE
;
952 /* Check if we need to Dispatch a New thread */
953 if (CurrentThread
->Affinity
& (1 << KeGetCurrentProcessorNumber())) {
955 /* No, just release */
956 KeReleaseDispatcherDatabaseLock(OldIrql
);
960 /* We need to dispatch a new thread */
961 CurrentThread
->WaitIrql
= OldIrql
;
962 KiDispatchThreadNoLock(Ready
);
963 KeLowerIrql(OldIrql
);
972 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
975 CCHAR PreviousIdealProcessor
;
978 /* Lock the Dispatcher Database */
979 OldIrql
= KeAcquireDispatcherDatabaseLock();
981 /* Save Old Ideal Processor */
982 PreviousIdealProcessor
= Thread
->IdealProcessor
;
984 /* Set New Ideal Processor */
985 Thread
->IdealProcessor
= Processor
;
988 KeReleaseDispatcherDatabaseLock(OldIrql
);
990 /* Return Old Ideal Processor */
991 return PreviousIdealProcessor
;
999 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
1001 PKTHREAD CurrentThread
= KeGetCurrentThread();
1004 ASSERT(Affinity
& ((1 << KeNumberProcessors
) - 1));
1006 /* Lock the Dispatcher Database */
1007 OldIrql
= KeAcquireDispatcherDatabaseLock();
1009 /* Set the System Affinity Specified */
1010 CurrentThread
->Affinity
= Affinity
;
1012 /* Enable System Affinity */
1013 CurrentThread
->SystemAffinityActive
= TRUE
;
1015 /* Check if we need to Dispatch a New thread */
1016 if (Affinity
& (1 << KeGetCurrentProcessorNumber())) {
1018 /* No, just release */
1019 KeReleaseDispatcherDatabaseLock(OldIrql
);
1023 /* We need to dispatch a new thread */
1024 CurrentThread
->WaitIrql
= OldIrql
;
1025 KiDispatchThreadNoLock(Ready
);
1026 KeLowerIrql(OldIrql
);
1032 KeQueryBasePriorityThread(IN PKTHREAD Thread
)
1034 LONG BasePriorityIncrement
;
1038 /* Lock the Dispatcher Database */
1039 OldIrql
= KeAcquireDispatcherDatabaseLock();
1041 /* Get the Process */
1042 Process
= Thread
->ApcStatePointer
[0]->Process
;
1044 /* Calculate the BPI */
1045 BasePriorityIncrement
= Thread
->BasePriority
- Process
->BasePriority
;
1047 /* If saturation occured, return the SI instead */
1048 if (Thread
->Saturation
) BasePriorityIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1052 KeReleaseDispatcherDatabaseLock(OldIrql
);
1054 /* Return Increment */
1055 return BasePriorityIncrement
;
1060 KiSetPriorityThread(PKTHREAD Thread
,
1064 KPRIORITY OldPriority
= Thread
->Priority
;
1068 DPRINT("Changing prio to : %lx\n", Priority
);
1070 /* Check if priority changed */
1071 if (OldPriority
!= Priority
)
1074 Thread
->Priority
= Priority
;
1076 /* Choose action based on thread's state */
1077 if (Thread
->State
== Ready
)
1079 /* Remove it from the current queue */
1080 KiRemoveFromThreadList(Thread
);
1082 /* Re-insert it at its current priority */
1083 KiInsertIntoThreadList(Priority
, Thread
);
1085 /* Check if the old priority was lower */
1086 if (KeGetCurrentThread()->Priority
< Priority
)
1088 /* Dispatch it immediately */
1089 KiDispatchThreadNoLock(Ready
);
1094 else if (Thread
->State
== Running
)
1096 /* Check if the new priority is lower */
1097 if (Priority
< OldPriority
)
1099 /* Check for threads with a higher priority */
1100 Mask
= ~((1 << (Priority
+ 1)) - 1);
1101 if (PriorityListMask
& Mask
)
1103 /* Found a thread, is it us? */
1104 if (Thread
== KeGetCurrentThread())
1107 KiDispatchThreadNoLock(Ready
);
1113 /* Loop every CPU */
1114 for (i
= 0; i
< KeNumberProcessors
; i
++)
1116 /* Get the PCR for this CPU */
1117 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1119 /* Reschedule if the new one is already on a CPU */
1120 if (Pcr
->Prcb
->CurrentThread
== Thread
)
1122 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1123 KiRequestReschedule(i
);
1134 /* Return to caller */
1139 * Sets thread's base priority relative to the process' base priority
1140 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
1146 KeSetBasePriorityThread (PKTHREAD Thread
,
1152 KPRIORITY CurrentBasePriority
;
1153 KPRIORITY BasePriority
;
1154 BOOLEAN Released
= FALSE
;
1155 LONG CurrentIncrement
;
1157 /* Lock the Dispatcher Database */
1158 OldIrql
= KeAcquireDispatcherDatabaseLock();
1160 /* Get the process and calculate current BP and BPI */
1161 Process
= Thread
->ApcStatePointer
[0]->Process
;
1162 CurrentBasePriority
= Thread
->BasePriority
;
1163 CurrentIncrement
= CurrentBasePriority
- Process
->BasePriority
;
1165 /* Change to use the SI if Saturation was used */
1166 if (Thread
->Saturation
) CurrentIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1169 /* Now check if saturation is being used for the new value */
1170 if (abs(Increment
) >= ((HIGH_PRIORITY
+ 1) / 2))
1172 /* Check if we need positive or negative saturation */
1173 Thread
->Saturation
= (Increment
> 0) ? 1 : -1;
1176 /* Normalize the Base Priority */
1177 BasePriority
= Process
->BasePriority
+ Increment
;
1178 if (Process
->BasePriority
>= LOW_REALTIME_PRIORITY
)
1180 /* Check if it's too low */
1181 if (BasePriority
< LOW_REALTIME_PRIORITY
)
1182 BasePriority
= LOW_REALTIME_PRIORITY
;
1184 /* Check if it's too high */
1185 if (BasePriority
> HIGH_PRIORITY
) BasePriority
= HIGH_PRIORITY
;
1187 /* We are at RTP, so use the raw BP */
1188 Priority
= BasePriority
;
1192 /* Check if it's entering RTP */
1193 if (BasePriority
>= LOW_REALTIME_PRIORITY
)
1194 BasePriority
= LOW_REALTIME_PRIORITY
- 1;
1196 /* Check if it's too low */
1197 if (BasePriority
<= LOW_PRIORITY
)
1200 /* If Saturation is used, then use the raw BP */
1201 if (Thread
->Saturation
)
1203 Priority
= BasePriority
;
1207 /* Calculate the new priority */
1208 Priority
= Thread
->Priority
+ (BasePriority
- CurrentBasePriority
)-
1209 Thread
->PriorityDecrement
;
1211 /* Make sure it won't enter RTP ranges */
1212 if (Priority
>= LOW_REALTIME_PRIORITY
)
1213 Priority
= LOW_REALTIME_PRIORITY
- 1;
1217 /* Finally set the new base priority */
1218 Thread
->BasePriority
= BasePriority
;
1220 /* Reset the decrements */
1221 Thread
->DecrementCount
= 0;
1222 Thread
->PriorityDecrement
= 0;
1224 /* If the priority will change, reset quantum and change it for real */
1225 if (Priority
!= Thread
->Priority
)
1227 Thread
->Quantum
= Thread
->QuantumReset
;
1228 KiSetPriorityThread(Thread
, Priority
, &Released
);
1231 /* Release Lock if needed */
1234 KeReleaseDispatcherDatabaseLock(OldIrql
);
1238 KeLowerIrql(OldIrql
);
1241 /* Return the Old Increment */
1242 return CurrentIncrement
;
1250 KeSetPriorityThread(PKTHREAD Thread
,
1253 KPRIORITY OldPriority
;
1254 BOOLEAN Released
= FALSE
;
1257 /* Lock the Dispatcher Database */
1258 OldIrql
= KeAcquireDispatcherDatabaseLock();
1260 /* Save the old Priority */
1261 OldPriority
= Thread
->Priority
;
1263 /* Reset the Quantum and Decrements */
1264 Thread
->Quantum
= Thread
->QuantumReset
;
1265 Thread
->DecrementCount
= 0;
1266 Thread
->PriorityDecrement
= 0;
1268 /* Set the new Priority */
1269 KiSetPriorityThread(Thread
, Priority
, &Released
);
1271 /* Release Lock if needed */
1274 KeReleaseDispatcherDatabaseLock(OldIrql
);
1278 KeLowerIrql(OldIrql
);
1281 /* Return Old Priority */
1288 * Sets thread's affinity
1292 KeSetAffinityThread(PKTHREAD Thread
,
1298 KAFFINITY ProcessorMask
;
1300 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread
, Affinity
);
1302 /* Verify correct affinity */
1303 if ((Affinity
& Thread
->ApcStatePointer
[0]->Process
->Affinity
) !=
1304 Affinity
|| !Affinity
)
1306 KEBUGCHECK(INVALID_AFFINITY_SET
);
1309 OldIrql
= KeAcquireDispatcherDatabaseLock();
1311 Thread
->UserAffinity
= Affinity
;
1313 if (Thread
->SystemAffinityActive
== FALSE
) {
1315 Thread
->Affinity
= Affinity
;
1317 if (Thread
->State
== Running
) {
1319 ProcessorMask
= 1 << KeGetCurrentKPCR()->Number
;
1320 if (Thread
== KeGetCurrentThread()) {
1322 if (!(Affinity
& ProcessorMask
)) {
1324 KiDispatchThreadNoLock(Ready
);
1325 KeLowerIrql(OldIrql
);
1326 return STATUS_SUCCESS
;
1331 for (i
= 0; i
< KeNumberProcessors
; i
++) {
1333 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1334 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
1336 if (!(Affinity
& ProcessorMask
)) {
1338 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1339 KiRequestReschedule(i
);
1340 KeLowerIrql(OldIrql
);
1341 return STATUS_SUCCESS
;
1348 ASSERT (i
< KeNumberProcessors
);
1353 KeReleaseDispatcherDatabaseLock(OldIrql
);
1354 return STATUS_SUCCESS
;
1360 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1363 KeTerminateThread(IN KPRIORITY Increment
)
1366 PKTHREAD Thread
= KeGetCurrentThread();
1368 /* Lock the Dispatcher Database and the APC Queue */
1369 DPRINT("Terminating\n");
1370 OldIrql
= KeAcquireDispatcherDatabaseLock();
1372 /* Remove the thread from the list */
1373 RemoveEntryList(&Thread
->ThreadListEntry
);
1375 /* Insert into the Reaper List */
1376 DPRINT("List: %p\n", PspReaperList
);
1377 ((PETHREAD
)Thread
)->ReaperLink
= PspReaperList
;
1378 PspReaperList
= (PETHREAD
)Thread
;
1379 DPRINT("List: %p\n", PspReaperList
);
1381 /* Check if it's active */
1382 if (PspReaping
== FALSE
) {
1384 /* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */
1386 DPRINT("Terminating\n");
1387 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1388 &PspReaperWorkItem
.List
,
1392 /* Handle Kernel Queues */
1393 if (Thread
->Queue
) {
1395 DPRINT("Waking Queue\n");
1396 RemoveEntryList(&Thread
->QueueListEntry
);
1397 KiWakeQueue(Thread
->Queue
);
1400 /* Signal the thread */
1401 Thread
->DispatcherHeader
.SignalState
= TRUE
;
1402 if (IsListEmpty(&Thread
->DispatcherHeader
.WaitListHead
) != TRUE
) {
1405 KiWaitTest((PVOID
)Thread
, Increment
);
1408 /* Find a new Thread */
1409 KiDispatchThreadNoLock(Terminated
);
1413 * FUNCTION: Tests whether there are any pending APCs for the current thread
1414 * and if so the APCs will be delivered on exit from kernel mode
1418 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
1421 PKTHREAD Thread
= KeGetCurrentThread();
1424 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1426 /* Lock the Dispatcher Database and the APC Queue */
1427 OldIrql
= KeAcquireDispatcherDatabaseLock();
1428 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
1430 /* Save the old State */
1431 OldState
= Thread
->Alerted
[AlertMode
];
1433 /* If the Thread is Alerted, Clear it */
1436 Thread
->Alerted
[AlertMode
] = FALSE
;
1438 } else if ((AlertMode
== UserMode
) && (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))) {
1440 /* If the mode is User and the Queue isn't empty, set Pending */
1441 Thread
->ApcState
.UserApcPending
= TRUE
;
1444 /* Release Locks and return the Old State */
1445 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
1446 KeReleaseDispatcherDatabaseLock(OldIrql
);
1451 KiServiceCheck (VOID
)
1453 PKTHREAD Thread
= KeGetCurrentThread();
1455 /* Check if we need to inialize Win32 for this Thread */
1456 if (Thread
->ServiceTable
!= KeServiceDescriptorTableShadow
) {
1458 /* We do. Initialize it and save the new table */
1459 PsInitWin32Thread((PETHREAD
)Thread
);
1460 Thread
->ServiceTable
= KeServiceDescriptorTableShadow
;
1470 NtAlertResumeThread(IN HANDLE ThreadHandle
,
1471 OUT PULONG SuspendCount
)
1473 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1476 ULONG PreviousState
;
1478 /* Check if parameters are valid */
1479 if(PreviousMode
!= KernelMode
) {
1483 ProbeForWrite(SuspendCount
,
1489 Status
= _SEH_GetExceptionCode();
1494 /* Reference the Object */
1495 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1496 THREAD_SUSPEND_RESUME
,
1502 /* Check for Success */
1503 if (NT_SUCCESS(Status
)) {
1505 /* Call the Kernel Function */
1506 PreviousState
= KeAlertResumeThread(&Thread
->Tcb
);
1508 /* Dereference Object */
1509 ObDereferenceObject(Thread
);
1515 *SuspendCount
= PreviousState
;
1519 Status
= _SEH_GetExceptionCode();
1536 NtAlertThread (IN HANDLE ThreadHandle
)
1538 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1542 /* Reference the Object */
1543 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1544 THREAD_SUSPEND_RESUME
,
1550 /* Check for Success */
1551 if (NT_SUCCESS(Status
)) {
1554 * Do an alert depending on the processor mode. If some kmode code wants to
1555 * enforce a umode alert it should call KeAlertThread() directly. If kmode
1556 * code wants to do a kmode alert it's sufficient to call it with Zw or just
1557 * use KeAlertThread() directly
1559 KeAlertThread(&Thread
->Tcb
, PreviousMode
);
1561 /* Dereference Object */
1562 ObDereferenceObject(Thread
);
1571 NtDelayExecution(IN BOOLEAN Alertable
,
1572 IN PLARGE_INTEGER DelayInterval
)
1574 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1575 LARGE_INTEGER SafeInterval
;
1578 /* Check if parameters are valid */
1579 if(PreviousMode
!= KernelMode
) {
1583 ProbeForRead(DelayInterval
,
1584 sizeof(LARGE_INTEGER
),
1587 /* make a copy on the kernel stack and let DelayInterval point to it so
1588 we don't need to wrap KeDelayExecutionThread in SEH! */
1589 SafeInterval
= *DelayInterval
;
1593 Status
= _SEH_GetExceptionCode();
1597 /* Call the Kernel Function */
1598 Status
= KeDelayExecutionThread(PreviousMode
,