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());
177 KiBlockThread(PNTSTATUS Status
,
182 PKTHREAD Thread
= KeGetCurrentThread();
183 PKWAIT_BLOCK WaitBlock
;
185 if (Thread
->ApcState
.KernelApcPending
) {
187 DPRINT("Dispatching Thread as ready (APC!)\n");
190 WaitBlock
= Thread
->WaitBlockList
;
192 RemoveEntryList (&WaitBlock
->WaitListEntry
);
193 WaitBlock
= WaitBlock
->NextWaitBlock
;
194 } while (WaitBlock
!= Thread
->WaitBlockList
);
195 Thread
->WaitBlockList
= NULL
;
197 /* Dispatch it and return status */
198 KiDispatchThreadNoLock (Ready
);
199 if (Status
!= NULL
) *Status
= STATUS_KERNEL_APC
;
203 /* Set the Thread Data as Requested */
204 DPRINT("Dispatching Thread as blocked: %d\n", Thread
->WaitStatus
);
205 Thread
->Alertable
= Alertable
;
206 Thread
->WaitMode
= (UCHAR
)WaitMode
;
207 Thread
->WaitReason
= WaitReason
;
209 /* Dispatch it and return status */
210 KiDispatchThreadNoLock(Waiting
);
211 DPRINT("Dispatching Thread as blocked: %d\n", Thread
->WaitStatus
);
212 if (Status
!= NULL
) *Status
= Thread
->WaitStatus
;
215 DPRINT("Releasing Dispatcher Lock\n");
216 KfLowerIrql(Thread
->WaitIrql
);
221 KiDispatchThread(ULONG NewThreadStatus
)
225 if (KeGetCurrentPrcb()->IdleThread
== NULL
) {
229 OldIrql
= KeAcquireDispatcherDatabaseLock();
230 KiDispatchThreadNoLock(NewThreadStatus
);
231 KeLowerIrql(OldIrql
);
236 KiUnblockThread(PKTHREAD Thread
,
237 PNTSTATUS WaitStatus
,
240 if (Terminated
== Thread
->State
) {
242 DPRINT("Can't unblock thread 0x%x because it's terminating\n",
245 } else if (Ready
== Thread
->State
||
246 Running
== Thread
->State
) {
248 DPRINT("Can't unblock thread 0x%x because it's %s\n",
249 Thread
, (Thread
->State
== Ready
? "ready" : "running"));
256 /* FIXME: This propably isn't the right way to do it... */
257 /* No it's not... i'll fix it later-- Alex */
258 if (Thread
->Priority
< LOW_REALTIME_PRIORITY
&&
259 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2) {
261 if (!Thread
->PriorityDecrement
&& !Thread
->DisableBoost
) {
263 Thread
->Priority
= Thread
->BasePriority
+ Increment
;
264 Thread
->PriorityDecrement
= Increment
;
267 /* Also decrease quantum */
272 Thread
->Quantum
= Thread
->QuantumReset
;
275 if (WaitStatus
!= NULL
) {
277 Thread
->WaitStatus
= *WaitStatus
;
280 Thread
->State
= Ready
;
281 KiInsertIntoThreadList(Thread
->Priority
, Thread
);
282 Processor
= KeGetCurrentProcessorNumber();
283 Affinity
= Thread
->Affinity
;
285 if (!(IdleProcessorMask
& (1 << Processor
) & Affinity
) &&
286 (IdleProcessorMask
& ~(1 << Processor
) & Affinity
)) {
290 for (i
= 0; i
< KeNumberProcessors
- 1; i
++) {
294 if (Processor
>= KeNumberProcessors
) {
299 if (IdleProcessorMask
& (1 << Processor
) & Affinity
) {
302 * Reschedule the threads on an other processor
304 KeReleaseDispatcherDatabaseLockFromDpcLevel();
305 KiRequestReschedule(Processor
);
306 KeAcquireDispatcherDatabaseLockAtDpcLevel();
317 KiAdjustQuantumThread(IN PKTHREAD Thread
)
321 /* Don't adjust for RT threads */
322 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
323 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2)
325 /* Decrease Quantum by one and see if we've ran out */
326 if (--Thread
->Quantum
<= 0)
329 Thread
->Quantum
= Thread
->QuantumReset
;
331 /* Calculate new Priority */
332 Priority
= Thread
->Priority
- (Thread
->PriorityDecrement
+ 1);
334 /* Normalize it if we've gone too low */
335 if (Priority
< Thread
->BasePriority
) Priority
= Thread
->BasePriority
;
337 /* Reset the priority decrement, we've done it */
338 Thread
->PriorityDecrement
= 0;
340 /* Set the new priority, if needed */
341 if (Priority
!= Thread
->Priority
)
344 * FIXME: This should be a call to KiSetPriorityThread but
345 * due to the current ""scheduler"" in ROS, it can't be done
346 * cleanly since it actualyl dispatches threads instead.
348 Thread
->Priority
= Priority
;
352 /* FIXME: Priority hasn't changed, find a new thread */
357 /* Nothing to do... */
364 KiSuspendThreadKernelRoutine(PKAPC Apc
,
365 PKNORMAL_ROUTINE
* NormalRoutine
,
366 PVOID
* NormalContext
,
367 PVOID
* SystemArgument1
,
368 PVOID
* SystemArguemnt2
)
374 KiSuspendThreadNormalRoutine(PVOID NormalContext
,
375 PVOID SystemArgument1
,
376 PVOID SystemArgument2
)
378 PKTHREAD CurrentThread
= KeGetCurrentThread();
380 /* Non-alertable kernel-mode suspended wait */
381 DPRINT("Waiting...\n");
382 KeWaitForSingleObject(&CurrentThread
->SuspendSemaphore
,
387 DPRINT("Done Waiting\n");
390 #ifdef KeGetCurrentThread
391 #undef KeGetCurrentThread
398 KeGetCurrentThread(VOID
)
403 Ke386SaveFlags(Flags
);
404 Ke386DisableInterrupts();
405 Thread
= KeGetCurrentPrcb()->CurrentThread
;
406 Ke386RestoreFlags(Flags
);
409 return(KeGetCurrentPrcb()->CurrentThread
);
415 KeSetPreviousMode(ULONG Mode
)
417 PsGetCurrentThread()->Tcb
.PreviousMode
= (UCHAR
)Mode
;
425 KeGetPreviousMode(VOID
)
427 return (ULONG
)PsGetCurrentThread()->Tcb
.PreviousMode
;
432 KeDisableThreadApcQueueing(IN PKTHREAD Thread
)
435 BOOLEAN PreviousState
;
437 /* Lock the Dispatcher Database */
438 OldIrql
= KeAcquireDispatcherDatabaseLock();
441 PreviousState
= Thread
->ApcQueueable
;
444 Thread
->ApcQueueable
= FALSE
;
446 /* Release the Lock */
447 KeReleaseDispatcherDatabaseLock(OldIrql
);
449 /* Return old state */
450 return PreviousState
;
455 KeRundownThread(VOID
)
458 PKTHREAD Thread
= KeGetCurrentThread();
459 PLIST_ENTRY NextEntry
, ListHead
;
461 DPRINT("KeRundownThread: %x\n", Thread
);
463 /* Optimized path if nothing is on the list at the moment */
464 if (IsListEmpty(&Thread
->MutantListHead
)) return;
466 /* Lock the Dispatcher Database */
467 OldIrql
= KeAcquireDispatcherDatabaseLock();
469 /* Get the List Pointers */
470 ListHead
= &Thread
->MutantListHead
;
471 NextEntry
= ListHead
->Flink
;
472 while (NextEntry
!= ListHead
)
475 Mutant
= CONTAINING_RECORD(NextEntry
, KMUTANT
, MutantListEntry
);
476 DPRINT1("Mutant: %p. Type, Size %x %x\n", Mutant
, Mutant
->Header
.Type
, Mutant
->Header
.Size
);
478 /* Make sure it's not terminating with APCs off */
479 if (Mutant
->ApcDisable
)
481 /* Bugcheck the system */
482 KEBUGCHECKEX(0,//THREAD_TERMINATE_HELD_MUTEX,
489 /* Now we can remove it */
490 RemoveEntryList(&Mutant
->MutantListEntry
);
492 /* Unconditionally abandon it */
493 DPRINT("Abandonning the Mutant\n");
494 Mutant
->Header
.SignalState
= 1;
495 Mutant
->Abandoned
= TRUE
;
496 Mutant
->OwnerThread
= NULL
;
498 /* Check if the Wait List isn't empty */
499 DPRINT("Checking whether to wake the Mutant\n");
500 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
))
502 /* Wake the Mutant */
503 DPRINT("Waking the Mutant\n");
504 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
508 NextEntry
= NextEntry
->Flink
;
511 /* Release the Lock */
512 KeReleaseDispatcherDatabaseLock(OldIrql
);
517 KeResumeThread(PKTHREAD Thread
)
522 DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread
,
523 Thread
->SuspendCount
, Thread
->FreezeCount
);
525 /* Lock the Dispatcher */
526 OldIrql
= KeAcquireDispatcherDatabaseLock();
528 /* Save the Old Count */
529 PreviousCount
= Thread
->SuspendCount
;
531 /* Check if it existed */
534 Thread
->SuspendCount
--;
536 /* Decrease the current Suspend Count and Check Freeze Count */
537 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
)) {
539 /* Signal the Suspend Semaphore */
540 Thread
->SuspendSemaphore
.Header
.SignalState
++;
541 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
545 /* Release Lock and return the Old State */
546 KeReleaseDispatcherDatabaseLock(OldIrql
);
547 return PreviousCount
;
552 KiInsertQueueApc(PKAPC Apc
,
553 KPRIORITY PriorityBoost
);
556 * Used by the debugging code to freeze all the process's threads
557 * while the debugger is examining their state.
561 KeFreezeAllThreads(PKPROCESS Process
)
565 PKTHREAD CurrentThread
= KeGetCurrentThread();
568 OldIrql
= KeAcquireDispatcherDatabaseLock();
570 /* If someone is already trying to free us, try again */
571 while (CurrentThread
->FreezeCount
)
573 /* Release and re-acquire the lock so the APC will go through */
574 KeReleaseDispatcherDatabaseLock(OldIrql
);
575 OldIrql
= KeAcquireDispatcherDatabaseLock();
578 /* Enter a critical region */
579 KeEnterCriticalRegion();
581 /* Loop the Process's Threads */
582 LIST_FOR_EACH(Current
, &Process
->ThreadListHead
, KTHREAD
, ThreadListEntry
)
584 /* Make sure it's not ours */
585 if (Current
!= CurrentThread
)
587 /* Should be bother inserting the APC? */
588 if (Current
->ApcQueueable
)
590 /* Make sure it wasn't already frozen, and that it's not suspended */
591 if (!(++Current
->FreezeCount
) && !(Current
->SuspendCount
))
593 /* Did we already insert it? */
594 if (!Current
->SuspendApc
.Inserted
)
597 Current
->SuspendApc
.Inserted
= TRUE
;
598 KiInsertQueueApc(&Current
->SuspendApc
, IO_NO_INCREMENT
);
602 /* Unsignal the Semaphore, the APC already got inserted */
603 Current
->SuspendSemaphore
.Header
.SignalState
--;
610 /* Release the lock */
611 KeReleaseDispatcherDatabaseLock(OldIrql
);
616 KeSuspendThread(PKTHREAD Thread
)
621 DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread
, Thread
->SuspendCount
, Thread
->FreezeCount
);
623 /* Lock the Dispatcher */
624 OldIrql
= KeAcquireDispatcherDatabaseLock();
626 /* Save the Old Count */
627 PreviousCount
= Thread
->SuspendCount
;
629 /* Handle the maximum */
630 if (PreviousCount
== MAXIMUM_SUSPEND_COUNT
)
632 /* Raise an exception */
633 KeReleaseDispatcherDatabaseLock(OldIrql
);
634 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED
);
637 /* Should we bother to queue at all? */
638 if (Thread
->ApcQueueable
)
640 /* Increment the suspend count */
641 Thread
->SuspendCount
++;
643 /* Check if we should suspend it */
644 if (!PreviousCount
&& !Thread
->FreezeCount
)
646 /* Is the APC already inserted? */
647 if (!Thread
->SuspendApc
.Inserted
)
649 /* Not inserted, insert it */
650 Thread
->SuspendApc
.Inserted
= TRUE
;
651 KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
);
655 /* Unsignal the Semaphore, the APC already got inserted */
656 Thread
->SuspendSemaphore
.Header
.SignalState
--;
661 /* Release Lock and return the Old State */
662 KeReleaseDispatcherDatabaseLock(OldIrql
);
663 return PreviousCount
;
668 KeForceResumeThread(IN PKTHREAD Thread
)
673 /* Lock the Dispatcher Database and the APC Queue */
674 OldIrql
= KeAcquireDispatcherDatabaseLock();
676 /* Save the old Suspend Count */
677 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
679 /* If the thread is suspended, wake it up!!! */
682 /* Unwait it completely */
683 Thread
->SuspendCount
= 0;
684 Thread
->FreezeCount
= 0;
686 /* Signal and satisfy */
687 Thread
->SuspendSemaphore
.Header
.SignalState
++;
688 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
691 /* Release Lock and return the Old State */
692 KeReleaseDispatcherDatabaseLock(OldIrql
);
693 return PreviousCount
;
698 KeAlertResumeThread(IN PKTHREAD Thread
)
703 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
705 /* Lock the Dispatcher Database and the APC Queue */
706 OldIrql
= KeAcquireDispatcherDatabaseLock();
707 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
709 /* Return if Thread is already alerted. */
710 if (Thread
->Alerted
[KernelMode
] == FALSE
) {
712 /* If it's Blocked, unblock if it we should */
713 if (Thread
->State
== Waiting
&& Thread
->Alertable
) {
715 DPRINT("Aborting Wait\n");
716 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
720 /* If not, simply Alert it */
721 Thread
->Alerted
[KernelMode
] = TRUE
;
725 /* Save the old Suspend Count */
726 PreviousCount
= Thread
->SuspendCount
;
728 /* If the thread is suspended, decrease one of the suspend counts */
731 /* Decrease count. If we are now zero, unwait it completely */
732 if (--Thread
->SuspendCount
) {
734 /* Signal and satisfy */
735 Thread
->SuspendSemaphore
.Header
.SignalState
++;
736 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
740 /* Release Locks and return the Old State */
741 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
742 KeReleaseDispatcherDatabaseLock(OldIrql
);
743 return PreviousCount
;
748 KeAlertThread(PKTHREAD Thread
,
749 KPROCESSOR_MODE AlertMode
)
752 BOOLEAN PreviousState
;
754 /* Acquire the Dispatcher Database Lock */
755 OldIrql
= KeAcquireDispatcherDatabaseLock();
757 /* Save the Previous State */
758 PreviousState
= Thread
->Alerted
[AlertMode
];
760 /* Return if Thread is already alerted. */
761 if (PreviousState
== FALSE
) {
763 /* If it's Blocked, unblock if it we should */
764 if (Thread
->State
== Waiting
&&
765 (AlertMode
== KernelMode
|| Thread
->WaitMode
== AlertMode
) &&
768 DPRINT("Aborting Wait\n");
769 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
773 /* If not, simply Alert it */
774 Thread
->Alerted
[AlertMode
] = TRUE
;
778 /* Release the Dispatcher Lock */
779 KeReleaseDispatcherDatabaseLock(OldIrql
);
781 /* Return the old state */
782 return PreviousState
;
790 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
796 IN PVOID ThreadState
)
802 * FUNCTION: Initialize the microkernel state of the thread
806 KeInitializeThread(PKPROCESS Process
,
808 PKSYSTEM_ROUTINE SystemRoutine
,
809 PKSTART_ROUTINE StartRoutine
,
815 /* Initalize the Dispatcher Header */
816 DPRINT("Initializing Dispatcher Header for New Thread: %x in Process: %x\n", Thread
, Process
);
817 KeInitializeDispatcherHeader(&Thread
->DispatcherHeader
,
822 DPRINT("Thread Header Created. SystemRoutine: %x, StartRoutine: %x with Context: %x\n",
823 SystemRoutine
, StartRoutine
, StartContext
);
824 DPRINT("UserMode Information. Context: %x, Teb: %x\n", Context
, Teb
);
826 /* Initialize the Mutant List */
827 InitializeListHead(&Thread
->MutantListHead
);
829 /* Setup the Service Descriptor Table for Native Calls */
830 Thread
->ServiceTable
= KeServiceDescriptorTable
;
832 /* Setup APC Fields */
833 InitializeListHead(&Thread
->ApcState
.ApcListHead
[0]);
834 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
835 Thread
->ApcState
.Process
= Process
;
836 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
837 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
838 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
839 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
841 /* Initialize the Suspend APC */
842 KeInitializeApc(&Thread
->SuspendApc
,
844 OriginalApcEnvironment
,
845 KiSuspendThreadKernelRoutine
,
847 KiSuspendThreadNormalRoutine
,
851 /* Initialize the Suspend Semaphore */
852 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 128);
854 /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
856 Thread
->WaitBlock
[3].Object
= (PVOID
)&Thread
->Timer
;
857 Thread
->WaitBlock
[3].Thread
= Thread
;
858 Thread
->WaitBlock
[3].WaitKey
= STATUS_TIMEOUT
;
859 Thread
->WaitBlock
[3].WaitType
= WaitAny
;
860 Thread
->WaitBlock
[3].NextWaitBlock
= NULL
;
861 InsertTailList(&Thread
->Timer
.Header
.WaitListHead
,
862 &Thread
->WaitBlock
[3].WaitListEntry
);
864 KeInitializeTimer(&Thread
->Timer
);
869 /* Set the Thread Stacks */
870 Thread
->InitialStack
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
871 Thread
->StackBase
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
872 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
;
873 Thread
->KernelStackResident
= TRUE
;
876 * Establish the pde's for the new stack and the thread structure within the
877 * address space of the new process. They are accessed while taskswitching or
878 * while handling page faults. At this point it isn't possible to call the
879 * page fault handler for the missing pde's.
881 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
->StackLimit
, MM_STACK_SIZE
);
882 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
884 /* Initalize the Thread Context */
885 DPRINT("Initializing the Context for the thread: %x\n", Thread
);
886 KiArchInitThreadWithContext(Thread
,
892 /* Setup scheduler Fields based on Parent */
893 DPRINT("Thread context created, setting Scheduler Data\n");
894 Thread
->BasePriority
= Process
->BasePriority
;
895 Thread
->Quantum
= Process
->QuantumReset
;
896 Thread
->QuantumReset
= Process
->QuantumReset
;
897 Thread
->Affinity
= Process
->Affinity
;
898 Thread
->Priority
= Process
->BasePriority
;
899 Thread
->UserAffinity
= Process
->Affinity
;
900 Thread
->DisableBoost
= Process
->DisableBoost
;
901 Thread
->AutoAlignment
= Process
->AutoAlignment
;
902 Thread
->Iopl
= Process
->Iopl
;
904 /* Set the Thread to initalized */
905 Thread
->State
= Initialized
;
908 * Insert the Thread into the Process's Thread List
909 * Note, this is the KTHREAD Thread List. It is removed in
910 * ke/kthread.c!KeTerminateThread.
912 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
913 DPRINT("Thread initalized\n");
922 KeQueryPriorityThread (IN PKTHREAD Thread
)
924 return Thread
->Priority
;
932 KeQueryRuntimeThread(IN PKTHREAD Thread
,
935 /* Return the User Time */
936 *UserTime
= Thread
->UserTime
;
938 /* Return the Kernel Time */
939 return Thread
->KernelTime
;
947 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
949 PKTHREAD Thread
= KeGetCurrentThread();
950 BOOLEAN PreviousState
;
953 /* Lock the Dispatcher Database */
954 OldIrql
= KeAcquireDispatcherDatabaseLock();
957 PreviousState
= Thread
->EnableStackSwap
;
960 Thread
->EnableStackSwap
= Enable
;
962 /* No, Release Lock */
963 KeReleaseDispatcherDatabaseLock(OldIrql
);
965 /* Return Old State */
966 return PreviousState
;
974 KeRevertToUserAffinityThread(VOID
)
976 PKTHREAD CurrentThread
= KeGetCurrentThread();
979 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
981 /* Lock the Dispatcher Database */
982 OldIrql
= KeAcquireDispatcherDatabaseLock();
984 /* Return to User Affinity */
985 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
987 /* Disable System Affinity */
988 CurrentThread
->SystemAffinityActive
= FALSE
;
990 /* Check if we need to Dispatch a New thread */
991 if (CurrentThread
->Affinity
& (1 << KeGetCurrentProcessorNumber())) {
993 /* No, just release */
994 KeReleaseDispatcherDatabaseLock(OldIrql
);
998 /* We need to dispatch a new thread */
999 CurrentThread
->WaitIrql
= OldIrql
;
1000 KiDispatchThreadNoLock(Ready
);
1001 KeLowerIrql(OldIrql
);
1010 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
1013 CCHAR PreviousIdealProcessor
;
1016 /* Lock the Dispatcher Database */
1017 OldIrql
= KeAcquireDispatcherDatabaseLock();
1019 /* Save Old Ideal Processor */
1020 PreviousIdealProcessor
= Thread
->IdealProcessor
;
1022 /* Set New Ideal Processor */
1023 Thread
->IdealProcessor
= Processor
;
1026 KeReleaseDispatcherDatabaseLock(OldIrql
);
1028 /* Return Old Ideal Processor */
1029 return PreviousIdealProcessor
;
1037 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
1039 PKTHREAD CurrentThread
= KeGetCurrentThread();
1042 ASSERT(Affinity
& ((1 << KeNumberProcessors
) - 1));
1044 /* Lock the Dispatcher Database */
1045 OldIrql
= KeAcquireDispatcherDatabaseLock();
1047 /* Set the System Affinity Specified */
1048 CurrentThread
->Affinity
= Affinity
;
1050 /* Enable System Affinity */
1051 CurrentThread
->SystemAffinityActive
= TRUE
;
1053 /* Check if we need to Dispatch a New thread */
1054 if (Affinity
& (1 << KeGetCurrentProcessorNumber())) {
1056 /* No, just release */
1057 KeReleaseDispatcherDatabaseLock(OldIrql
);
1061 /* We need to dispatch a new thread */
1062 CurrentThread
->WaitIrql
= OldIrql
;
1063 KiDispatchThreadNoLock(Ready
);
1064 KeLowerIrql(OldIrql
);
1070 KeQueryBasePriorityThread(IN PKTHREAD Thread
)
1072 LONG BasePriorityIncrement
;
1076 /* Lock the Dispatcher Database */
1077 OldIrql
= KeAcquireDispatcherDatabaseLock();
1079 /* Get the Process */
1080 Process
= Thread
->ApcStatePointer
[0]->Process
;
1082 /* Calculate the BPI */
1083 BasePriorityIncrement
= Thread
->BasePriority
- Process
->BasePriority
;
1085 /* If saturation occured, return the SI instead */
1086 if (Thread
->Saturation
) BasePriorityIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1090 KeReleaseDispatcherDatabaseLock(OldIrql
);
1092 /* Return Increment */
1093 return BasePriorityIncrement
;
1098 KiSetPriorityThread(PKTHREAD Thread
,
1102 KPRIORITY OldPriority
= Thread
->Priority
;
1106 DPRINT("Changing prio to : %lx\n", Priority
);
1108 /* Check if priority changed */
1109 if (OldPriority
!= Priority
)
1112 Thread
->Priority
= Priority
;
1114 /* Choose action based on thread's state */
1115 if (Thread
->State
== Ready
)
1117 /* Remove it from the current queue */
1118 KiRemoveFromThreadList(Thread
);
1120 /* Re-insert it at its current priority */
1121 KiInsertIntoThreadList(Priority
, Thread
);
1123 /* Check if the old priority was lower */
1124 if (KeGetCurrentThread()->Priority
< Priority
)
1126 /* Dispatch it immediately */
1127 KiDispatchThreadNoLock(Ready
);
1132 else if (Thread
->State
== Running
)
1134 /* Check if the new priority is lower */
1135 if (Priority
< OldPriority
)
1137 /* Check for threads with a higher priority */
1138 Mask
= ~((1 << (Priority
+ 1)) - 1);
1139 if (PriorityListMask
& Mask
)
1141 /* Found a thread, is it us? */
1142 if (Thread
== KeGetCurrentThread())
1145 KiDispatchThreadNoLock(Ready
);
1151 /* Loop every CPU */
1152 for (i
= 0; i
< KeNumberProcessors
; i
++)
1154 /* Get the PCR for this CPU */
1155 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1157 /* Reschedule if the new one is already on a CPU */
1158 if (Pcr
->Prcb
->CurrentThread
== Thread
)
1160 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1161 KiRequestReschedule(i
);
1172 /* Return to caller */
1177 * Sets thread's base priority relative to the process' base priority
1178 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
1184 KeSetBasePriorityThread (PKTHREAD Thread
,
1190 KPRIORITY CurrentBasePriority
;
1191 KPRIORITY BasePriority
;
1192 BOOLEAN Released
= FALSE
;
1193 LONG CurrentIncrement
;
1195 /* Lock the Dispatcher Database */
1196 OldIrql
= KeAcquireDispatcherDatabaseLock();
1198 /* Get the process and calculate current BP and BPI */
1199 Process
= Thread
->ApcStatePointer
[0]->Process
;
1200 CurrentBasePriority
= Thread
->BasePriority
;
1201 CurrentIncrement
= CurrentBasePriority
- Process
->BasePriority
;
1203 /* Change to use the SI if Saturation was used */
1204 if (Thread
->Saturation
) CurrentIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1207 /* Now check if saturation is being used for the new value */
1208 if (abs(Increment
) >= ((HIGH_PRIORITY
+ 1) / 2))
1210 /* Check if we need positive or negative saturation */
1211 Thread
->Saturation
= (Increment
> 0) ? 1 : -1;
1214 /* Normalize the Base Priority */
1215 BasePriority
= Process
->BasePriority
+ Increment
;
1216 if (Process
->BasePriority
>= LOW_REALTIME_PRIORITY
)
1218 /* Check if it's too low */
1219 if (BasePriority
< LOW_REALTIME_PRIORITY
)
1220 BasePriority
= LOW_REALTIME_PRIORITY
;
1222 /* Check if it's too high */
1223 if (BasePriority
> HIGH_PRIORITY
) BasePriority
= HIGH_PRIORITY
;
1225 /* We are at RTP, so use the raw BP */
1226 Priority
= BasePriority
;
1230 /* Check if it's entering RTP */
1231 if (BasePriority
>= LOW_REALTIME_PRIORITY
)
1232 BasePriority
= LOW_REALTIME_PRIORITY
- 1;
1234 /* Check if it's too low */
1235 if (BasePriority
<= LOW_PRIORITY
)
1238 /* If Saturation is used, then use the raw BP */
1239 if (Thread
->Saturation
)
1241 Priority
= BasePriority
;
1245 /* Calculate the new priority */
1246 Priority
= Thread
->Priority
+ (BasePriority
- CurrentBasePriority
)-
1247 Thread
->PriorityDecrement
;
1249 /* Make sure it won't enter RTP ranges */
1250 if (Priority
>= LOW_REALTIME_PRIORITY
)
1251 Priority
= LOW_REALTIME_PRIORITY
- 1;
1255 /* Finally set the new base priority */
1256 Thread
->BasePriority
= BasePriority
;
1258 /* Reset the decrements */
1259 Thread
->DecrementCount
= 0;
1260 Thread
->PriorityDecrement
= 0;
1262 /* If the priority will change, reset quantum and change it for real */
1263 if (Priority
!= Thread
->Priority
)
1265 Thread
->Quantum
= Thread
->QuantumReset
;
1266 KiSetPriorityThread(Thread
, Priority
, &Released
);
1269 /* Release Lock if needed */
1272 KeReleaseDispatcherDatabaseLock(OldIrql
);
1276 KeLowerIrql(OldIrql
);
1279 /* Return the Old Increment */
1280 return CurrentIncrement
;
1288 KeSetPriorityThread(PKTHREAD Thread
,
1291 KPRIORITY OldPriority
;
1292 BOOLEAN Released
= FALSE
;
1295 /* Lock the Dispatcher Database */
1296 OldIrql
= KeAcquireDispatcherDatabaseLock();
1298 /* Save the old Priority */
1299 OldPriority
= Thread
->Priority
;
1301 /* Reset the Quantum and Decrements */
1302 Thread
->Quantum
= Thread
->QuantumReset
;
1303 Thread
->DecrementCount
= 0;
1304 Thread
->PriorityDecrement
= 0;
1306 /* Set the new Priority */
1307 KiSetPriorityThread(Thread
, Priority
, &Released
);
1309 /* Release Lock if needed */
1312 KeReleaseDispatcherDatabaseLock(OldIrql
);
1316 KeLowerIrql(OldIrql
);
1319 /* Return Old Priority */
1326 * Sets thread's affinity
1330 KeSetAffinityThread(PKTHREAD Thread
,
1336 KAFFINITY ProcessorMask
;
1338 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread
, Affinity
);
1340 /* Verify correct affinity */
1341 if ((Affinity
& Thread
->ApcStatePointer
[0]->Process
->Affinity
) !=
1342 Affinity
|| !Affinity
)
1344 KEBUGCHECK(INVALID_AFFINITY_SET
);
1347 OldIrql
= KeAcquireDispatcherDatabaseLock();
1349 Thread
->UserAffinity
= Affinity
;
1351 if (Thread
->SystemAffinityActive
== FALSE
) {
1353 Thread
->Affinity
= Affinity
;
1355 if (Thread
->State
== Running
) {
1357 ProcessorMask
= 1 << KeGetCurrentKPCR()->Number
;
1358 if (Thread
== KeGetCurrentThread()) {
1360 if (!(Affinity
& ProcessorMask
)) {
1362 KiDispatchThreadNoLock(Ready
);
1363 KeLowerIrql(OldIrql
);
1364 return STATUS_SUCCESS
;
1369 for (i
= 0; i
< KeNumberProcessors
; i
++) {
1371 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1372 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
1374 if (!(Affinity
& ProcessorMask
)) {
1376 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1377 KiRequestReschedule(i
);
1378 KeLowerIrql(OldIrql
);
1379 return STATUS_SUCCESS
;
1386 ASSERT (i
< KeNumberProcessors
);
1391 KeReleaseDispatcherDatabaseLock(OldIrql
);
1392 return STATUS_SUCCESS
;
1398 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1401 KeTerminateThread(IN KPRIORITY Increment
)
1404 PKTHREAD Thread
= KeGetCurrentThread();
1406 /* Lock the Dispatcher Database and the APC Queue */
1407 DPRINT("Terminating\n");
1408 OldIrql
= KeAcquireDispatcherDatabaseLock();
1410 /* Remove the thread from the list */
1411 RemoveEntryList(&Thread
->ThreadListEntry
);
1413 /* Insert into the Reaper List */
1414 DPRINT("List: %p\n", PspReaperList
);
1415 ((PETHREAD
)Thread
)->ReaperLink
= PspReaperList
;
1416 PspReaperList
= (PETHREAD
)Thread
;
1417 DPRINT("List: %p\n", PspReaperList
);
1419 /* Check if it's active */
1420 if (PspReaping
== FALSE
) {
1422 /* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */
1424 DPRINT("Terminating\n");
1425 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1426 &PspReaperWorkItem
.List
,
1430 /* Handle Kernel Queues */
1431 if (Thread
->Queue
) {
1433 DPRINT("Waking Queue\n");
1434 RemoveEntryList(&Thread
->QueueListEntry
);
1435 KiWakeQueue(Thread
->Queue
);
1438 /* Signal the thread */
1439 Thread
->DispatcherHeader
.SignalState
= TRUE
;
1440 if (IsListEmpty(&Thread
->DispatcherHeader
.WaitListHead
) != TRUE
) {
1443 KiWaitTest((PVOID
)Thread
, Increment
);
1446 /* Find a new Thread */
1447 KiDispatchThreadNoLock(Terminated
);
1451 * FUNCTION: Tests whether there are any pending APCs for the current thread
1452 * and if so the APCs will be delivered on exit from kernel mode
1456 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
1459 PKTHREAD Thread
= KeGetCurrentThread();
1462 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1464 /* Lock the Dispatcher Database and the APC Queue */
1465 OldIrql
= KeAcquireDispatcherDatabaseLock();
1466 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
1468 /* Save the old State */
1469 OldState
= Thread
->Alerted
[AlertMode
];
1471 /* If the Thread is Alerted, Clear it */
1474 Thread
->Alerted
[AlertMode
] = FALSE
;
1476 } else if ((AlertMode
!= KernelMode
) && (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))) {
1478 /* If the mode is User and the Queue isn't empty, set Pending */
1479 Thread
->ApcState
.UserApcPending
= TRUE
;
1482 /* Release Locks and return the Old State */
1483 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
1484 KeReleaseDispatcherDatabaseLock(OldIrql
);
1489 KiServiceCheck (VOID
)
1491 PKTHREAD Thread
= KeGetCurrentThread();
1493 /* Check if we need to inialize Win32 for this Thread */
1494 if (Thread
->ServiceTable
!= KeServiceDescriptorTableShadow
) {
1496 /* We do. Initialize it and save the new table */
1497 Thread
->ServiceTable
= KeServiceDescriptorTableShadow
;
1498 PsInitWin32Thread((PETHREAD
)Thread
);
1508 NtAlertResumeThread(IN HANDLE ThreadHandle
,
1509 OUT PULONG SuspendCount
)
1511 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1514 ULONG PreviousState
;
1516 /* Check if parameters are valid */
1517 if(PreviousMode
!= KernelMode
) {
1521 ProbeForWriteUlong(SuspendCount
);
1525 Status
= _SEH_GetExceptionCode();
1530 /* Reference the Object */
1531 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1532 THREAD_SUSPEND_RESUME
,
1538 /* Check for Success */
1539 if (NT_SUCCESS(Status
)) {
1541 /* Call the Kernel Function */
1542 PreviousState
= KeAlertResumeThread(&Thread
->Tcb
);
1544 /* Dereference Object */
1545 ObDereferenceObject(Thread
);
1551 *SuspendCount
= PreviousState
;
1555 Status
= _SEH_GetExceptionCode();
1572 NtAlertThread (IN HANDLE ThreadHandle
)
1574 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1578 /* Reference the Object */
1579 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1580 THREAD_SUSPEND_RESUME
,
1586 /* Check for Success */
1587 if (NT_SUCCESS(Status
)) {
1590 * Do an alert depending on the processor mode. If some kmode code wants to
1591 * enforce a umode alert it should call KeAlertThread() directly. If kmode
1592 * code wants to do a kmode alert it's sufficient to call it with Zw or just
1593 * use KeAlertThread() directly
1595 KeAlertThread(&Thread
->Tcb
, PreviousMode
);
1597 /* Dereference Object */
1598 ObDereferenceObject(Thread
);
1607 NtDelayExecution(IN BOOLEAN Alertable
,
1608 IN PLARGE_INTEGER DelayInterval
)
1610 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1611 LARGE_INTEGER SafeInterval
;
1614 /* Check if parameters are valid */
1615 if(PreviousMode
!= KernelMode
) {
1617 Status
= STATUS_SUCCESS
;
1621 /* make a copy on the kernel stack and let DelayInterval point to it so
1622 we don't need to wrap KeDelayExecutionThread in SEH! */
1623 SafeInterval
= ProbeForReadLargeInteger(DelayInterval
);
1624 DelayInterval
= &SafeInterval
;
1628 Status
= _SEH_GetExceptionCode();
1631 if (!NT_SUCCESS(Status
))
1637 /* Call the Kernel Function */
1638 Status
= KeDelayExecutionThread(PreviousMode
,