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 #define THREAD_ALERT_INCREMENT 2
19 extern EX_WORK_QUEUE ExWorkerQueue
[MaximumWorkQueue
];
22 * PURPOSE: List of threads associated with each priority level
24 LIST_ENTRY PriorityListHead
[MAXIMUM_PRIORITY
];
25 static ULONG PriorityListMask
= 0;
26 ULONG IdleProcessorMask
= 0;
27 extern BOOLEAN DoneInitYet
;
29 /* FUNCTIONS *****************************************************************/
33 KiRequestReschedule(CCHAR Processor
)
37 Pcr
= (PKPCR
)(KPCR_BASE
+ Processor
* PAGE_SIZE
);
38 Pcr
->Prcb
->QuantumEnd
= TRUE
;
39 KiIpiSendRequest(1 << Processor
, IPI_REQUEST_DPC
);
44 KiInsertIntoThreadList(KPRIORITY Priority
,
47 ASSERT(THREAD_STATE_READY
== Thread
->State
);
48 ASSERT(Thread
->Priority
== Priority
);
50 if (Priority
>= MAXIMUM_PRIORITY
|| Priority
< LOW_PRIORITY
) {
52 DPRINT1("Invalid thread priority (%d)\n", Priority
);
56 InsertTailList(&PriorityListHead
[Priority
], &Thread
->QueueListEntry
);
57 PriorityListMask
|= (1 << Priority
);
62 KiRemoveFromThreadList(PKTHREAD Thread
)
64 ASSERT(THREAD_STATE_READY
== Thread
->State
);
65 RemoveEntryList(&Thread
->QueueListEntry
);
66 if (IsListEmpty(&PriorityListHead
[(ULONG
)Thread
->Priority
])) {
68 PriorityListMask
&= ~(1 << Thread
->Priority
);
74 KiScanThreadList(KPRIORITY Priority
,
77 PLIST_ENTRY current_entry
;
81 Mask
= (1 << Priority
);
83 if (PriorityListMask
& Mask
) {
85 current_entry
= PriorityListHead
[Priority
].Flink
;
87 while (current_entry
!= &PriorityListHead
[Priority
]) {
89 current
= CONTAINING_RECORD(current_entry
, KTHREAD
, QueueListEntry
);
91 if (current
->State
!= THREAD_STATE_READY
) {
93 DPRINT1("%d/%d\n", ¤t
, current
->State
);
96 ASSERT(current
->State
== THREAD_STATE_READY
);
98 if (current
->Affinity
& Affinity
) {
100 KiRemoveFromThreadList(current
);
104 current_entry
= current_entry
->Flink
;
113 KiDispatchThreadNoLock(ULONG NewThreadStatus
)
115 KPRIORITY CurrentPriority
;
118 PKTHREAD CurrentThread
= KeGetCurrentThread();
120 DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
121 CurrentThread
, NewThreadStatus
, CurrentThread
->State
);
123 CurrentThread
->State
= (UCHAR
)NewThreadStatus
;
125 if (NewThreadStatus
== THREAD_STATE_READY
) {
127 KiInsertIntoThreadList(CurrentThread
->Priority
,
131 Affinity
= 1 << KeGetCurrentProcessorNumber();
133 for (CurrentPriority
= HIGH_PRIORITY
; CurrentPriority
>= LOW_PRIORITY
; CurrentPriority
--) {
135 Candidate
= KiScanThreadList(CurrentPriority
, Affinity
);
137 if (Candidate
== CurrentThread
) {
139 Candidate
->State
= THREAD_STATE_RUNNING
;
140 KeReleaseDispatcherDatabaseLockFromDpcLevel();
144 if (Candidate
!= NULL
) {
149 DPRINT("Scheduling %x(%d)\n",Candidate
, CurrentPriority
);
151 Candidate
->State
= THREAD_STATE_RUNNING
;
153 OldThread
= CurrentThread
;
154 CurrentThread
= Candidate
;
155 IdleThread
= KeGetCurrentPrcb()->IdleThread
;
157 if (OldThread
== IdleThread
) {
159 IdleProcessorMask
&= ~Affinity
;
161 } else if (CurrentThread
== IdleThread
) {
163 IdleProcessorMask
|= Affinity
;
166 MmUpdatePageDir(PsGetCurrentProcess(),((PETHREAD
)CurrentThread
)->ThreadsProcess
, sizeof(EPROCESS
));
168 /* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */
169 KiArchContextSwitch(CurrentThread
, OldThread
);
174 DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber());
180 KiBlockThread(PNTSTATUS Status
,
185 PKTHREAD Thread
= KeGetCurrentThread();
186 PKWAIT_BLOCK WaitBlock
;
188 if (Thread
->ApcState
.KernelApcPending
) {
190 DPRINT("Dispatching Thread as ready (APC!)\n");
193 WaitBlock
= Thread
->WaitBlockList
;
195 RemoveEntryList (&WaitBlock
->WaitListEntry
);
196 WaitBlock
= WaitBlock
->NextWaitBlock
;
198 Thread
->WaitBlockList
= NULL
;
200 /* Dispatch it and return status */
201 KiDispatchThreadNoLock (THREAD_STATE_READY
);
202 if (Status
!= NULL
) *Status
= STATUS_KERNEL_APC
;
206 /* Set the Thread Data as Requested */
207 DPRINT("Dispatching Thread as blocked\n");
208 Thread
->Alertable
= Alertable
;
209 Thread
->WaitMode
= (UCHAR
)WaitMode
;
210 Thread
->WaitReason
= WaitReason
;
212 /* Dispatch it and return status */
213 KiDispatchThreadNoLock(THREAD_STATE_BLOCKED
);
214 if (Status
!= NULL
) *Status
= Thread
->WaitStatus
;
217 DPRINT("Releasing Dispatcher Lock\n");
218 KfLowerIrql(Thread
->WaitIrql
);
223 KiDispatchThread(ULONG NewThreadStatus
)
227 if (!DoneInitYet
|| KeGetCurrentPrcb()->IdleThread
== NULL
) {
231 OldIrql
= KeAcquireDispatcherDatabaseLock();
232 KiDispatchThreadNoLock(NewThreadStatus
);
233 KeLowerIrql(OldIrql
);
238 KiUnblockThread(PKTHREAD Thread
,
239 PNTSTATUS WaitStatus
,
242 if (THREAD_STATE_TERMINATED_1
== Thread
->State
||
243 THREAD_STATE_TERMINATED_2
== Thread
->State
) {
245 DPRINT("Can't unblock thread %d because it's terminating\n",
246 Thread
->Cid
.UniqueThread
);
248 } else if (THREAD_STATE_READY
== Thread
->State
||
249 THREAD_STATE_RUNNING
== Thread
->State
) {
251 DPRINT("Can't unblock thread %d because it's ready or running\n",
252 Thread
->Cid
.UniqueThread
);
259 /* FIXME: This propably isn't the right way to do it... */
260 /* No it's not... i'll fix it later-- Alex */
261 if (Thread
->Priority
< LOW_REALTIME_PRIORITY
&&
262 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2) {
264 if (!Thread
->PriorityDecrement
&& !Thread
->DisableBoost
) {
266 Thread
->Priority
= Thread
->BasePriority
+ Increment
;
267 Thread
->PriorityDecrement
= Increment
;
272 Thread
->Quantum
= Thread
->ApcState
.Process
->ThreadQuantum
;
275 if (WaitStatus
!= NULL
) {
277 Thread
->WaitStatus
= *WaitStatus
;
280 Thread
->State
= 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 KiSuspendThreadKernelRoutine(PKAPC Apc
,
318 PKNORMAL_ROUTINE
* NormalRoutine
,
319 PVOID
* NormalContext
,
320 PVOID
* SystemArgument1
,
321 PVOID
* SystemArguemnt2
)
327 KiSuspendThreadNormalRoutine(PVOID NormalContext
,
328 PVOID SystemArgument1
,
329 PVOID SystemArgument2
)
331 PKTHREAD CurrentThread
= KeGetCurrentThread();
333 /* Non-alertable kernel-mode suspended wait */
334 DPRINT1("Waiting...\n");
335 KeWaitForSingleObject(&CurrentThread
->SuspendSemaphore
,
340 DPRINT1("Done Waiting\n");
345 KeRundownThread(VOID
)
348 PKTHREAD Thread
= KeGetCurrentThread();
349 PLIST_ENTRY CurrentEntry
;
352 DPRINT("KeRundownThread: %x\n", Thread
);
354 /* Lock the Dispatcher Database */
355 OldIrql
= KeAcquireDispatcherDatabaseLock();
357 CurrentEntry
= Thread
->MutantListHead
.Flink
;
358 while (CurrentEntry
!= &Thread
->MutantListHead
) {
361 Mutant
= CONTAINING_RECORD(CurrentEntry
, KMUTANT
, MutantListEntry
);
362 ASSERT(Mutant
->ApcDisable
);
364 /* Uncondtionally abandon it */
365 DPRINT("Abandonning the Mutant\n");
366 Mutant
->Header
.SignalState
= 1;
367 Mutant
->Abandoned
= TRUE
;
368 Mutant
->OwnerThread
= NULL
;
369 RemoveEntryList(&Mutant
->MutantListEntry
);
371 /* Check if the Wait List isn't empty */
372 DPRINT("Checking whether to wake the Mutant\n");
373 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
)) {
375 /* Wake the Mutant */
376 DPRINT("Waking the Mutant\n");
377 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
381 /* Release the Lock */
382 KeReleaseDispatcherDatabaseLock(OldIrql
);
387 KeResumeThread(PKTHREAD Thread
)
392 DPRINT1("KeResumeThread (Thread %p called). %x, %x\n", Thread
, Thread
->SuspendCount
, Thread
->FreezeCount
);
394 /* Lock the Dispatcher */
395 OldIrql
= KeAcquireDispatcherDatabaseLock();
397 /* Save the Old Count */
398 PreviousCount
= Thread
->SuspendCount
;
400 /* Check if it existed */
403 Thread
->SuspendCount
--;
405 /* Decrease the current Suspend Count and Check Freeze Count */
406 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
)) {
408 /* Signal the Suspend Semaphore */
409 Thread
->SuspendSemaphore
.Header
.SignalState
++;
410 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
414 /* Release Lock and return the Old State */
415 KeReleaseDispatcherDatabaseLock(OldIrql
);
416 return PreviousCount
;
421 KiInsertQueueApc(PKAPC Apc
,
422 KPRIORITY PriorityBoost
);
426 KeSuspendThread(PKTHREAD Thread
)
431 DPRINT1("KeSuspendThread (Thread %p called). %x, %x\n", Thread
, Thread
->SuspendCount
, Thread
->FreezeCount
);
433 /* Lock the Dispatcher */
434 OldIrql
= KeAcquireDispatcherDatabaseLock();
436 /* Save the Old Count */
437 PreviousCount
= Thread
->SuspendCount
;
440 Thread
->SuspendCount
++;
442 /* Check if we should suspend it */
443 if (!PreviousCount
&& !Thread
->FreezeCount
) {
446 if (!KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
)) {
448 /* Unsignal the Semaphore, the APC already got inserted */
449 Thread
->SuspendSemaphore
.Header
.SignalState
--;
453 /* Release Lock and return the Old State */
454 KeReleaseDispatcherDatabaseLock(OldIrql
);
455 return PreviousCount
;
460 KeForceResumeThread(IN PKTHREAD Thread
)
465 /* Lock the Dispatcher Database and the APC Queue */
466 OldIrql
= KeAcquireDispatcherDatabaseLock();
468 /* Save the old Suspend Count */
469 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
471 /* If the thread is suspended, wake it up!!! */
474 /* Unwait it completely */
475 Thread
->SuspendCount
= 0;
476 Thread
->FreezeCount
= 0;
478 /* Signal and satisfy */
479 Thread
->SuspendSemaphore
.Header
.SignalState
++;
480 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
483 /* Release Lock and return the Old State */
484 KeReleaseDispatcherDatabaseLock(OldIrql
);
485 return PreviousCount
;
490 KeAlertResumeThread(IN PKTHREAD Thread
)
495 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
497 /* Lock the Dispatcher Database and the APC Queue */
498 OldIrql
= KeAcquireDispatcherDatabaseLock();
499 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
501 /* Return if Thread is already alerted. */
502 if (Thread
->Alerted
[KernelMode
] == FALSE
) {
504 /* If it's Blocked, unblock if it we should */
505 if (Thread
->State
== THREAD_STATE_BLOCKED
&& Thread
->Alertable
) {
507 DPRINT("Aborting Wait\n");
508 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
512 /* If not, simply Alert it */
513 Thread
->Alerted
[KernelMode
] = TRUE
;
517 /* Save the old Suspend Count */
518 PreviousCount
= Thread
->SuspendCount
;
520 /* If the thread is suspended, decrease one of the suspend counts */
523 /* Decrease count. If we are now zero, unwait it completely */
524 if (--Thread
->SuspendCount
) {
526 /* Signal and satisfy */
527 Thread
->SuspendSemaphore
.Header
.SignalState
++;
528 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
532 /* Release Locks and return the Old State */
533 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
534 KeReleaseDispatcherDatabaseLock(OldIrql
);
535 return PreviousCount
;
540 KeAlertThread(PKTHREAD Thread
,
541 KPROCESSOR_MODE AlertMode
)
544 BOOLEAN PreviousState
;
546 /* Acquire the Dispatcher Database Lock */
547 OldIrql
= KeAcquireDispatcherDatabaseLock();
549 /* Save the Previous State */
550 PreviousState
= Thread
->Alerted
[AlertMode
];
552 /* Return if Thread is already alerted. */
553 if (PreviousState
== FALSE
) {
555 /* If it's Blocked, unblock if it we should */
556 if (Thread
->State
== THREAD_STATE_BLOCKED
&&
557 (AlertMode
== KernelMode
|| Thread
->WaitMode
== AlertMode
) &&
560 DPRINT("Aborting Wait\n");
561 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
565 /* If not, simply Alert it */
566 Thread
->Alerted
[AlertMode
] = TRUE
;
570 /* Release the Dispatcher Lock */
571 KeReleaseDispatcherDatabaseLock(OldIrql
);
573 /* Return the old state */
574 return PreviousState
;
582 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
588 IN PVOID ThreadState
)
594 * FUNCTION: Initialize the microkernel state of the thread
598 KeInitializeThread(PKPROCESS Process
,
604 extern unsigned int init_stack_top
;
605 extern unsigned int init_stack
;
606 PMEMORY_AREA StackArea
;
608 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
610 /* Initialize the Boundary Address */
611 BoundaryAddressMultiple
.QuadPart
= 0;
613 /* Initalize the Dispatcher Header */
614 KeInitializeDispatcherHeader(&Thread
->DispatcherHeader
,
618 InitializeListHead(&Thread
->MutantListHead
);
620 /* If this is isn't the first thread, allocate the Kernel Stack */
623 PFN_TYPE Page
[MM_STACK_SIZE
/ PAGE_SIZE
];
626 MmLockAddressSpace(MmGetKernelAddressSpace());
627 Status
= MmCreateMemoryArea(NULL
,
628 MmGetKernelAddressSpace(),
629 MEMORY_AREA_KERNEL_STACK
,
636 BoundaryAddressMultiple
);
637 MmUnlockAddressSpace(MmGetKernelAddressSpace());
639 /* Check for Success */
640 if (!NT_SUCCESS(Status
)) {
642 DPRINT1("Failed to create thread stack\n");
647 for (i
= 0; i
< (MM_STACK_SIZE
/ PAGE_SIZE
); i
++) {
649 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, TRUE
, &Page
[i
]);
651 /* Check for success */
652 if (!NT_SUCCESS(Status
)) {
658 /* Create a Virtual Mapping for it */
659 Status
= MmCreateVirtualMapping(NULL
,
663 MM_STACK_SIZE
/ PAGE_SIZE
);
665 /* Check for success */
666 if (!NT_SUCCESS(Status
)) {
671 /* Set the Kernel Stack */
672 Thread
->InitialStack
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
673 Thread
->StackBase
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
674 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
;
675 Thread
->KernelStack
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
679 /* Use the Initial Stack */
680 Thread
->InitialStack
= (PCHAR
)init_stack_top
;
681 Thread
->StackBase
= (PCHAR
)init_stack_top
;
682 Thread
->StackLimit
= (ULONG_PTR
)init_stack
;
683 Thread
->KernelStack
= (PCHAR
)init_stack_top
;
687 * Establish the pde's for the new stack and the thread structure within the
688 * address space of the new process. They are accessed while taskswitching or
689 * while handling page faults. At this point it isn't possible to call the
690 * page fault handler for the missing pde's.
692 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
->StackLimit
, MM_STACK_SIZE
);
693 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
695 /* Set the Thread to initalized */
696 Thread
->State
= THREAD_STATE_INITIALIZED
;
698 /* The Native API function will initialize the TEB field later */
701 /* Initialize stuff to zero */
702 Thread
->TlsArray
= NULL
;
703 Thread
->DebugActive
= 0;
704 Thread
->Alerted
[0] = 0;
705 Thread
->Alerted
[1] = 0;
708 /* Set up FPU/NPX Stuff */
709 Thread
->NpxState
= NPX_STATE_INVALID
;
712 /* Setup APC Fields */
713 InitializeListHead(&Thread
->ApcState
.ApcListHead
[0]);
714 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
715 Thread
->ApcState
.Process
= Process
;
716 Thread
->ApcState
.KernelApcInProgress
= 0;
717 Thread
->ApcState
.KernelApcPending
= 0;
718 Thread
->ApcState
.UserApcPending
= 0;
719 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
720 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
721 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
722 Thread
->ApcQueueable
= TRUE
;
723 RtlZeroMemory(&Thread
->SavedApcState
, sizeof(KAPC_STATE
));
724 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
726 /* Setup Wait Fields */
727 Thread
->WaitStatus
= STATUS_SUCCESS
;
728 Thread
->WaitIrql
= PASSIVE_LEVEL
;
729 Thread
->WaitMode
= 0;
730 Thread
->WaitNext
= FALSE
;
731 Thread
->WaitListEntry
.Flink
= NULL
;
732 Thread
->WaitListEntry
.Blink
= NULL
;
733 Thread
->WaitTime
= 0;
734 Thread
->WaitBlockList
= NULL
;
735 RtlZeroMemory(Thread
->WaitBlock
, sizeof(KWAIT_BLOCK
) * 4);
736 RtlZeroMemory(&Thread
->Timer
, sizeof(KTIMER
));
737 KeInitializeTimer(&Thread
->Timer
);
739 /* Setup scheduler Fields */
740 Thread
->BasePriority
= Process
->BasePriority
;
741 Thread
->DecrementCount
= 0;
742 Thread
->PriorityDecrement
= 0;
743 Thread
->Quantum
= Process
->ThreadQuantum
;
744 Thread
->Saturation
= 0;
745 Thread
->Priority
= Process
->BasePriority
;
746 Thread
->UserAffinity
= Process
->Affinity
;
747 Thread
->SystemAffinityActive
= 0;
748 Thread
->Affinity
= Process
->Affinity
;
749 Thread
->Preempted
= 0;
750 Thread
->ProcessReadyQueue
= 0;
751 Thread
->KernelStackResident
= 1;
752 Thread
->NextProcessor
= 0;
753 Thread
->ContextSwitches
= 0;
755 /* Setup Queue Fields */
756 Thread
->Queue
= NULL
;
757 Thread
->QueueListEntry
.Flink
= NULL
;
758 Thread
->QueueListEntry
.Blink
= NULL
;
760 /* Setup Misc Fields */
761 Thread
->LegoData
= 0;
762 Thread
->PowerState
= 0;
763 Thread
->ServiceTable
= KeServiceDescriptorTable
;
764 Thread
->CallbackStack
= NULL
;
765 Thread
->Win32Thread
= NULL
;
766 Thread
->TrapFrame
= NULL
;
767 Thread
->EnableStackSwap
= 0;
768 Thread
->LargeStack
= 0;
769 Thread
->ResourceIndex
= 0;
770 Thread
->PreviousMode
= KernelMode
;
771 Thread
->KernelTime
= 0;
772 Thread
->UserTime
= 0;
773 Thread
->AutoAlignment
= Process
->AutoAlignment
;
775 /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
777 Thread
->WaitBlock
[3].Object
= (PVOID
)&Thread
->Timer
;
778 Thread
->WaitBlock
[3].Thread
= Thread
;
779 Thread
->WaitBlock
[3].WaitKey
= STATUS_TIMEOUT
;
780 Thread
->WaitBlock
[3].WaitType
= WaitAny
;
781 Thread
->WaitBlock
[3].NextWaitBlock
= NULL
;
782 InsertTailList(&Thread
->Timer
.Header
.WaitListHead
,
783 &Thread
->WaitBlock
[3].WaitListEntry
);
786 /* Initialize the Suspend APC */
787 KeInitializeApc(&Thread
->SuspendApc
,
789 OriginalApcEnvironment
,
790 KiSuspendThreadKernelRoutine
,
792 KiSuspendThreadNormalRoutine
,
796 /* Initialize the Suspend Semaphore */
797 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 128);
799 /* Insert the Thread into the Process's Thread List */
800 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
802 /* Set up the Suspend Counts */
803 Thread
->FreezeCount
= 0;
804 Thread
->SuspendCount
= 0;
806 /* Do x86 specific part */
814 KeQueryPriorityThread (IN PKTHREAD Thread
)
816 return Thread
->Priority
;
824 KeQueryRuntimeThread(IN PKTHREAD Thread
,
827 /* Return the User Time */
828 *UserTime
= Thread
->UserTime
;
830 /* Return the Kernel Time */
831 return Thread
->KernelTime
;
835 KeFreeStackPage(PVOID Context
,
836 MEMORY_AREA
* MemoryArea
,
842 ASSERT(SwapEntry
== 0);
843 if (Page
) MmReleasePageMemoryConsumer(MC_NPPOOL
, Page
);
847 KeReleaseThread(PKTHREAD Thread
)
849 * FUNCTION: Releases the resource allocated for a thread by
851 * NOTE: The thread had better not be running when this is called
854 extern unsigned int init_stack
;
856 /* FIXME - lock the process */
857 RemoveEntryList(&Thread
->ThreadListEntry
);
859 if (Thread
->StackLimit
!= (ULONG_PTR
)init_stack
)
861 MmLockAddressSpace(MmGetKernelAddressSpace());
862 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
863 (PVOID
)Thread
->StackLimit
,
866 MmUnlockAddressSpace(MmGetKernelAddressSpace());
868 Thread
->StackLimit
= 0;
869 Thread
->InitialStack
= NULL
;
870 Thread
->StackBase
= NULL
;
871 Thread
->KernelStack
= NULL
;
872 return(STATUS_SUCCESS
);
880 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
882 PKTHREAD Thread
= KeGetCurrentThread();
883 BOOLEAN PreviousState
;
886 /* Lock the Dispatcher Database */
887 OldIrql
= KeAcquireDispatcherDatabaseLock();
890 PreviousState
= Thread
->EnableStackSwap
;
893 Thread
->EnableStackSwap
= Enable
;
895 /* No, Release Lock */
896 KeReleaseDispatcherDatabaseLock(OldIrql
);
898 /* Return Old State */
899 return PreviousState
;
907 KeRevertToUserAffinityThread(VOID
)
909 PKTHREAD CurrentThread
= KeGetCurrentThread();
912 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
914 /* Lock the Dispatcher Database */
915 OldIrql
= KeAcquireDispatcherDatabaseLock();
917 /* Return to User Affinity */
918 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
920 /* Disable System Affinity */
921 CurrentThread
->SystemAffinityActive
= FALSE
;
923 /* Check if we need to Dispatch a New thread */
924 if (CurrentThread
->Affinity
& (1 << KeGetCurrentProcessorNumber())) {
926 /* No, just release */
927 KeReleaseDispatcherDatabaseLock(OldIrql
);
931 /* We need to dispatch a new thread */
932 CurrentThread
->WaitIrql
= OldIrql
;
933 KiDispatchThreadNoLock(THREAD_STATE_READY
);
934 KeLowerIrql(OldIrql
);
943 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
946 CCHAR PreviousIdealProcessor
;
949 /* Lock the Dispatcher Database */
950 OldIrql
= KeAcquireDispatcherDatabaseLock();
952 /* Save Old Ideal Processor */
953 PreviousIdealProcessor
= Thread
->IdealProcessor
;
955 /* Set New Ideal Processor */
956 Thread
->IdealProcessor
= Processor
;
959 KeReleaseDispatcherDatabaseLock(OldIrql
);
961 /* Return Old Ideal Processor */
962 return PreviousIdealProcessor
;
970 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
972 PKTHREAD CurrentThread
= KeGetCurrentThread();
975 ASSERT(Affinity
& ((1 << KeNumberProcessors
) - 1));
977 /* Lock the Dispatcher Database */
978 OldIrql
= KeAcquireDispatcherDatabaseLock();
980 /* Set the System Affinity Specified */
981 CurrentThread
->Affinity
= Affinity
;
983 /* Enable System Affinity */
984 CurrentThread
->SystemAffinityActive
= TRUE
;
986 /* Check if we need to Dispatch a New thread */
987 if (Affinity
& (1 << KeGetCurrentProcessorNumber())) {
989 /* No, just release */
990 KeReleaseDispatcherDatabaseLock(OldIrql
);
994 /* We need to dispatch a new thread */
995 CurrentThread
->WaitIrql
= OldIrql
;
996 KiDispatchThreadNoLock(THREAD_STATE_READY
);
997 KeLowerIrql(OldIrql
);
1005 KeSetBasePriorityThread (PKTHREAD Thread
,
1008 * Sets thread's base priority relative to the process' base priority
1009 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
1017 else if (Increment
> 2)
1021 Priority
= ((PETHREAD
)Thread
)->ThreadsProcess
->Pcb
.BasePriority
+ Increment
;
1022 if (Priority
< LOW_PRIORITY
)
1024 Priority
= LOW_PRIORITY
;
1026 else if (Priority
>= MAXIMUM_PRIORITY
)
1028 Thread
->BasePriority
= HIGH_PRIORITY
;
1030 KeSetPriorityThread(Thread
, Priority
);
1039 KeSetPriorityThread(PKTHREAD Thread
,
1042 KPRIORITY OldPriority
;
1044 PKTHREAD CurrentThread
;
1049 if (Priority
< LOW_PRIORITY
|| Priority
>= MAXIMUM_PRIORITY
) {
1054 OldIrql
= KeAcquireDispatcherDatabaseLock();
1056 OldPriority
= Thread
->Priority
;
1058 if (OldPriority
!= Priority
) {
1060 CurrentThread
= KeGetCurrentThread();
1062 if (Thread
->State
== THREAD_STATE_READY
) {
1064 KiRemoveFromThreadList(Thread
);
1065 Thread
->BasePriority
= Thread
->Priority
= (CHAR
)Priority
;
1066 KiInsertIntoThreadList(Priority
, Thread
);
1068 if (CurrentThread
->Priority
< Priority
) {
1070 KiDispatchThreadNoLock(THREAD_STATE_READY
);
1071 KeLowerIrql(OldIrql
);
1072 return (OldPriority
);
1075 } else if (Thread
->State
== THREAD_STATE_RUNNING
) {
1077 Thread
->BasePriority
= Thread
->Priority
= (CHAR
)Priority
;
1079 if (Priority
< OldPriority
) {
1081 /* Check for threads with a higher priority */
1082 Mask
= ~((1 << (Priority
+ 1)) - 1);
1083 if (PriorityListMask
& Mask
) {
1085 if (Thread
== CurrentThread
) {
1087 KiDispatchThreadNoLock(THREAD_STATE_READY
);
1088 KeLowerIrql(OldIrql
);
1089 return (OldPriority
);
1093 for (i
= 0; i
< KeNumberProcessors
; i
++) {
1095 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1097 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
1099 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1100 KiRequestReschedule(i
);
1101 KeLowerIrql(OldIrql
);
1102 return (OldPriority
);
1110 Thread
->BasePriority
= Thread
->Priority
= (CHAR
)Priority
;
1114 KeReleaseDispatcherDatabaseLock(OldIrql
);
1115 return(OldPriority
);
1121 * Sets thread's affinity
1125 KeSetAffinityThread(PKTHREAD Thread
,
1131 KAFFINITY ProcessorMask
;
1133 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread
, Affinity
);
1135 ASSERT(Affinity
& ((1 << KeNumberProcessors
) - 1));
1137 OldIrql
= KeAcquireDispatcherDatabaseLock();
1139 Thread
->UserAffinity
= Affinity
;
1141 if (Thread
->SystemAffinityActive
== FALSE
) {
1143 Thread
->Affinity
= Affinity
;
1145 if (Thread
->State
== THREAD_STATE_RUNNING
) {
1147 ProcessorMask
= 1 << KeGetCurrentKPCR()->ProcessorNumber
;
1148 if (Thread
== KeGetCurrentThread()) {
1150 if (!(Affinity
& ProcessorMask
)) {
1152 KiDispatchThreadNoLock(THREAD_STATE_READY
);
1153 KeLowerIrql(OldIrql
);
1154 return STATUS_SUCCESS
;
1159 for (i
= 0; i
< KeNumberProcessors
; i
++) {
1161 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1162 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
1164 if (!(Affinity
& ProcessorMask
)) {
1166 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1167 KiRequestReschedule(i
);
1168 KeLowerIrql(OldIrql
);
1169 return STATUS_SUCCESS
;
1176 ASSERT (i
< KeNumberProcessors
);
1181 KeReleaseDispatcherDatabaseLock(OldIrql
);
1182 return STATUS_SUCCESS
;
1188 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1191 KeTerminateThread(IN KPRIORITY Increment
)
1194 PKTHREAD Thread
= KeGetCurrentThread();
1196 /* Lock the Dispatcher Database and the APC Queue */
1197 DPRINT1("Terminating\n");
1198 OldIrql
= KeAcquireDispatcherDatabaseLock();
1200 /* Insert into the Reaper List */
1201 InsertTailList(&PspReaperListHead
, &((PETHREAD
)Thread
)->TerminationPortList
);
1203 /* Check if it's active */
1204 if (PspReaping
== FALSE
) {
1206 /* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */
1208 DPRINT1("Terminating\n");
1209 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1210 &PspReaperWorkItem
.List
,
1214 /* Handle Kernel Queues */
1215 if (Thread
->Queue
) {
1217 DPRINT1("Waking Queue\n");
1218 RemoveEntryList(&Thread
->QueueListEntry
);
1219 KiWakeQueue(Thread
->Queue
);
1222 /* Signal the thread */
1223 Thread
->DispatcherHeader
.SignalState
= TRUE
;
1224 if (IsListEmpty(&Thread
->DispatcherHeader
.WaitListHead
) != TRUE
) {
1227 KiWaitTest((PVOID
)Thread
, Increment
);
1230 /* Find a new Thread */
1231 KiDispatchThreadNoLock(THREAD_STATE_TERMINATED_1
);
1235 * FUNCTION: Tests whether there are any pending APCs for the current thread
1236 * and if so the APCs will be delivered on exit from kernel mode
1240 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
1243 PKTHREAD Thread
= KeGetCurrentThread();
1246 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1248 /* Lock the Dispatcher Database and the APC Queue */
1249 OldIrql
= KeAcquireDispatcherDatabaseLock();
1250 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
1252 /* Save the old State */
1253 OldState
= Thread
->Alerted
[AlertMode
];
1255 /* If the Thread is Alerted, Clear it */
1258 Thread
->Alerted
[AlertMode
] = FALSE
;
1260 } else if ((AlertMode
== UserMode
) && (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))) {
1262 /* If the mode is User and the Queue isn't empty, set Pending */
1263 Thread
->ApcState
.UserApcPending
= TRUE
;
1266 /* Release Locks and return the Old State */
1267 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
1268 KeReleaseDispatcherDatabaseLock(OldIrql
);
1273 KiServiceCheck (VOID
)
1275 PKTHREAD Thread
= KeGetCurrentThread();
1277 /* Check if we need to inialize Win32 for this Thread */
1278 if (Thread
->ServiceTable
!= KeServiceDescriptorTableShadow
) {
1280 /* We do. Initialize it and save the new table */
1281 PsInitWin32Thread((PETHREAD
)Thread
);
1282 Thread
->ServiceTable
= KeServiceDescriptorTableShadow
;
1292 NtAlertResumeThread(IN HANDLE ThreadHandle
,
1293 OUT PULONG SuspendCount
)
1295 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1298 ULONG PreviousState
;
1300 /* Check if parameters are valid */
1301 if(PreviousMode
!= KernelMode
) {
1305 ProbeForWrite(SuspendCount
,
1311 Status
= _SEH_GetExceptionCode();
1316 /* Reference the Object */
1317 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1318 THREAD_SUSPEND_RESUME
,
1324 /* Check for Success */
1325 if (NT_SUCCESS(Status
)) {
1327 /* Call the Kernel Function */
1328 PreviousState
= KeAlertResumeThread(&Thread
->Tcb
);
1330 /* Dereference Object */
1331 ObDereferenceObject(Thread
);
1337 *SuspendCount
= PreviousState
;
1341 Status
= _SEH_GetExceptionCode();
1358 NtAlertThread (IN HANDLE ThreadHandle
)
1360 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1364 /* Reference the Object */
1365 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1366 THREAD_SUSPEND_RESUME
,
1372 /* Check for Success */
1373 if (NT_SUCCESS(Status
)) {
1376 * Do an alert depending on the processor mode. If some kmode code wants to
1377 * enforce a umode alert it should call KeAlertThread() directly. If kmode
1378 * code wants to do a kmode alert it's sufficient to call it with Zw or just
1379 * use KeAlertThread() directly
1381 KeAlertThread(&Thread
->Tcb
, PreviousMode
);
1383 /* Dereference Object */
1384 ObDereferenceObject(Thread
);
1393 NtDelayExecution(IN BOOLEAN Alertable
,
1394 IN PLARGE_INTEGER DelayInterval
)
1396 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1397 LARGE_INTEGER SafeInterval
;
1400 /* Check if parameters are valid */
1401 if(PreviousMode
!= KernelMode
) {
1405 ProbeForRead(DelayInterval
,
1406 sizeof(LARGE_INTEGER
),
1409 /* make a copy on the kernel stack and let DelayInterval point to it so
1410 we don't need to wrap KeDelayExecutionThread in SEH! */
1411 SafeInterval
= *DelayInterval
;
1415 Status
= _SEH_GetExceptionCode();
1419 /* Call the Kernel Function */
1420 Status
= KeDelayExecutionThread(PreviousMode
,