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>
17 extern EX_WORK_QUEUE ExWorkerQueue
[MaximumWorkQueue
];
20 * PURPOSE: List of threads associated with each priority level
22 LIST_ENTRY PriorityListHead
[MAXIMUM_PRIORITY
];
23 static ULONG PriorityListMask
= 0;
24 ULONG IdleProcessorMask
= 0;
25 extern PETHREAD PspReaperList
;
27 /* FUNCTIONS *****************************************************************/
31 KiRequestReschedule(CCHAR Processor
)
35 Pcr
= (PKPCR
)(KPCR_BASE
+ Processor
* PAGE_SIZE
);
36 Pcr
->Prcb
->QuantumEnd
= TRUE
;
37 KiIpiSendRequest(1 << Processor
, IPI_DPC
);
42 KiInsertIntoThreadList(KPRIORITY Priority
,
45 ASSERT(Ready
== Thread
->State
);
46 ASSERT(Thread
->Priority
== Priority
);
48 if (Priority
>= MAXIMUM_PRIORITY
|| Priority
< LOW_PRIORITY
) {
50 DPRINT1("Invalid thread priority (%d)\n", Priority
);
54 InsertTailList(&PriorityListHead
[Priority
], &Thread
->WaitListEntry
);
55 PriorityListMask
|= (1 << Priority
);
60 KiRemoveFromThreadList(PKTHREAD Thread
)
62 ASSERT(Ready
== Thread
->State
);
63 RemoveEntryList(&Thread
->WaitListEntry
);
64 if (IsListEmpty(&PriorityListHead
[(ULONG
)Thread
->Priority
])) {
66 PriorityListMask
&= ~(1 << Thread
->Priority
);
72 KiScanThreadList(KPRIORITY Priority
,
78 Mask
= (1 << Priority
);
80 if (PriorityListMask
& Mask
) {
82 LIST_FOR_EACH(current
, &PriorityListHead
[Priority
], KTHREAD
, WaitListEntry
) {
84 if (current
->State
!= Ready
) {
86 DPRINT1("%d/%d\n", ¤t
, current
->State
);
89 ASSERT(current
->State
== Ready
);
91 if (current
->Affinity
& Affinity
) {
93 KiRemoveFromThreadList(current
);
104 KiDispatchThreadNoLock(ULONG NewThreadStatus
)
106 KPRIORITY CurrentPriority
;
109 PKTHREAD CurrentThread
= KeGetCurrentThread();
111 DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
112 CurrentThread
, NewThreadStatus
, CurrentThread
->State
);
114 CurrentThread
->State
= (UCHAR
)NewThreadStatus
;
116 if (NewThreadStatus
== Ready
) {
118 KiInsertIntoThreadList(CurrentThread
->Priority
,
122 Affinity
= 1 << KeGetCurrentProcessorNumber();
124 for (CurrentPriority
= HIGH_PRIORITY
; CurrentPriority
>= LOW_PRIORITY
; CurrentPriority
--) {
126 Candidate
= KiScanThreadList(CurrentPriority
, Affinity
);
128 if (Candidate
== CurrentThread
) {
130 Candidate
->State
= Running
;
131 KeReleaseDispatcherDatabaseLockFromDpcLevel();
135 if (Candidate
!= NULL
) {
140 DPRINT("Scheduling %x(%d)\n",Candidate
, CurrentPriority
);
142 Candidate
->State
= Running
;
144 OldThread
= CurrentThread
;
145 CurrentThread
= Candidate
;
146 IdleThread
= KeGetCurrentPrcb()->IdleThread
;
148 if (OldThread
== IdleThread
) {
150 IdleProcessorMask
&= ~Affinity
;
152 } else if (CurrentThread
== IdleThread
) {
154 IdleProcessorMask
|= Affinity
;
157 MmUpdatePageDir(PsGetCurrentProcess(),((PETHREAD
)CurrentThread
)->ThreadsProcess
, sizeof(EPROCESS
));
159 /* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */
160 DPRINT("You are : %x, swapping to: %x\n", OldThread
, CurrentThread
);
161 KiArchContextSwitch(CurrentThread
);
162 DPRINT("You are : %x, swapped from: %x\n", OldThread
, CurrentThread
);
167 DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber());
175 PKTHREAD CurrentThread
= KeGetCurrentThread();
177 /* Find a new thread to run */
178 DPRINT("Dispatching Thread as blocked\n");
179 KiDispatchThreadNoLock(Waiting
);
181 /* Lower IRQL back */
182 DPRINT("Lowering IRQL \n");
183 KfLowerIrql(CurrentThread
->WaitIrql
);
185 /* Return the wait status */
186 return CurrentThread
->WaitStatus
;
191 KiDispatchThread(ULONG NewThreadStatus
)
195 if (KeGetCurrentPrcb()->IdleThread
== NULL
) {
199 OldIrql
= KeAcquireDispatcherDatabaseLock();
200 KiDispatchThreadNoLock(NewThreadStatus
);
201 KeLowerIrql(OldIrql
);
206 KiUnblockThread(PKTHREAD Thread
,
207 PNTSTATUS WaitStatus
,
210 if (Terminated
== Thread
->State
) {
212 DPRINT("Can't unblock thread 0x%x because it's terminating\n",
215 } else if (Ready
== Thread
->State
||
216 Running
== Thread
->State
) {
218 DPRINT("Can't unblock thread 0x%x because it's %s\n",
219 Thread
, (Thread
->State
== Ready
? "ready" : "running"));
226 /* FIXME: This propably isn't the right way to do it... */
227 /* No it's not... i'll fix it later-- Alex */
228 if (Thread
->Priority
< LOW_REALTIME_PRIORITY
&&
229 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2) {
231 if (!Thread
->PriorityDecrement
&& !Thread
->DisableBoost
) {
233 Thread
->Priority
= Thread
->BasePriority
+ Increment
;
234 Thread
->PriorityDecrement
= Increment
;
237 /* Also decrease quantum */
242 Thread
->Quantum
= Thread
->QuantumReset
;
245 if (WaitStatus
!= NULL
) {
247 Thread
->WaitStatus
= *WaitStatus
;
250 Thread
->State
= Ready
;
251 KiInsertIntoThreadList(Thread
->Priority
, Thread
);
252 Processor
= KeGetCurrentProcessorNumber();
253 Affinity
= Thread
->Affinity
;
255 if (!(IdleProcessorMask
& (1 << Processor
) & Affinity
) &&
256 (IdleProcessorMask
& ~(1 << Processor
) & Affinity
)) {
260 for (i
= 0; i
< KeNumberProcessors
- 1; i
++) {
264 if (Processor
>= KeNumberProcessors
) {
269 if (IdleProcessorMask
& (1 << Processor
) & Affinity
) {
272 * Reschedule the threads on an other processor
274 KeReleaseDispatcherDatabaseLockFromDpcLevel();
275 KiRequestReschedule(Processor
);
276 KeAcquireDispatcherDatabaseLockAtDpcLevel();
287 KiAdjustQuantumThread(IN PKTHREAD Thread
)
291 /* Don't adjust for RT threads */
292 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
293 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2)
295 /* Decrease Quantum by one and see if we've ran out */
296 if (--Thread
->Quantum
<= 0)
299 Thread
->Quantum
= Thread
->QuantumReset
;
301 /* Calculate new Priority */
302 Priority
= Thread
->Priority
- (Thread
->PriorityDecrement
+ 1);
304 /* Normalize it if we've gone too low */
305 if (Priority
< Thread
->BasePriority
) Priority
= Thread
->BasePriority
;
307 /* Reset the priority decrement, we've done it */
308 Thread
->PriorityDecrement
= 0;
310 /* Set the new priority, if needed */
311 if (Priority
!= Thread
->Priority
)
314 * FIXME: This should be a call to KiSetPriorityThread but
315 * due to the current ""scheduler"" in ROS, it can't be done
316 * cleanly since it actualyl dispatches threads instead.
318 Thread
->Priority
= Priority
;
322 /* FIXME: Priority hasn't changed, find a new thread */
327 /* Nothing to do... */
334 KiSuspendThreadKernelRoutine(PKAPC Apc
,
335 PKNORMAL_ROUTINE
* NormalRoutine
,
336 PVOID
* NormalContext
,
337 PVOID
* SystemArgument1
,
338 PVOID
* SystemArguemnt2
)
344 KiSuspendThreadNormalRoutine(PVOID NormalContext
,
345 PVOID SystemArgument1
,
346 PVOID SystemArgument2
)
348 PKTHREAD CurrentThread
= KeGetCurrentThread();
350 /* Non-alertable kernel-mode suspended wait */
351 DPRINT("Waiting...\n");
352 KeWaitForSingleObject(&CurrentThread
->SuspendSemaphore
,
357 DPRINT("Done Waiting\n");
360 #ifdef KeGetCurrentThread
361 #undef KeGetCurrentThread
368 KeGetCurrentThread(VOID
)
373 Ke386SaveFlags(Flags
);
374 Ke386DisableInterrupts();
375 Thread
= KeGetCurrentPrcb()->CurrentThread
;
376 Ke386RestoreFlags(Flags
);
379 return(KeGetCurrentPrcb()->CurrentThread
);
385 KeSetPreviousMode(ULONG Mode
)
387 PsGetCurrentThread()->Tcb
.PreviousMode
= (UCHAR
)Mode
;
395 KeGetPreviousMode(VOID
)
397 return (ULONG
)PsGetCurrentThread()->Tcb
.PreviousMode
;
402 KeDisableThreadApcQueueing(IN PKTHREAD Thread
)
405 BOOLEAN PreviousState
;
407 /* Lock the Dispatcher Database */
408 OldIrql
= KeAcquireDispatcherDatabaseLock();
411 PreviousState
= Thread
->ApcQueueable
;
414 Thread
->ApcQueueable
= FALSE
;
416 /* Release the Lock */
417 KeReleaseDispatcherDatabaseLock(OldIrql
);
419 /* Return old state */
420 return PreviousState
;
425 KeRundownThread(VOID
)
428 PKTHREAD Thread
= KeGetCurrentThread();
429 PLIST_ENTRY NextEntry
, ListHead
;
431 DPRINT("KeRundownThread: %x\n", Thread
);
433 /* Optimized path if nothing is on the list at the moment */
434 if (IsListEmpty(&Thread
->MutantListHead
)) return;
436 /* Lock the Dispatcher Database */
437 OldIrql
= KeAcquireDispatcherDatabaseLock();
439 /* Get the List Pointers */
440 ListHead
= &Thread
->MutantListHead
;
441 NextEntry
= ListHead
->Flink
;
442 while (NextEntry
!= ListHead
)
445 Mutant
= CONTAINING_RECORD(NextEntry
, KMUTANT
, MutantListEntry
);
446 DPRINT1("Mutant: %p. Type, Size %x %x\n",
449 Mutant
->Header
.Size
);
451 /* Make sure it's not terminating with APCs off */
452 if (Mutant
->ApcDisable
)
454 /* Bugcheck the system */
455 KEBUGCHECKEX(0,//THREAD_TERMINATE_HELD_MUTEX,
462 /* Now we can remove it */
463 RemoveEntryList(&Mutant
->MutantListEntry
);
465 /* Unconditionally abandon it */
466 DPRINT("Abandonning the Mutant\n");
467 Mutant
->Header
.SignalState
= 1;
468 Mutant
->Abandoned
= TRUE
;
469 Mutant
->OwnerThread
= NULL
;
471 /* Check if the Wait List isn't empty */
472 DPRINT("Checking whether to wake the Mutant\n");
473 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
))
475 /* Wake the Mutant */
476 DPRINT("Waking the Mutant\n");
477 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
481 NextEntry
= NextEntry
->Flink
;
484 /* Release the Lock */
485 KeReleaseDispatcherDatabaseLock(OldIrql
);
490 KeResumeThread(PKTHREAD Thread
)
495 DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread
,
496 Thread
->SuspendCount
, Thread
->FreezeCount
);
498 /* Lock the Dispatcher */
499 OldIrql
= KeAcquireDispatcherDatabaseLock();
501 /* Save the Old Count */
502 PreviousCount
= Thread
->SuspendCount
;
504 /* Check if it existed */
507 Thread
->SuspendCount
--;
509 /* Decrease the current Suspend Count and Check Freeze Count */
510 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
)) {
512 /* Signal the Suspend Semaphore */
513 Thread
->SuspendSemaphore
.Header
.SignalState
++;
514 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
518 /* Release Lock and return the Old State */
519 KeReleaseDispatcherDatabaseLock(OldIrql
);
520 return PreviousCount
;
525 KiInsertQueueApc(PKAPC Apc
,
526 KPRIORITY PriorityBoost
);
529 * Used by the debugging code to freeze all the process's threads
530 * while the debugger is examining their state.
534 KeFreezeAllThreads(PKPROCESS Process
)
538 PKTHREAD CurrentThread
= KeGetCurrentThread();
541 OldIrql
= KeAcquireDispatcherDatabaseLock();
543 /* If someone is already trying to free us, try again */
544 while (CurrentThread
->FreezeCount
)
546 /* Release and re-acquire the lock so the APC will go through */
547 KeReleaseDispatcherDatabaseLock(OldIrql
);
548 OldIrql
= KeAcquireDispatcherDatabaseLock();
551 /* Enter a critical region */
552 KeEnterCriticalRegion();
554 /* Loop the Process's Threads */
555 LIST_FOR_EACH(Current
, &Process
->ThreadListHead
, KTHREAD
, ThreadListEntry
)
557 /* Make sure it's not ours */
558 if (Current
!= CurrentThread
)
560 /* Should be bother inserting the APC? */
561 if (Current
->ApcQueueable
)
563 /* Make sure it wasn't already frozen, and that it's not suspended */
564 if (!(++Current
->FreezeCount
) && !(Current
->SuspendCount
))
566 /* Did we already insert it? */
567 if (!Current
->SuspendApc
.Inserted
)
570 Current
->SuspendApc
.Inserted
= TRUE
;
571 KiInsertQueueApc(&Current
->SuspendApc
, IO_NO_INCREMENT
);
575 /* Unsignal the Semaphore, the APC already got inserted */
576 Current
->SuspendSemaphore
.Header
.SignalState
--;
583 /* Release the lock */
584 KeReleaseDispatcherDatabaseLock(OldIrql
);
589 KeSuspendThread(PKTHREAD Thread
)
594 DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread
, Thread
->SuspendCount
, Thread
->FreezeCount
);
596 /* Lock the Dispatcher */
597 OldIrql
= KeAcquireDispatcherDatabaseLock();
599 /* Save the Old Count */
600 PreviousCount
= Thread
->SuspendCount
;
602 /* Handle the maximum */
603 if (PreviousCount
== MAXIMUM_SUSPEND_COUNT
)
605 /* Raise an exception */
606 KeReleaseDispatcherDatabaseLock(OldIrql
);
607 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED
);
610 /* Should we bother to queue at all? */
611 if (Thread
->ApcQueueable
)
613 /* Increment the suspend count */
614 Thread
->SuspendCount
++;
616 /* Check if we should suspend it */
617 if (!PreviousCount
&& !Thread
->FreezeCount
)
619 /* Is the APC already inserted? */
620 if (!Thread
->SuspendApc
.Inserted
)
622 /* Not inserted, insert it */
623 Thread
->SuspendApc
.Inserted
= TRUE
;
624 KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
);
628 /* Unsignal the Semaphore, the APC already got inserted */
629 Thread
->SuspendSemaphore
.Header
.SignalState
--;
634 /* Release Lock and return the Old State */
635 KeReleaseDispatcherDatabaseLock(OldIrql
);
636 return PreviousCount
;
641 KeForceResumeThread(IN PKTHREAD Thread
)
646 /* Lock the Dispatcher Database and the APC Queue */
647 OldIrql
= KeAcquireDispatcherDatabaseLock();
649 /* Save the old Suspend Count */
650 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
652 /* If the thread is suspended, wake it up!!! */
655 /* Unwait it completely */
656 Thread
->SuspendCount
= 0;
657 Thread
->FreezeCount
= 0;
659 /* Signal and satisfy */
660 Thread
->SuspendSemaphore
.Header
.SignalState
++;
661 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
664 /* Release Lock and return the Old State */
665 KeReleaseDispatcherDatabaseLock(OldIrql
);
666 return PreviousCount
;
671 KeAlertResumeThread(IN PKTHREAD Thread
)
676 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
678 /* Lock the Dispatcher Database and the APC Queue */
679 OldIrql
= KeAcquireDispatcherDatabaseLock();
680 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
682 /* Return if Thread is already alerted. */
683 if (Thread
->Alerted
[KernelMode
] == FALSE
) {
685 /* If it's Blocked, unblock if it we should */
686 if (Thread
->State
== Waiting
&& Thread
->Alertable
) {
688 DPRINT("Aborting Wait\n");
689 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
693 /* If not, simply Alert it */
694 Thread
->Alerted
[KernelMode
] = TRUE
;
698 /* Save the old Suspend Count */
699 PreviousCount
= Thread
->SuspendCount
;
701 /* If the thread is suspended, decrease one of the suspend counts */
704 /* Decrease count. If we are now zero, unwait it completely */
705 if (--Thread
->SuspendCount
) {
707 /* Signal and satisfy */
708 Thread
->SuspendSemaphore
.Header
.SignalState
++;
709 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
713 /* Release Locks and return the Old State */
714 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
715 KeReleaseDispatcherDatabaseLock(OldIrql
);
716 return PreviousCount
;
721 KeAlertThread(PKTHREAD Thread
,
722 KPROCESSOR_MODE AlertMode
)
725 BOOLEAN PreviousState
;
727 /* Acquire the Dispatcher Database Lock */
728 OldIrql
= KeAcquireDispatcherDatabaseLock();
730 /* Save the Previous State */
731 PreviousState
= Thread
->Alerted
[AlertMode
];
733 /* Return if Thread is already alerted. */
734 if (PreviousState
== FALSE
) {
736 /* If it's Blocked, unblock if it we should */
737 if (Thread
->State
== Waiting
&&
738 (AlertMode
== KernelMode
|| Thread
->WaitMode
== AlertMode
) &&
741 DPRINT("Aborting Wait\n");
742 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
746 /* If not, simply Alert it */
747 Thread
->Alerted
[AlertMode
] = TRUE
;
751 /* Release the Dispatcher Lock */
752 KeReleaseDispatcherDatabaseLock(OldIrql
);
754 /* Return the old state */
755 return PreviousState
;
763 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
769 IN PVOID ThreadState
)
775 * FUNCTION: Initialize the microkernel state of the thread
779 KeInitializeThread(PKPROCESS Process
,
781 PKSYSTEM_ROUTINE SystemRoutine
,
782 PKSTART_ROUTINE StartRoutine
,
789 PKWAIT_BLOCK TimerWaitBlock
;
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 /* Initialize the wait blocks */
807 for (i
= 0; i
< (THREAD_WAIT_OBJECTS
+ 1); i
++)
809 /* Put our pointer */
810 Thread
->WaitBlock
[i
].Thread
= Thread
;
813 /* Setup the Service Descriptor Table for Native Calls */
814 Thread
->ServiceTable
= KeServiceDescriptorTable
;
816 /* Setup APC Fields */
817 InitializeListHead(&Thread
->ApcState
.ApcListHead
[0]);
818 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
819 Thread
->ApcState
.Process
= Process
;
820 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
821 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
822 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
823 Thread
->ApcQueueable
= TRUE
;
824 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
826 /* Initialize the Suspend APC */
827 KeInitializeApc(&Thread
->SuspendApc
,
829 OriginalApcEnvironment
,
830 KiSuspendThreadKernelRoutine
,
832 KiSuspendThreadNormalRoutine
,
836 /* Initialize the Suspend Semaphore */
837 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 128);
839 /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
840 Timer
= &Thread
->Timer
;
841 KeInitializeTimer(Timer
);
842 TimerWaitBlock
= &Thread
->WaitBlock
[TIMER_WAIT_BLOCK
];
843 TimerWaitBlock
->Object
= Timer
;
844 TimerWaitBlock
->WaitKey
= STATUS_TIMEOUT
;
845 TimerWaitBlock
->WaitType
= WaitAny
;
846 TimerWaitBlock
->NextWaitBlock
= NULL
;
848 /* Link the two wait lists together */
849 TimerWaitBlock
->WaitListEntry
.Flink
= &Timer
->Header
.WaitListHead
;
850 TimerWaitBlock
->WaitListEntry
.Blink
= &Timer
->Header
.WaitListHead
;
855 /* Set the Thread Stacks */
856 Thread
->InitialStack
= (PCHAR
)KernelStack
+ KERNEL_STACK_SIZE
;
857 Thread
->StackBase
= (PCHAR
)KernelStack
+ KERNEL_STACK_SIZE
;
858 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
;
859 Thread
->KernelStackResident
= TRUE
;
862 * Establish the pde's for the new stack and the thread structure within the
863 * address space of the new process. They are accessed while taskswitching or
864 * while handling page faults. At this point it isn't possible to call the
865 * page fault handler for the missing pde's.
867 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
->StackLimit
, KERNEL_STACK_SIZE
);
868 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
870 /* Initalize the Thread Context */
871 DPRINT("Initializing the Context for the thread: %x\n", Thread
);
872 KiArchInitThreadWithContext(Thread
,
878 /* Setup scheduler Fields based on Parent */
879 DPRINT("Thread context created, setting Scheduler Data\n");
880 Thread
->BasePriority
= Process
->BasePriority
;
881 Thread
->Quantum
= Process
->QuantumReset
;
882 Thread
->QuantumReset
= Process
->QuantumReset
;
883 Thread
->Affinity
= Process
->Affinity
;
884 Thread
->Priority
= Process
->BasePriority
;
885 Thread
->UserAffinity
= Process
->Affinity
;
886 Thread
->DisableBoost
= Process
->DisableBoost
;
887 Thread
->AutoAlignment
= Process
->AutoAlignment
;
888 Thread
->Iopl
= Process
->Iopl
;
890 /* Set the Thread to initalized */
891 Thread
->State
= Initialized
;
894 * Insert the Thread into the Process's Thread List
895 * Note, this is the KTHREAD Thread List. It is removed in
896 * ke/kthread.c!KeTerminateThread.
898 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
899 DPRINT("Thread initalized\n");
908 KeQueryPriorityThread (IN PKTHREAD Thread
)
910 return Thread
->Priority
;
918 KeQueryRuntimeThread(IN PKTHREAD Thread
,
921 /* Return the User Time */
922 *UserTime
= Thread
->UserTime
;
924 /* Return the Kernel Time */
925 return Thread
->KernelTime
;
933 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
935 PKTHREAD Thread
= KeGetCurrentThread();
936 BOOLEAN PreviousState
;
939 /* Lock the Dispatcher Database */
940 OldIrql
= KeAcquireDispatcherDatabaseLock();
943 PreviousState
= Thread
->EnableStackSwap
;
946 Thread
->EnableStackSwap
= Enable
;
948 /* No, Release Lock */
949 KeReleaseDispatcherDatabaseLock(OldIrql
);
951 /* Return Old State */
952 return PreviousState
;
960 KeRevertToUserAffinityThread(VOID
)
962 PKTHREAD CurrentThread
= KeGetCurrentThread();
965 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
967 /* Lock the Dispatcher Database */
968 OldIrql
= KeAcquireDispatcherDatabaseLock();
970 /* Return to User Affinity */
971 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
973 /* Disable System Affinity */
974 CurrentThread
->SystemAffinityActive
= FALSE
;
976 /* Check if we need to Dispatch a New thread */
977 if (CurrentThread
->Affinity
& (1 << KeGetCurrentProcessorNumber())) {
979 /* No, just release */
980 KeReleaseDispatcherDatabaseLock(OldIrql
);
984 /* We need to dispatch a new thread */
985 CurrentThread
->WaitIrql
= OldIrql
;
986 KiDispatchThreadNoLock(Ready
);
987 KeLowerIrql(OldIrql
);
996 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
999 CCHAR PreviousIdealProcessor
;
1002 /* Lock the Dispatcher Database */
1003 OldIrql
= KeAcquireDispatcherDatabaseLock();
1005 /* Save Old Ideal Processor */
1006 PreviousIdealProcessor
= Thread
->IdealProcessor
;
1008 /* Set New Ideal Processor */
1009 Thread
->IdealProcessor
= Processor
;
1012 KeReleaseDispatcherDatabaseLock(OldIrql
);
1014 /* Return Old Ideal Processor */
1015 return PreviousIdealProcessor
;
1023 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
1025 PKTHREAD CurrentThread
= KeGetCurrentThread();
1028 ASSERT(Affinity
& ((1 << KeNumberProcessors
) - 1));
1030 /* Lock the Dispatcher Database */
1031 OldIrql
= KeAcquireDispatcherDatabaseLock();
1033 /* Set the System Affinity Specified */
1034 CurrentThread
->Affinity
= Affinity
;
1036 /* Enable System Affinity */
1037 CurrentThread
->SystemAffinityActive
= TRUE
;
1039 /* Check if we need to Dispatch a New thread */
1040 if (Affinity
& (1 << KeGetCurrentProcessorNumber())) {
1042 /* No, just release */
1043 KeReleaseDispatcherDatabaseLock(OldIrql
);
1047 /* We need to dispatch a new thread */
1048 CurrentThread
->WaitIrql
= OldIrql
;
1049 KiDispatchThreadNoLock(Ready
);
1050 KeLowerIrql(OldIrql
);
1056 KeQueryBasePriorityThread(IN PKTHREAD Thread
)
1058 LONG BasePriorityIncrement
;
1062 /* Lock the Dispatcher Database */
1063 OldIrql
= KeAcquireDispatcherDatabaseLock();
1065 /* Get the Process */
1066 Process
= Thread
->ApcStatePointer
[0]->Process
;
1068 /* Calculate the BPI */
1069 BasePriorityIncrement
= Thread
->BasePriority
- Process
->BasePriority
;
1071 /* If saturation occured, return the SI instead */
1072 if (Thread
->Saturation
) BasePriorityIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1076 KeReleaseDispatcherDatabaseLock(OldIrql
);
1078 /* Return Increment */
1079 return BasePriorityIncrement
;
1084 KiSetPriorityThread(PKTHREAD Thread
,
1088 KPRIORITY OldPriority
= Thread
->Priority
;
1092 DPRINT("Changing prio to : %lx\n", Priority
);
1094 /* Check if priority changed */
1095 if (OldPriority
!= Priority
)
1098 Thread
->Priority
= Priority
;
1100 /* Choose action based on thread's state */
1101 if (Thread
->State
== Ready
)
1103 /* Remove it from the current queue */
1104 KiRemoveFromThreadList(Thread
);
1106 /* Re-insert it at its current priority */
1107 KiInsertIntoThreadList(Priority
, Thread
);
1109 /* Check if the old priority was lower */
1110 if (KeGetCurrentThread()->Priority
< Priority
)
1112 /* Dispatch it immediately */
1113 KiDispatchThreadNoLock(Ready
);
1118 else if (Thread
->State
== Running
)
1120 /* Check if the new priority is lower */
1121 if (Priority
< OldPriority
)
1123 /* Check for threads with a higher priority */
1124 Mask
= ~((1 << (Priority
+ 1)) - 1);
1125 if (PriorityListMask
& Mask
)
1127 /* Found a thread, is it us? */
1128 if (Thread
== KeGetCurrentThread())
1131 KiDispatchThreadNoLock(Ready
);
1137 /* Loop every CPU */
1138 for (i
= 0; i
< KeNumberProcessors
; i
++)
1140 /* Get the PCR for this CPU */
1141 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1143 /* Reschedule if the new one is already on a CPU */
1144 if (Pcr
->Prcb
->CurrentThread
== Thread
)
1146 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1147 KiRequestReschedule(i
);
1158 /* Return to caller */
1167 KeSetBasePriorityThread(PKTHREAD Thread
,
1173 KPRIORITY CurrentBasePriority
;
1174 KPRIORITY BasePriority
;
1175 BOOLEAN Released
= FALSE
;
1176 LONG CurrentIncrement
;
1178 /* Lock the Dispatcher Database */
1179 OldIrql
= KeAcquireDispatcherDatabaseLock();
1181 /* Get the process and calculate current BP and BPI */
1182 Process
= Thread
->ApcStatePointer
[0]->Process
;
1183 CurrentBasePriority
= Thread
->BasePriority
;
1184 CurrentIncrement
= CurrentBasePriority
- Process
->BasePriority
;
1186 /* Change to use the SI if Saturation was used */
1187 if (Thread
->Saturation
) CurrentIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1190 /* Now check if saturation is being used for the new value */
1191 if (abs(Increment
) >= ((HIGH_PRIORITY
+ 1) / 2))
1193 /* Check if we need positive or negative saturation */
1194 Thread
->Saturation
= (Increment
> 0) ? 1 : -1;
1197 /* Normalize the Base Priority */
1198 BasePriority
= Process
->BasePriority
+ Increment
;
1199 if (Process
->BasePriority
>= LOW_REALTIME_PRIORITY
)
1201 /* Check if it's too low */
1202 if (BasePriority
< LOW_REALTIME_PRIORITY
)
1203 BasePriority
= LOW_REALTIME_PRIORITY
;
1205 /* Check if it's too high */
1206 if (BasePriority
> HIGH_PRIORITY
) BasePriority
= HIGH_PRIORITY
;
1208 /* We are at RTP, so use the raw BP */
1209 Priority
= BasePriority
;
1213 /* Check if it's entering RTP */
1214 if (BasePriority
>= LOW_REALTIME_PRIORITY
)
1215 BasePriority
= LOW_REALTIME_PRIORITY
- 1;
1217 /* Check if it's too low */
1218 if (BasePriority
<= LOW_PRIORITY
)
1221 /* If Saturation is used, then use the raw BP */
1222 if (Thread
->Saturation
)
1224 Priority
= BasePriority
;
1228 /* Calculate the new priority */
1229 Priority
= Thread
->Priority
+ (BasePriority
- CurrentBasePriority
)-
1230 Thread
->PriorityDecrement
;
1232 /* Make sure it won't enter RTP ranges */
1233 if (Priority
>= LOW_REALTIME_PRIORITY
)
1234 Priority
= LOW_REALTIME_PRIORITY
- 1;
1238 /* Finally set the new base priority */
1239 Thread
->BasePriority
= BasePriority
;
1241 /* Reset the decrements */
1242 Thread
->PriorityDecrement
= 0;
1244 /* If the priority will change, reset quantum and change it for real */
1245 if (Priority
!= Thread
->Priority
)
1247 Thread
->Quantum
= Thread
->QuantumReset
;
1248 KiSetPriorityThread(Thread
, Priority
, &Released
);
1251 /* Release Lock if needed */
1254 KeReleaseDispatcherDatabaseLock(OldIrql
);
1258 KeLowerIrql(OldIrql
);
1261 /* Return the Old Increment */
1262 return CurrentIncrement
;
1270 KeSetPriorityThread(PKTHREAD Thread
,
1273 KPRIORITY OldPriority
;
1274 BOOLEAN Released
= FALSE
;
1277 /* Lock the Dispatcher Database */
1278 OldIrql
= KeAcquireDispatcherDatabaseLock();
1280 /* Save the old Priority */
1281 OldPriority
= Thread
->Priority
;
1283 /* Reset the Quantum and Decrements */
1284 Thread
->Quantum
= Thread
->QuantumReset
;
1285 Thread
->PriorityDecrement
= 0;
1287 /* Set the new Priority */
1288 KiSetPriorityThread(Thread
, Priority
, &Released
);
1290 /* Release Lock if needed */
1293 KeReleaseDispatcherDatabaseLock(OldIrql
);
1297 KeLowerIrql(OldIrql
);
1300 /* Return Old Priority */
1307 * Sets thread's affinity
1311 KeSetAffinityThread(PKTHREAD Thread
,
1317 KAFFINITY ProcessorMask
;
1319 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread
, Affinity
);
1321 /* Verify correct affinity */
1322 if ((Affinity
& Thread
->ApcStatePointer
[0]->Process
->Affinity
) !=
1323 Affinity
|| !Affinity
)
1325 KEBUGCHECK(INVALID_AFFINITY_SET
);
1328 OldIrql
= KeAcquireDispatcherDatabaseLock();
1330 Thread
->UserAffinity
= Affinity
;
1332 if (Thread
->SystemAffinityActive
== FALSE
) {
1334 Thread
->Affinity
= Affinity
;
1336 if (Thread
->State
== Running
) {
1338 ProcessorMask
= 1 << KeGetCurrentProcessorNumber();
1339 if (Thread
== KeGetCurrentThread()) {
1341 if (!(Affinity
& ProcessorMask
)) {
1343 KiDispatchThreadNoLock(Ready
);
1344 KeLowerIrql(OldIrql
);
1345 return STATUS_SUCCESS
;
1350 for (i
= 0; i
< KeNumberProcessors
; i
++) {
1352 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1353 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
1355 if (!(Affinity
& ProcessorMask
)) {
1357 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1358 KiRequestReschedule(i
);
1359 KeLowerIrql(OldIrql
);
1360 return STATUS_SUCCESS
;
1367 ASSERT (i
< KeNumberProcessors
);
1372 KeReleaseDispatcherDatabaseLock(OldIrql
);
1373 return STATUS_SUCCESS
;
1379 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1382 KeTerminateThread(IN KPRIORITY Increment
)
1385 PKTHREAD Thread
= KeGetCurrentThread();
1387 /* Lock the Dispatcher Database and the APC Queue */
1388 DPRINT("Terminating\n");
1389 OldIrql
= KeAcquireDispatcherDatabaseLock();
1391 /* Remove the thread from the list */
1392 RemoveEntryList(&Thread
->ThreadListEntry
);
1394 /* Insert into the Reaper List */
1395 DPRINT("List: %p\n", PspReaperList
);
1396 ((PETHREAD
)Thread
)->ReaperLink
= PspReaperList
;
1397 PspReaperList
= (PETHREAD
)Thread
;
1398 DPRINT("List: %p\n", PspReaperList
);
1400 /* Check if it's active */
1401 if (PspReaping
== FALSE
) {
1403 /* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */
1405 DPRINT("Terminating\n");
1406 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1407 &PspReaperWorkItem
.List
,
1411 /* Handle Kernel Queues */
1412 if (Thread
->Queue
) {
1414 DPRINT("Waking Queue\n");
1415 RemoveEntryList(&Thread
->QueueListEntry
);
1416 KiWakeQueue(Thread
->Queue
);
1419 /* Signal the thread */
1420 Thread
->DispatcherHeader
.SignalState
= TRUE
;
1421 if (IsListEmpty(&Thread
->DispatcherHeader
.WaitListHead
) != TRUE
) {
1424 KiWaitTest((PVOID
)Thread
, Increment
);
1427 /* Find a new Thread */
1428 KiDispatchThreadNoLock(Terminated
);
1432 * FUNCTION: Tests whether there are any pending APCs for the current thread
1433 * and if so the APCs will be delivered on exit from kernel mode
1437 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
1440 PKTHREAD Thread
= KeGetCurrentThread();
1443 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1445 /* Lock the Dispatcher Database and the APC Queue */
1446 OldIrql
= KeAcquireDispatcherDatabaseLock();
1447 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
1449 /* Save the old State */
1450 OldState
= Thread
->Alerted
[AlertMode
];
1452 /* If the Thread is Alerted, Clear it */
1455 Thread
->Alerted
[AlertMode
] = FALSE
;
1457 } else if ((AlertMode
!= KernelMode
) && (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))) {
1459 /* If the mode is User and the Queue isn't empty, set Pending */
1460 Thread
->ApcState
.UserApcPending
= TRUE
;
1463 /* Release Locks and return the Old State */
1464 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
1465 KeReleaseDispatcherDatabaseLock(OldIrql
);
1475 NtAlertResumeThread(IN HANDLE ThreadHandle
,
1476 OUT PULONG SuspendCount
)
1478 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1481 ULONG PreviousState
;
1483 /* Check if parameters are valid */
1484 if(PreviousMode
!= KernelMode
) {
1488 ProbeForWriteUlong(SuspendCount
);
1492 Status
= _SEH_GetExceptionCode();
1497 /* Reference the Object */
1498 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1499 THREAD_SUSPEND_RESUME
,
1505 /* Check for Success */
1506 if (NT_SUCCESS(Status
)) {
1508 /* Call the Kernel Function */
1509 PreviousState
= KeAlertResumeThread(&Thread
->Tcb
);
1511 /* Dereference Object */
1512 ObDereferenceObject(Thread
);
1518 *SuspendCount
= PreviousState
;
1522 Status
= _SEH_GetExceptionCode();
1539 NtAlertThread (IN HANDLE ThreadHandle
)
1541 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1545 /* Reference the Object */
1546 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1547 THREAD_SUSPEND_RESUME
,
1553 /* Check for Success */
1554 if (NT_SUCCESS(Status
)) {
1557 * Do an alert depending on the processor mode. If some kmode code wants to
1558 * enforce a umode alert it should call KeAlertThread() directly. If kmode
1559 * code wants to do a kmode alert it's sufficient to call it with Zw or just
1560 * use KeAlertThread() directly
1562 KeAlertThread(&Thread
->Tcb
, PreviousMode
);
1564 /* Dereference Object */
1565 ObDereferenceObject(Thread
);
1574 NtDelayExecution(IN BOOLEAN Alertable
,
1575 IN PLARGE_INTEGER DelayInterval
)
1577 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1578 LARGE_INTEGER SafeInterval
;
1581 /* Check if parameters are valid */
1582 if(PreviousMode
!= KernelMode
) {
1584 Status
= STATUS_SUCCESS
;
1588 /* make a copy on the kernel stack and let DelayInterval point to it so
1589 we don't need to wrap KeDelayExecutionThread in SEH! */
1590 SafeInterval
= ProbeForReadLargeInteger(DelayInterval
);
1591 DelayInterval
= &SafeInterval
;
1595 Status
= _SEH_GetExceptionCode();
1598 if (!NT_SUCCESS(Status
))
1604 /* Call the Kernel Function */
1605 Status
= KeDelayExecutionThread(PreviousMode
,