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",
479 Mutant
->Header
.Size
);
481 /* Make sure it's not terminating with APCs off */
482 if (Mutant
->ApcDisable
)
484 /* Bugcheck the system */
485 KEBUGCHECKEX(0,//THREAD_TERMINATE_HELD_MUTEX,
492 /* Now we can remove it */
493 RemoveEntryList(&Mutant
->MutantListEntry
);
495 /* Unconditionally abandon it */
496 DPRINT("Abandonning the Mutant\n");
497 Mutant
->Header
.SignalState
= 1;
498 Mutant
->Abandoned
= TRUE
;
499 Mutant
->OwnerThread
= NULL
;
501 /* Check if the Wait List isn't empty */
502 DPRINT("Checking whether to wake the Mutant\n");
503 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
))
505 /* Wake the Mutant */
506 DPRINT("Waking the Mutant\n");
507 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
511 NextEntry
= NextEntry
->Flink
;
514 /* Release the Lock */
515 KeReleaseDispatcherDatabaseLock(OldIrql
);
520 KeResumeThread(PKTHREAD Thread
)
525 DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread
,
526 Thread
->SuspendCount
, Thread
->FreezeCount
);
528 /* Lock the Dispatcher */
529 OldIrql
= KeAcquireDispatcherDatabaseLock();
531 /* Save the Old Count */
532 PreviousCount
= Thread
->SuspendCount
;
534 /* Check if it existed */
537 Thread
->SuspendCount
--;
539 /* Decrease the current Suspend Count and Check Freeze Count */
540 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
)) {
542 /* Signal the Suspend Semaphore */
543 Thread
->SuspendSemaphore
.Header
.SignalState
++;
544 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
548 /* Release Lock and return the Old State */
549 KeReleaseDispatcherDatabaseLock(OldIrql
);
550 return PreviousCount
;
555 KiInsertQueueApc(PKAPC Apc
,
556 KPRIORITY PriorityBoost
);
559 * Used by the debugging code to freeze all the process's threads
560 * while the debugger is examining their state.
564 KeFreezeAllThreads(PKPROCESS Process
)
568 PKTHREAD CurrentThread
= KeGetCurrentThread();
571 OldIrql
= KeAcquireDispatcherDatabaseLock();
573 /* If someone is already trying to free us, try again */
574 while (CurrentThread
->FreezeCount
)
576 /* Release and re-acquire the lock so the APC will go through */
577 KeReleaseDispatcherDatabaseLock(OldIrql
);
578 OldIrql
= KeAcquireDispatcherDatabaseLock();
581 /* Enter a critical region */
582 KeEnterCriticalRegion();
584 /* Loop the Process's Threads */
585 LIST_FOR_EACH(Current
, &Process
->ThreadListHead
, KTHREAD
, ThreadListEntry
)
587 /* Make sure it's not ours */
588 if (Current
!= CurrentThread
)
590 /* Should be bother inserting the APC? */
591 if (Current
->ApcQueueable
)
593 /* Make sure it wasn't already frozen, and that it's not suspended */
594 if (!(++Current
->FreezeCount
) && !(Current
->SuspendCount
))
596 /* Did we already insert it? */
597 if (!Current
->SuspendApc
.Inserted
)
600 Current
->SuspendApc
.Inserted
= TRUE
;
601 KiInsertQueueApc(&Current
->SuspendApc
, IO_NO_INCREMENT
);
605 /* Unsignal the Semaphore, the APC already got inserted */
606 Current
->SuspendSemaphore
.Header
.SignalState
--;
613 /* Release the lock */
614 KeReleaseDispatcherDatabaseLock(OldIrql
);
619 KeSuspendThread(PKTHREAD Thread
)
624 DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread
, Thread
->SuspendCount
, Thread
->FreezeCount
);
626 /* Lock the Dispatcher */
627 OldIrql
= KeAcquireDispatcherDatabaseLock();
629 /* Save the Old Count */
630 PreviousCount
= Thread
->SuspendCount
;
632 /* Handle the maximum */
633 if (PreviousCount
== MAXIMUM_SUSPEND_COUNT
)
635 /* Raise an exception */
636 KeReleaseDispatcherDatabaseLock(OldIrql
);
637 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED
);
640 /* Should we bother to queue at all? */
641 if (Thread
->ApcQueueable
)
643 /* Increment the suspend count */
644 Thread
->SuspendCount
++;
646 /* Check if we should suspend it */
647 if (!PreviousCount
&& !Thread
->FreezeCount
)
649 /* Is the APC already inserted? */
650 if (!Thread
->SuspendApc
.Inserted
)
652 /* Not inserted, insert it */
653 Thread
->SuspendApc
.Inserted
= TRUE
;
654 KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
);
658 /* Unsignal the Semaphore, the APC already got inserted */
659 Thread
->SuspendSemaphore
.Header
.SignalState
--;
664 /* Release Lock and return the Old State */
665 KeReleaseDispatcherDatabaseLock(OldIrql
);
666 return PreviousCount
;
671 KeForceResumeThread(IN PKTHREAD Thread
)
676 /* Lock the Dispatcher Database and the APC Queue */
677 OldIrql
= KeAcquireDispatcherDatabaseLock();
679 /* Save the old Suspend Count */
680 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
682 /* If the thread is suspended, wake it up!!! */
685 /* Unwait it completely */
686 Thread
->SuspendCount
= 0;
687 Thread
->FreezeCount
= 0;
689 /* Signal and satisfy */
690 Thread
->SuspendSemaphore
.Header
.SignalState
++;
691 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
694 /* Release Lock and return the Old State */
695 KeReleaseDispatcherDatabaseLock(OldIrql
);
696 return PreviousCount
;
701 KeAlertResumeThread(IN PKTHREAD Thread
)
706 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
708 /* Lock the Dispatcher Database and the APC Queue */
709 OldIrql
= KeAcquireDispatcherDatabaseLock();
710 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
712 /* Return if Thread is already alerted. */
713 if (Thread
->Alerted
[KernelMode
] == FALSE
) {
715 /* If it's Blocked, unblock if it we should */
716 if (Thread
->State
== Waiting
&& Thread
->Alertable
) {
718 DPRINT("Aborting Wait\n");
719 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
723 /* If not, simply Alert it */
724 Thread
->Alerted
[KernelMode
] = TRUE
;
728 /* Save the old Suspend Count */
729 PreviousCount
= Thread
->SuspendCount
;
731 /* If the thread is suspended, decrease one of the suspend counts */
734 /* Decrease count. If we are now zero, unwait it completely */
735 if (--Thread
->SuspendCount
) {
737 /* Signal and satisfy */
738 Thread
->SuspendSemaphore
.Header
.SignalState
++;
739 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
743 /* Release Locks and return the Old State */
744 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
745 KeReleaseDispatcherDatabaseLock(OldIrql
);
746 return PreviousCount
;
751 KeAlertThread(PKTHREAD Thread
,
752 KPROCESSOR_MODE AlertMode
)
755 BOOLEAN PreviousState
;
757 /* Acquire the Dispatcher Database Lock */
758 OldIrql
= KeAcquireDispatcherDatabaseLock();
760 /* Save the Previous State */
761 PreviousState
= Thread
->Alerted
[AlertMode
];
763 /* Return if Thread is already alerted. */
764 if (PreviousState
== FALSE
) {
766 /* If it's Blocked, unblock if it we should */
767 if (Thread
->State
== Waiting
&&
768 (AlertMode
== KernelMode
|| Thread
->WaitMode
== AlertMode
) &&
771 DPRINT("Aborting Wait\n");
772 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
776 /* If not, simply Alert it */
777 Thread
->Alerted
[AlertMode
] = TRUE
;
781 /* Release the Dispatcher Lock */
782 KeReleaseDispatcherDatabaseLock(OldIrql
);
784 /* Return the old state */
785 return PreviousState
;
793 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
799 IN PVOID ThreadState
)
805 * FUNCTION: Initialize the microkernel state of the thread
809 KeInitializeThread(PKPROCESS Process
,
811 PKSYSTEM_ROUTINE SystemRoutine
,
812 PKSTART_ROUTINE StartRoutine
,
818 /* Initalize the Dispatcher Header */
819 DPRINT("Initializing Dispatcher Header for New Thread: %x in Process: %x\n", Thread
, Process
);
820 KeInitializeDispatcherHeader(&Thread
->DispatcherHeader
,
825 DPRINT("Thread Header Created. SystemRoutine: %x, StartRoutine: %x with Context: %x\n",
826 SystemRoutine
, StartRoutine
, StartContext
);
827 DPRINT("UserMode Information. Context: %x, Teb: %x\n", Context
, Teb
);
829 /* Initialize the Mutant List */
830 InitializeListHead(&Thread
->MutantListHead
);
832 /* Setup the Service Descriptor Table for Native Calls */
833 Thread
->ServiceTable
= KeServiceDescriptorTable
;
835 /* Setup APC Fields */
836 InitializeListHead(&Thread
->ApcState
.ApcListHead
[0]);
837 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
838 Thread
->ApcState
.Process
= Process
;
839 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
840 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
841 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
842 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
844 /* Initialize the Suspend APC */
845 KeInitializeApc(&Thread
->SuspendApc
,
847 OriginalApcEnvironment
,
848 KiSuspendThreadKernelRoutine
,
850 KiSuspendThreadNormalRoutine
,
854 /* Initialize the Suspend Semaphore */
855 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 128);
857 /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
859 Thread
->WaitBlock
[3].Object
= (PVOID
)&Thread
->Timer
;
860 Thread
->WaitBlock
[3].Thread
= Thread
;
861 Thread
->WaitBlock
[3].WaitKey
= STATUS_TIMEOUT
;
862 Thread
->WaitBlock
[3].WaitType
= WaitAny
;
863 Thread
->WaitBlock
[3].NextWaitBlock
= NULL
;
864 InsertTailList(&Thread
->Timer
.Header
.WaitListHead
,
865 &Thread
->WaitBlock
[3].WaitListEntry
);
867 KeInitializeTimer(&Thread
->Timer
);
872 /* Set the Thread Stacks */
873 Thread
->InitialStack
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
874 Thread
->StackBase
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
875 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
;
876 Thread
->KernelStackResident
= TRUE
;
879 * Establish the pde's for the new stack and the thread structure within the
880 * address space of the new process. They are accessed while taskswitching or
881 * while handling page faults. At this point it isn't possible to call the
882 * page fault handler for the missing pde's.
884 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
->StackLimit
, MM_STACK_SIZE
);
885 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
887 /* Initalize the Thread Context */
888 DPRINT("Initializing the Context for the thread: %x\n", Thread
);
889 KiArchInitThreadWithContext(Thread
,
895 /* Setup scheduler Fields based on Parent */
896 DPRINT("Thread context created, setting Scheduler Data\n");
897 Thread
->BasePriority
= Process
->BasePriority
;
898 Thread
->Quantum
= Process
->QuantumReset
;
899 Thread
->QuantumReset
= Process
->QuantumReset
;
900 Thread
->Affinity
= Process
->Affinity
;
901 Thread
->Priority
= Process
->BasePriority
;
902 Thread
->UserAffinity
= Process
->Affinity
;
903 Thread
->DisableBoost
= Process
->DisableBoost
;
904 Thread
->AutoAlignment
= Process
->AutoAlignment
;
905 Thread
->Iopl
= Process
->Iopl
;
907 /* Set the Thread to initalized */
908 Thread
->State
= Initialized
;
911 * Insert the Thread into the Process's Thread List
912 * Note, this is the KTHREAD Thread List. It is removed in
913 * ke/kthread.c!KeTerminateThread.
915 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
916 DPRINT("Thread initalized\n");
925 KeQueryPriorityThread (IN PKTHREAD Thread
)
927 return Thread
->Priority
;
935 KeQueryRuntimeThread(IN PKTHREAD Thread
,
938 /* Return the User Time */
939 *UserTime
= Thread
->UserTime
;
941 /* Return the Kernel Time */
942 return Thread
->KernelTime
;
950 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
952 PKTHREAD Thread
= KeGetCurrentThread();
953 BOOLEAN PreviousState
;
956 /* Lock the Dispatcher Database */
957 OldIrql
= KeAcquireDispatcherDatabaseLock();
960 PreviousState
= Thread
->EnableStackSwap
;
963 Thread
->EnableStackSwap
= Enable
;
965 /* No, Release Lock */
966 KeReleaseDispatcherDatabaseLock(OldIrql
);
968 /* Return Old State */
969 return PreviousState
;
977 KeRevertToUserAffinityThread(VOID
)
979 PKTHREAD CurrentThread
= KeGetCurrentThread();
982 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
984 /* Lock the Dispatcher Database */
985 OldIrql
= KeAcquireDispatcherDatabaseLock();
987 /* Return to User Affinity */
988 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
990 /* Disable System Affinity */
991 CurrentThread
->SystemAffinityActive
= FALSE
;
993 /* Check if we need to Dispatch a New thread */
994 if (CurrentThread
->Affinity
& (1 << KeGetCurrentProcessorNumber())) {
996 /* No, just release */
997 KeReleaseDispatcherDatabaseLock(OldIrql
);
1001 /* We need to dispatch a new thread */
1002 CurrentThread
->WaitIrql
= OldIrql
;
1003 KiDispatchThreadNoLock(Ready
);
1004 KeLowerIrql(OldIrql
);
1013 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
1016 CCHAR PreviousIdealProcessor
;
1019 /* Lock the Dispatcher Database */
1020 OldIrql
= KeAcquireDispatcherDatabaseLock();
1022 /* Save Old Ideal Processor */
1023 PreviousIdealProcessor
= Thread
->IdealProcessor
;
1025 /* Set New Ideal Processor */
1026 Thread
->IdealProcessor
= Processor
;
1029 KeReleaseDispatcherDatabaseLock(OldIrql
);
1031 /* Return Old Ideal Processor */
1032 return PreviousIdealProcessor
;
1040 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
1042 PKTHREAD CurrentThread
= KeGetCurrentThread();
1045 ASSERT(Affinity
& ((1 << KeNumberProcessors
) - 1));
1047 /* Lock the Dispatcher Database */
1048 OldIrql
= KeAcquireDispatcherDatabaseLock();
1050 /* Set the System Affinity Specified */
1051 CurrentThread
->Affinity
= Affinity
;
1053 /* Enable System Affinity */
1054 CurrentThread
->SystemAffinityActive
= TRUE
;
1056 /* Check if we need to Dispatch a New thread */
1057 if (Affinity
& (1 << KeGetCurrentProcessorNumber())) {
1059 /* No, just release */
1060 KeReleaseDispatcherDatabaseLock(OldIrql
);
1064 /* We need to dispatch a new thread */
1065 CurrentThread
->WaitIrql
= OldIrql
;
1066 KiDispatchThreadNoLock(Ready
);
1067 KeLowerIrql(OldIrql
);
1073 KeQueryBasePriorityThread(IN PKTHREAD Thread
)
1075 LONG BasePriorityIncrement
;
1079 /* Lock the Dispatcher Database */
1080 OldIrql
= KeAcquireDispatcherDatabaseLock();
1082 /* Get the Process */
1083 Process
= Thread
->ApcStatePointer
[0]->Process
;
1085 /* Calculate the BPI */
1086 BasePriorityIncrement
= Thread
->BasePriority
- Process
->BasePriority
;
1088 /* If saturation occured, return the SI instead */
1089 if (Thread
->Saturation
) BasePriorityIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1093 KeReleaseDispatcherDatabaseLock(OldIrql
);
1095 /* Return Increment */
1096 return BasePriorityIncrement
;
1101 KiSetPriorityThread(PKTHREAD Thread
,
1105 KPRIORITY OldPriority
= Thread
->Priority
;
1109 DPRINT("Changing prio to : %lx\n", Priority
);
1111 /* Check if priority changed */
1112 if (OldPriority
!= Priority
)
1115 Thread
->Priority
= Priority
;
1117 /* Choose action based on thread's state */
1118 if (Thread
->State
== Ready
)
1120 /* Remove it from the current queue */
1121 KiRemoveFromThreadList(Thread
);
1123 /* Re-insert it at its current priority */
1124 KiInsertIntoThreadList(Priority
, Thread
);
1126 /* Check if the old priority was lower */
1127 if (KeGetCurrentThread()->Priority
< Priority
)
1129 /* Dispatch it immediately */
1130 KiDispatchThreadNoLock(Ready
);
1135 else if (Thread
->State
== Running
)
1137 /* Check if the new priority is lower */
1138 if (Priority
< OldPriority
)
1140 /* Check for threads with a higher priority */
1141 Mask
= ~((1 << (Priority
+ 1)) - 1);
1142 if (PriorityListMask
& Mask
)
1144 /* Found a thread, is it us? */
1145 if (Thread
== KeGetCurrentThread())
1148 KiDispatchThreadNoLock(Ready
);
1154 /* Loop every CPU */
1155 for (i
= 0; i
< KeNumberProcessors
; i
++)
1157 /* Get the PCR for this CPU */
1158 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1160 /* Reschedule if the new one is already on a CPU */
1161 if (Pcr
->Prcb
->CurrentThread
== Thread
)
1163 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1164 KiRequestReschedule(i
);
1175 /* Return to caller */
1180 * Sets thread's base priority relative to the process' base priority
1181 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
1187 KeSetBasePriorityThread (PKTHREAD Thread
,
1193 KPRIORITY CurrentBasePriority
;
1194 KPRIORITY BasePriority
;
1195 BOOLEAN Released
= FALSE
;
1196 LONG CurrentIncrement
;
1198 /* Lock the Dispatcher Database */
1199 OldIrql
= KeAcquireDispatcherDatabaseLock();
1201 /* Get the process and calculate current BP and BPI */
1202 Process
= Thread
->ApcStatePointer
[0]->Process
;
1203 CurrentBasePriority
= Thread
->BasePriority
;
1204 CurrentIncrement
= CurrentBasePriority
- Process
->BasePriority
;
1206 /* Change to use the SI if Saturation was used */
1207 if (Thread
->Saturation
) CurrentIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1210 /* Now check if saturation is being used for the new value */
1211 if (abs(Increment
) >= ((HIGH_PRIORITY
+ 1) / 2))
1213 /* Check if we need positive or negative saturation */
1214 Thread
->Saturation
= (Increment
> 0) ? 1 : -1;
1217 /* Normalize the Base Priority */
1218 BasePriority
= Process
->BasePriority
+ Increment
;
1219 if (Process
->BasePriority
>= LOW_REALTIME_PRIORITY
)
1221 /* Check if it's too low */
1222 if (BasePriority
< LOW_REALTIME_PRIORITY
)
1223 BasePriority
= LOW_REALTIME_PRIORITY
;
1225 /* Check if it's too high */
1226 if (BasePriority
> HIGH_PRIORITY
) BasePriority
= HIGH_PRIORITY
;
1228 /* We are at RTP, so use the raw BP */
1229 Priority
= BasePriority
;
1233 /* Check if it's entering RTP */
1234 if (BasePriority
>= LOW_REALTIME_PRIORITY
)
1235 BasePriority
= LOW_REALTIME_PRIORITY
- 1;
1237 /* Check if it's too low */
1238 if (BasePriority
<= LOW_PRIORITY
)
1241 /* If Saturation is used, then use the raw BP */
1242 if (Thread
->Saturation
)
1244 Priority
= BasePriority
;
1248 /* Calculate the new priority */
1249 Priority
= Thread
->Priority
+ (BasePriority
- CurrentBasePriority
)-
1250 Thread
->PriorityDecrement
;
1252 /* Make sure it won't enter RTP ranges */
1253 if (Priority
>= LOW_REALTIME_PRIORITY
)
1254 Priority
= LOW_REALTIME_PRIORITY
- 1;
1258 /* Finally set the new base priority */
1259 Thread
->BasePriority
= BasePriority
;
1261 /* Reset the decrements */
1262 Thread
->PriorityDecrement
= 0;
1264 /* If the priority will change, reset quantum and change it for real */
1265 if (Priority
!= Thread
->Priority
)
1267 Thread
->Quantum
= Thread
->QuantumReset
;
1268 KiSetPriorityThread(Thread
, Priority
, &Released
);
1271 /* Release Lock if needed */
1274 KeReleaseDispatcherDatabaseLock(OldIrql
);
1278 KeLowerIrql(OldIrql
);
1281 /* Return the Old Increment */
1282 return CurrentIncrement
;
1290 KeSetPriorityThread(PKTHREAD Thread
,
1293 KPRIORITY OldPriority
;
1294 BOOLEAN Released
= FALSE
;
1297 /* Lock the Dispatcher Database */
1298 OldIrql
= KeAcquireDispatcherDatabaseLock();
1300 /* Save the old Priority */
1301 OldPriority
= Thread
->Priority
;
1303 /* Reset the Quantum and Decrements */
1304 Thread
->Quantum
= Thread
->QuantumReset
;
1305 Thread
->PriorityDecrement
= 0;
1307 /* Set the new Priority */
1308 KiSetPriorityThread(Thread
, Priority
, &Released
);
1310 /* Release Lock if needed */
1313 KeReleaseDispatcherDatabaseLock(OldIrql
);
1317 KeLowerIrql(OldIrql
);
1320 /* Return Old Priority */
1327 * Sets thread's affinity
1331 KeSetAffinityThread(PKTHREAD Thread
,
1337 KAFFINITY ProcessorMask
;
1339 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread
, Affinity
);
1341 /* Verify correct affinity */
1342 if ((Affinity
& Thread
->ApcStatePointer
[0]->Process
->Affinity
) !=
1343 Affinity
|| !Affinity
)
1345 KEBUGCHECK(INVALID_AFFINITY_SET
);
1348 OldIrql
= KeAcquireDispatcherDatabaseLock();
1350 Thread
->UserAffinity
= Affinity
;
1352 if (Thread
->SystemAffinityActive
== FALSE
) {
1354 Thread
->Affinity
= Affinity
;
1356 if (Thread
->State
== Running
) {
1358 ProcessorMask
= 1 << KeGetCurrentProcessorNumber();
1359 if (Thread
== KeGetCurrentThread()) {
1361 if (!(Affinity
& ProcessorMask
)) {
1363 KiDispatchThreadNoLock(Ready
);
1364 KeLowerIrql(OldIrql
);
1365 return STATUS_SUCCESS
;
1370 for (i
= 0; i
< KeNumberProcessors
; i
++) {
1372 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1373 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
1375 if (!(Affinity
& ProcessorMask
)) {
1377 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1378 KiRequestReschedule(i
);
1379 KeLowerIrql(OldIrql
);
1380 return STATUS_SUCCESS
;
1387 ASSERT (i
< KeNumberProcessors
);
1392 KeReleaseDispatcherDatabaseLock(OldIrql
);
1393 return STATUS_SUCCESS
;
1399 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1402 KeTerminateThread(IN KPRIORITY Increment
)
1405 PKTHREAD Thread
= KeGetCurrentThread();
1407 /* Lock the Dispatcher Database and the APC Queue */
1408 DPRINT("Terminating\n");
1409 OldIrql
= KeAcquireDispatcherDatabaseLock();
1411 /* Remove the thread from the list */
1412 RemoveEntryList(&Thread
->ThreadListEntry
);
1414 /* Insert into the Reaper List */
1415 DPRINT("List: %p\n", PspReaperList
);
1416 ((PETHREAD
)Thread
)->ReaperLink
= PspReaperList
;
1417 PspReaperList
= (PETHREAD
)Thread
;
1418 DPRINT("List: %p\n", PspReaperList
);
1420 /* Check if it's active */
1421 if (PspReaping
== FALSE
) {
1423 /* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */
1425 DPRINT("Terminating\n");
1426 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1427 &PspReaperWorkItem
.List
,
1431 /* Handle Kernel Queues */
1432 if (Thread
->Queue
) {
1434 DPRINT("Waking Queue\n");
1435 RemoveEntryList(&Thread
->QueueListEntry
);
1436 KiWakeQueue(Thread
->Queue
);
1439 /* Signal the thread */
1440 Thread
->DispatcherHeader
.SignalState
= TRUE
;
1441 if (IsListEmpty(&Thread
->DispatcherHeader
.WaitListHead
) != TRUE
) {
1444 KiWaitTest((PVOID
)Thread
, Increment
);
1447 /* Find a new Thread */
1448 KiDispatchThreadNoLock(Terminated
);
1452 * FUNCTION: Tests whether there are any pending APCs for the current thread
1453 * and if so the APCs will be delivered on exit from kernel mode
1457 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
1460 PKTHREAD Thread
= KeGetCurrentThread();
1463 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1465 /* Lock the Dispatcher Database and the APC Queue */
1466 OldIrql
= KeAcquireDispatcherDatabaseLock();
1467 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
1469 /* Save the old State */
1470 OldState
= Thread
->Alerted
[AlertMode
];
1472 /* If the Thread is Alerted, Clear it */
1475 Thread
->Alerted
[AlertMode
] = FALSE
;
1477 } else if ((AlertMode
!= KernelMode
) && (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))) {
1479 /* If the mode is User and the Queue isn't empty, set Pending */
1480 Thread
->ApcState
.UserApcPending
= TRUE
;
1483 /* Release Locks and return the Old State */
1484 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
1485 KeReleaseDispatcherDatabaseLock(OldIrql
);
1490 KiServiceCheck (VOID
)
1492 PKTHREAD Thread
= KeGetCurrentThread();
1494 /* Check if we need to inialize Win32 for this Thread */
1495 if (Thread
->ServiceTable
!= KeServiceDescriptorTableShadow
) {
1497 /* We do. Initialize it and save the new table */
1498 Thread
->ServiceTable
= KeServiceDescriptorTableShadow
;
1499 PsInitWin32Thread((PETHREAD
)Thread
);
1509 NtAlertResumeThread(IN HANDLE ThreadHandle
,
1510 OUT PULONG SuspendCount
)
1512 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1515 ULONG PreviousState
;
1517 /* Check if parameters are valid */
1518 if(PreviousMode
!= KernelMode
) {
1522 ProbeForWriteUlong(SuspendCount
);
1526 Status
= _SEH_GetExceptionCode();
1531 /* Reference the Object */
1532 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1533 THREAD_SUSPEND_RESUME
,
1539 /* Check for Success */
1540 if (NT_SUCCESS(Status
)) {
1542 /* Call the Kernel Function */
1543 PreviousState
= KeAlertResumeThread(&Thread
->Tcb
);
1545 /* Dereference Object */
1546 ObDereferenceObject(Thread
);
1552 *SuspendCount
= PreviousState
;
1556 Status
= _SEH_GetExceptionCode();
1573 NtAlertThread (IN HANDLE ThreadHandle
)
1575 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1579 /* Reference the Object */
1580 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1581 THREAD_SUSPEND_RESUME
,
1587 /* Check for Success */
1588 if (NT_SUCCESS(Status
)) {
1591 * Do an alert depending on the processor mode. If some kmode code wants to
1592 * enforce a umode alert it should call KeAlertThread() directly. If kmode
1593 * code wants to do a kmode alert it's sufficient to call it with Zw or just
1594 * use KeAlertThread() directly
1596 KeAlertThread(&Thread
->Tcb
, PreviousMode
);
1598 /* Dereference Object */
1599 ObDereferenceObject(Thread
);
1608 NtDelayExecution(IN BOOLEAN Alertable
,
1609 IN PLARGE_INTEGER DelayInterval
)
1611 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1612 LARGE_INTEGER SafeInterval
;
1615 /* Check if parameters are valid */
1616 if(PreviousMode
!= KernelMode
) {
1618 Status
= STATUS_SUCCESS
;
1622 /* make a copy on the kernel stack and let DelayInterval point to it so
1623 we don't need to wrap KeDelayExecutionThread in SEH! */
1624 SafeInterval
= ProbeForReadLargeInteger(DelayInterval
);
1625 DelayInterval
= &SafeInterval
;
1629 Status
= _SEH_GetExceptionCode();
1632 if (!NT_SUCCESS(Status
))
1638 /* Call the Kernel Function */
1639 Status
= KeDelayExecutionThread(PreviousMode
,