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
;
28 extern PETHREAD PspReaperList
;
30 /* FUNCTIONS *****************************************************************/
34 KiRequestReschedule(CCHAR Processor
)
38 Pcr
= (PKPCR
)(KPCR_BASE
+ Processor
* PAGE_SIZE
);
39 Pcr
->Prcb
->QuantumEnd
= TRUE
;
40 KiIpiSendRequest(1 << Processor
, IPI_REQUEST_DPC
);
45 KiInsertIntoThreadList(KPRIORITY Priority
,
48 ASSERT(THREAD_STATE_READY
== Thread
->State
);
49 ASSERT(Thread
->Priority
== Priority
);
51 if (Priority
>= MAXIMUM_PRIORITY
|| Priority
< LOW_PRIORITY
) {
53 DPRINT1("Invalid thread priority (%d)\n", Priority
);
57 InsertTailList(&PriorityListHead
[Priority
], &Thread
->QueueListEntry
);
58 PriorityListMask
|= (1 << Priority
);
63 KiRemoveFromThreadList(PKTHREAD Thread
)
65 ASSERT(THREAD_STATE_READY
== Thread
->State
);
66 RemoveEntryList(&Thread
->QueueListEntry
);
67 if (IsListEmpty(&PriorityListHead
[(ULONG
)Thread
->Priority
])) {
69 PriorityListMask
&= ~(1 << Thread
->Priority
);
75 KiScanThreadList(KPRIORITY Priority
,
78 PLIST_ENTRY current_entry
;
82 Mask
= (1 << Priority
);
84 if (PriorityListMask
& Mask
) {
86 current_entry
= PriorityListHead
[Priority
].Flink
;
88 while (current_entry
!= &PriorityListHead
[Priority
]) {
90 current
= CONTAINING_RECORD(current_entry
, KTHREAD
, QueueListEntry
);
92 if (current
->State
!= THREAD_STATE_READY
) {
94 DPRINT1("%d/%d\n", ¤t
, current
->State
);
97 ASSERT(current
->State
== THREAD_STATE_READY
);
99 if (current
->Affinity
& Affinity
) {
101 KiRemoveFromThreadList(current
);
105 current_entry
= current_entry
->Flink
;
114 KiDispatchThreadNoLock(ULONG NewThreadStatus
)
116 KPRIORITY CurrentPriority
;
119 PKTHREAD CurrentThread
= KeGetCurrentThread();
121 DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
122 CurrentThread
, NewThreadStatus
, CurrentThread
->State
);
124 CurrentThread
->State
= (UCHAR
)NewThreadStatus
;
126 if (NewThreadStatus
== THREAD_STATE_READY
) {
128 KiInsertIntoThreadList(CurrentThread
->Priority
,
132 Affinity
= 1 << KeGetCurrentProcessorNumber();
134 for (CurrentPriority
= HIGH_PRIORITY
; CurrentPriority
>= LOW_PRIORITY
; CurrentPriority
--) {
136 Candidate
= KiScanThreadList(CurrentPriority
, Affinity
);
138 if (Candidate
== CurrentThread
) {
140 Candidate
->State
= THREAD_STATE_RUNNING
;
141 KeReleaseDispatcherDatabaseLockFromDpcLevel();
145 if (Candidate
!= NULL
) {
150 DPRINT("Scheduling %x(%d)\n",Candidate
, CurrentPriority
);
152 Candidate
->State
= THREAD_STATE_RUNNING
;
154 OldThread
= CurrentThread
;
155 CurrentThread
= Candidate
;
156 IdleThread
= KeGetCurrentPrcb()->IdleThread
;
158 if (OldThread
== IdleThread
) {
160 IdleProcessorMask
&= ~Affinity
;
162 } else if (CurrentThread
== IdleThread
) {
164 IdleProcessorMask
|= Affinity
;
167 MmUpdatePageDir(PsGetCurrentProcess(),((PETHREAD
)CurrentThread
)->ThreadsProcess
, sizeof(EPROCESS
));
169 /* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */
170 KiArchContextSwitch(CurrentThread
, OldThread
);
175 DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber());
181 KiBlockThread(PNTSTATUS Status
,
186 PKTHREAD Thread
= KeGetCurrentThread();
187 PKWAIT_BLOCK WaitBlock
;
189 if (Thread
->ApcState
.KernelApcPending
) {
191 DPRINT("Dispatching Thread as ready (APC!)\n");
194 WaitBlock
= Thread
->WaitBlockList
;
196 RemoveEntryList (&WaitBlock
->WaitListEntry
);
197 WaitBlock
= WaitBlock
->NextWaitBlock
;
199 Thread
->WaitBlockList
= NULL
;
201 /* Dispatch it and return status */
202 KiDispatchThreadNoLock (THREAD_STATE_READY
);
203 if (Status
!= NULL
) *Status
= STATUS_KERNEL_APC
;
207 /* Set the Thread Data as Requested */
208 DPRINT("Dispatching Thread as blocked\n");
209 Thread
->Alertable
= Alertable
;
210 Thread
->WaitMode
= (UCHAR
)WaitMode
;
211 Thread
->WaitReason
= WaitReason
;
213 /* Dispatch it and return status */
214 KiDispatchThreadNoLock(THREAD_STATE_BLOCKED
);
215 if (Status
!= NULL
) *Status
= Thread
->WaitStatus
;
218 DPRINT("Releasing Dispatcher Lock\n");
219 KfLowerIrql(Thread
->WaitIrql
);
224 KiDispatchThread(ULONG NewThreadStatus
)
228 if (!DoneInitYet
|| KeGetCurrentPrcb()->IdleThread
== NULL
) {
232 OldIrql
= KeAcquireDispatcherDatabaseLock();
233 KiDispatchThreadNoLock(NewThreadStatus
);
234 KeLowerIrql(OldIrql
);
239 KiUnblockThread(PKTHREAD Thread
,
240 PNTSTATUS WaitStatus
,
243 if (THREAD_STATE_TERMINATED_1
== Thread
->State
||
244 THREAD_STATE_TERMINATED_2
== Thread
->State
) {
246 DPRINT("Can't unblock thread 0x%x because it's terminating\n",
249 } else if (THREAD_STATE_READY
== Thread
->State
||
250 THREAD_STATE_RUNNING
== Thread
->State
) {
252 DPRINT("Can't unblock thread 0x%x because it's %s\n",
253 Thread
, (Thread
->State
== THREAD_STATE_READY
? "ready" : "running"));
260 /* FIXME: This propably isn't the right way to do it... */
261 /* No it's not... i'll fix it later-- Alex */
262 if (Thread
->Priority
< LOW_REALTIME_PRIORITY
&&
263 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2) {
265 if (!Thread
->PriorityDecrement
&& !Thread
->DisableBoost
) {
267 Thread
->Priority
= Thread
->BasePriority
+ Increment
;
268 Thread
->PriorityDecrement
= Increment
;
273 Thread
->Quantum
= Thread
->ApcState
.Process
->ThreadQuantum
;
276 if (WaitStatus
!= NULL
) {
278 Thread
->WaitStatus
= *WaitStatus
;
281 Thread
->State
= THREAD_STATE_READY
;
282 KiInsertIntoThreadList(Thread
->Priority
, Thread
);
283 Processor
= KeGetCurrentProcessorNumber();
284 Affinity
= Thread
->Affinity
;
286 if (!(IdleProcessorMask
& (1 << Processor
) & Affinity
) &&
287 (IdleProcessorMask
& ~(1 << Processor
) & Affinity
)) {
291 for (i
= 0; i
< KeNumberProcessors
- 1; i
++) {
295 if (Processor
>= KeNumberProcessors
) {
300 if (IdleProcessorMask
& (1 << Processor
) & Affinity
) {
303 * Reschedule the threads on an other processor
305 KeReleaseDispatcherDatabaseLockFromDpcLevel();
306 KiRequestReschedule(Processor
);
307 KeAcquireDispatcherDatabaseLockAtDpcLevel();
318 KiSuspendThreadKernelRoutine(PKAPC Apc
,
319 PKNORMAL_ROUTINE
* NormalRoutine
,
320 PVOID
* NormalContext
,
321 PVOID
* SystemArgument1
,
322 PVOID
* SystemArguemnt2
)
328 KiSuspendThreadNormalRoutine(PVOID NormalContext
,
329 PVOID SystemArgument1
,
330 PVOID SystemArgument2
)
332 PKTHREAD CurrentThread
= KeGetCurrentThread();
334 /* Non-alertable kernel-mode suspended wait */
335 DPRINT("Waiting...\n");
336 KeWaitForSingleObject(&CurrentThread
->SuspendSemaphore
,
341 DPRINT("Done Waiting\n");
346 KeRundownThread(VOID
)
349 PKTHREAD Thread
= KeGetCurrentThread();
350 PLIST_ENTRY CurrentEntry
;
353 DPRINT("KeRundownThread: %x\n", Thread
);
355 /* Lock the Dispatcher Database */
356 OldIrql
= KeAcquireDispatcherDatabaseLock();
358 while (!IsListEmpty(&Thread
->MutantListHead
)) {
361 CurrentEntry
= RemoveHeadList(&Thread
->MutantListHead
);
362 Mutant
= CONTAINING_RECORD(CurrentEntry
, KMUTANT
, MutantListEntry
);
363 ASSERT(Mutant
->ApcDisable
== 0);
365 /* Uncondtionally abandon it */
366 DPRINT("Abandonning the Mutant\n");
367 Mutant
->Header
.SignalState
= 1;
368 Mutant
->Abandoned
= TRUE
;
369 Mutant
->OwnerThread
= NULL
;
370 RemoveEntryList(&Mutant
->MutantListEntry
);
372 /* Check if the Wait List isn't empty */
373 DPRINT("Checking whether to wake the Mutant\n");
374 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
)) {
376 /* Wake the Mutant */
377 DPRINT("Waking the Mutant\n");
378 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
382 /* Release the Lock */
383 KeReleaseDispatcherDatabaseLock(OldIrql
);
388 KeResumeThread(PKTHREAD Thread
)
393 DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread
, Thread
->SuspendCount
, Thread
->FreezeCount
);
395 /* Lock the Dispatcher */
396 OldIrql
= KeAcquireDispatcherDatabaseLock();
398 /* Save the Old Count */
399 PreviousCount
= Thread
->SuspendCount
;
401 /* Check if it existed */
404 Thread
->SuspendCount
--;
406 /* Decrease the current Suspend Count and Check Freeze Count */
407 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
)) {
409 /* Signal the Suspend Semaphore */
410 Thread
->SuspendSemaphore
.Header
.SignalState
++;
411 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
415 /* Release Lock and return the Old State */
416 KeReleaseDispatcherDatabaseLock(OldIrql
);
417 return PreviousCount
;
422 KiInsertQueueApc(PKAPC Apc
,
423 KPRIORITY PriorityBoost
);
427 KeSuspendThread(PKTHREAD Thread
)
432 DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread
, Thread
->SuspendCount
, Thread
->FreezeCount
);
434 /* Lock the Dispatcher */
435 OldIrql
= KeAcquireDispatcherDatabaseLock();
437 /* Save the Old Count */
438 PreviousCount
= Thread
->SuspendCount
;
441 Thread
->SuspendCount
++;
443 /* Check if we should suspend it */
444 if (!PreviousCount
&& !Thread
->FreezeCount
) {
447 if (!KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
)) {
449 /* Unsignal the Semaphore, the APC already got inserted */
450 Thread
->SuspendSemaphore
.Header
.SignalState
--;
454 /* Release Lock and return the Old State */
455 KeReleaseDispatcherDatabaseLock(OldIrql
);
456 return PreviousCount
;
461 KeForceResumeThread(IN PKTHREAD Thread
)
466 /* Lock the Dispatcher Database and the APC Queue */
467 OldIrql
= KeAcquireDispatcherDatabaseLock();
469 /* Save the old Suspend Count */
470 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
472 /* If the thread is suspended, wake it up!!! */
475 /* Unwait it completely */
476 Thread
->SuspendCount
= 0;
477 Thread
->FreezeCount
= 0;
479 /* Signal and satisfy */
480 Thread
->SuspendSemaphore
.Header
.SignalState
++;
481 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
484 /* Release Lock and return the Old State */
485 KeReleaseDispatcherDatabaseLock(OldIrql
);
486 return PreviousCount
;
491 KeAlertResumeThread(IN PKTHREAD Thread
)
496 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
498 /* Lock the Dispatcher Database and the APC Queue */
499 OldIrql
= KeAcquireDispatcherDatabaseLock();
500 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
502 /* Return if Thread is already alerted. */
503 if (Thread
->Alerted
[KernelMode
] == FALSE
) {
505 /* If it's Blocked, unblock if it we should */
506 if (Thread
->State
== THREAD_STATE_BLOCKED
&& Thread
->Alertable
) {
508 DPRINT("Aborting Wait\n");
509 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
513 /* If not, simply Alert it */
514 Thread
->Alerted
[KernelMode
] = TRUE
;
518 /* Save the old Suspend Count */
519 PreviousCount
= Thread
->SuspendCount
;
521 /* If the thread is suspended, decrease one of the suspend counts */
524 /* Decrease count. If we are now zero, unwait it completely */
525 if (--Thread
->SuspendCount
) {
527 /* Signal and satisfy */
528 Thread
->SuspendSemaphore
.Header
.SignalState
++;
529 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
533 /* Release Locks and return the Old State */
534 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
535 KeReleaseDispatcherDatabaseLock(OldIrql
);
536 return PreviousCount
;
541 KeAlertThread(PKTHREAD Thread
,
542 KPROCESSOR_MODE AlertMode
)
545 BOOLEAN PreviousState
;
547 /* Acquire the Dispatcher Database Lock */
548 OldIrql
= KeAcquireDispatcherDatabaseLock();
550 /* Save the Previous State */
551 PreviousState
= Thread
->Alerted
[AlertMode
];
553 /* Return if Thread is already alerted. */
554 if (PreviousState
== FALSE
) {
556 /* If it's Blocked, unblock if it we should */
557 if (Thread
->State
== THREAD_STATE_BLOCKED
&&
558 (AlertMode
== KernelMode
|| Thread
->WaitMode
== AlertMode
) &&
561 DPRINT("Aborting Wait\n");
562 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
566 /* If not, simply Alert it */
567 Thread
->Alerted
[AlertMode
] = TRUE
;
571 /* Release the Dispatcher Lock */
572 KeReleaseDispatcherDatabaseLock(OldIrql
);
574 /* Return the old state */
575 return PreviousState
;
583 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
589 IN PVOID ThreadState
)
595 * FUNCTION: Initialize the microkernel state of the thread
599 KeInitializeThread(PKPROCESS Process
,
605 extern unsigned int init_stack_top
;
606 extern unsigned int init_stack
;
607 PMEMORY_AREA StackArea
;
609 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
611 /* Initialize the Boundary Address */
612 BoundaryAddressMultiple
.QuadPart
= 0;
614 /* Initalize the Dispatcher Header */
615 KeInitializeDispatcherHeader(&Thread
->DispatcherHeader
,
619 InitializeListHead(&Thread
->MutantListHead
);
621 /* If this is isn't the first thread, allocate the Kernel Stack */
624 PFN_TYPE Page
[MM_STACK_SIZE
/ PAGE_SIZE
];
627 MmLockAddressSpace(MmGetKernelAddressSpace());
628 Status
= MmCreateMemoryArea(NULL
,
629 MmGetKernelAddressSpace(),
630 MEMORY_AREA_KERNEL_STACK
,
637 BoundaryAddressMultiple
);
638 MmUnlockAddressSpace(MmGetKernelAddressSpace());
640 /* Check for Success */
641 if (!NT_SUCCESS(Status
)) {
643 DPRINT1("Failed to create thread stack\n");
648 for (i
= 0; i
< (MM_STACK_SIZE
/ PAGE_SIZE
); i
++) {
650 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, TRUE
, &Page
[i
]);
652 /* Check for success */
653 if (!NT_SUCCESS(Status
)) {
659 /* Create a Virtual Mapping for it */
660 Status
= MmCreateVirtualMapping(NULL
,
664 MM_STACK_SIZE
/ PAGE_SIZE
);
666 /* Check for success */
667 if (!NT_SUCCESS(Status
)) {
672 /* Set the Kernel Stack */
673 Thread
->InitialStack
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
674 Thread
->StackBase
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
675 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
;
676 Thread
->KernelStack
= (PCHAR
)KernelStack
+ MM_STACK_SIZE
;
680 /* Use the Initial Stack */
681 Thread
->InitialStack
= (PCHAR
)init_stack_top
;
682 Thread
->StackBase
= (PCHAR
)init_stack_top
;
683 Thread
->StackLimit
= (ULONG_PTR
)init_stack
;
684 Thread
->KernelStack
= (PCHAR
)init_stack_top
;
688 * Establish the pde's for the new stack and the thread structure within the
689 * address space of the new process. They are accessed while taskswitching or
690 * while handling page faults. At this point it isn't possible to call the
691 * page fault handler for the missing pde's.
693 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
->StackLimit
, MM_STACK_SIZE
);
694 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
696 /* Set the Thread to initalized */
697 Thread
->State
= THREAD_STATE_INITIALIZED
;
699 /* The Native API function will initialize the TEB field later */
702 /* Initialize stuff to zero */
703 Thread
->TlsArray
= NULL
;
704 Thread
->DebugActive
= 0;
705 Thread
->Alerted
[0] = 0;
706 Thread
->Alerted
[1] = 0;
709 /* Set up FPU/NPX Stuff */
710 Thread
->NpxState
= NPX_STATE_INVALID
;
713 /* Setup APC Fields */
714 InitializeListHead(&Thread
->ApcState
.ApcListHead
[0]);
715 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
716 Thread
->ApcState
.Process
= Process
;
717 Thread
->ApcState
.KernelApcInProgress
= 0;
718 Thread
->ApcState
.KernelApcPending
= 0;
719 Thread
->ApcState
.UserApcPending
= 0;
720 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
721 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
722 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
723 Thread
->ApcQueueable
= TRUE
;
724 RtlZeroMemory(&Thread
->SavedApcState
, sizeof(KAPC_STATE
));
725 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
727 /* Setup Wait Fields */
728 Thread
->WaitStatus
= STATUS_SUCCESS
;
729 Thread
->WaitIrql
= PASSIVE_LEVEL
;
730 Thread
->WaitMode
= 0;
731 Thread
->WaitNext
= FALSE
;
732 Thread
->WaitListEntry
.Flink
= NULL
;
733 Thread
->WaitListEntry
.Blink
= NULL
;
734 Thread
->WaitTime
= 0;
735 Thread
->WaitBlockList
= NULL
;
736 RtlZeroMemory(Thread
->WaitBlock
, sizeof(KWAIT_BLOCK
) * 4);
737 RtlZeroMemory(&Thread
->Timer
, sizeof(KTIMER
));
738 KeInitializeTimer(&Thread
->Timer
);
740 /* Setup scheduler Fields */
741 Thread
->BasePriority
= Process
->BasePriority
;
742 Thread
->DecrementCount
= 0;
743 Thread
->PriorityDecrement
= 0;
744 Thread
->Quantum
= Process
->ThreadQuantum
;
745 Thread
->Saturation
= 0;
746 Thread
->Priority
= Process
->BasePriority
;
747 Thread
->UserAffinity
= Process
->Affinity
;
748 Thread
->SystemAffinityActive
= 0;
749 Thread
->Affinity
= Process
->Affinity
;
750 Thread
->Preempted
= 0;
751 Thread
->ProcessReadyQueue
= 0;
752 Thread
->KernelStackResident
= 1;
753 Thread
->NextProcessor
= 0;
754 Thread
->ContextSwitches
= 0;
756 /* Setup Queue Fields */
757 Thread
->Queue
= NULL
;
758 Thread
->QueueListEntry
.Flink
= NULL
;
759 Thread
->QueueListEntry
.Blink
= NULL
;
761 /* Setup Misc Fields */
762 Thread
->LegoData
= 0;
763 Thread
->PowerState
= 0;
764 Thread
->ServiceTable
= KeServiceDescriptorTable
;
765 Thread
->CallbackStack
= NULL
;
766 Thread
->Win32Thread
= NULL
;
767 Thread
->TrapFrame
= NULL
;
768 Thread
->EnableStackSwap
= 0;
769 Thread
->LargeStack
= 0;
770 Thread
->ResourceIndex
= 0;
771 Thread
->PreviousMode
= KernelMode
;
772 Thread
->KernelTime
= 0;
773 Thread
->UserTime
= 0;
774 Thread
->AutoAlignment
= Process
->AutoAlignment
;
776 /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
778 Thread
->WaitBlock
[3].Object
= (PVOID
)&Thread
->Timer
;
779 Thread
->WaitBlock
[3].Thread
= Thread
;
780 Thread
->WaitBlock
[3].WaitKey
= STATUS_TIMEOUT
;
781 Thread
->WaitBlock
[3].WaitType
= WaitAny
;
782 Thread
->WaitBlock
[3].NextWaitBlock
= NULL
;
783 InsertTailList(&Thread
->Timer
.Header
.WaitListHead
,
784 &Thread
->WaitBlock
[3].WaitListEntry
);
787 /* Initialize the Suspend APC */
788 KeInitializeApc(&Thread
->SuspendApc
,
790 OriginalApcEnvironment
,
791 KiSuspendThreadKernelRoutine
,
793 KiSuspendThreadNormalRoutine
,
797 /* Initialize the Suspend Semaphore */
798 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 128);
800 /* Insert the Thread into the Process's Thread List */
801 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
803 /* Set up the Suspend Counts */
804 Thread
->FreezeCount
= 0;
805 Thread
->SuspendCount
= 0;
806 ((PETHREAD
)Thread
)->ReaperLink
= NULL
; /* Union. Will also clear termination port */
808 /* Do x86 specific part */
816 KeQueryPriorityThread (IN PKTHREAD Thread
)
818 return Thread
->Priority
;
826 KeQueryRuntimeThread(IN PKTHREAD Thread
,
829 /* Return the User Time */
830 *UserTime
= Thread
->UserTime
;
832 /* Return the Kernel Time */
833 return Thread
->KernelTime
;
837 KeFreeStackPage(PVOID Context
,
838 MEMORY_AREA
* MemoryArea
,
844 ASSERT(SwapEntry
== 0);
845 if (Page
) MmReleasePageMemoryConsumer(MC_NPPOOL
, Page
);
849 KeReleaseThread(PKTHREAD Thread
)
851 * FUNCTION: Releases the resource allocated for a thread by
853 * NOTE: The thread had better not be running when this is called
856 extern unsigned int init_stack
;
858 /* FIXME - lock the process */
859 RemoveEntryList(&Thread
->ThreadListEntry
);
861 if (Thread
->StackLimit
!= (ULONG_PTR
)init_stack
)
863 MmLockAddressSpace(MmGetKernelAddressSpace());
864 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
865 (PVOID
)Thread
->StackLimit
,
868 MmUnlockAddressSpace(MmGetKernelAddressSpace());
870 Thread
->StackLimit
= 0;
871 Thread
->InitialStack
= NULL
;
872 Thread
->StackBase
= NULL
;
873 Thread
->KernelStack
= NULL
;
874 return(STATUS_SUCCESS
);
882 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
884 PKTHREAD Thread
= KeGetCurrentThread();
885 BOOLEAN PreviousState
;
888 /* Lock the Dispatcher Database */
889 OldIrql
= KeAcquireDispatcherDatabaseLock();
892 PreviousState
= Thread
->EnableStackSwap
;
895 Thread
->EnableStackSwap
= Enable
;
897 /* No, Release Lock */
898 KeReleaseDispatcherDatabaseLock(OldIrql
);
900 /* Return Old State */
901 return PreviousState
;
909 KeRevertToUserAffinityThread(VOID
)
911 PKTHREAD CurrentThread
= KeGetCurrentThread();
914 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
916 /* Lock the Dispatcher Database */
917 OldIrql
= KeAcquireDispatcherDatabaseLock();
919 /* Return to User Affinity */
920 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
922 /* Disable System Affinity */
923 CurrentThread
->SystemAffinityActive
= FALSE
;
925 /* Check if we need to Dispatch a New thread */
926 if (CurrentThread
->Affinity
& (1 << KeGetCurrentProcessorNumber())) {
928 /* No, just release */
929 KeReleaseDispatcherDatabaseLock(OldIrql
);
933 /* We need to dispatch a new thread */
934 CurrentThread
->WaitIrql
= OldIrql
;
935 KiDispatchThreadNoLock(THREAD_STATE_READY
);
936 KeLowerIrql(OldIrql
);
945 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
948 CCHAR PreviousIdealProcessor
;
951 /* Lock the Dispatcher Database */
952 OldIrql
= KeAcquireDispatcherDatabaseLock();
954 /* Save Old Ideal Processor */
955 PreviousIdealProcessor
= Thread
->IdealProcessor
;
957 /* Set New Ideal Processor */
958 Thread
->IdealProcessor
= Processor
;
961 KeReleaseDispatcherDatabaseLock(OldIrql
);
963 /* Return Old Ideal Processor */
964 return PreviousIdealProcessor
;
972 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
974 PKTHREAD CurrentThread
= KeGetCurrentThread();
977 ASSERT(Affinity
& ((1 << KeNumberProcessors
) - 1));
979 /* Lock the Dispatcher Database */
980 OldIrql
= KeAcquireDispatcherDatabaseLock();
982 /* Set the System Affinity Specified */
983 CurrentThread
->Affinity
= Affinity
;
985 /* Enable System Affinity */
986 CurrentThread
->SystemAffinityActive
= TRUE
;
988 /* Check if we need to Dispatch a New thread */
989 if (Affinity
& (1 << KeGetCurrentProcessorNumber())) {
991 /* No, just release */
992 KeReleaseDispatcherDatabaseLock(OldIrql
);
996 /* We need to dispatch a new thread */
997 CurrentThread
->WaitIrql
= OldIrql
;
998 KiDispatchThreadNoLock(THREAD_STATE_READY
);
999 KeLowerIrql(OldIrql
);
1007 KeSetBasePriorityThread (PKTHREAD Thread
,
1010 * Sets thread's base priority relative to the process' base priority
1011 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
1019 else if (Increment
> 2)
1023 Priority
= ((PETHREAD
)Thread
)->ThreadsProcess
->Pcb
.BasePriority
+ Increment
;
1024 if (Priority
< LOW_PRIORITY
)
1026 Priority
= LOW_PRIORITY
;
1028 else if (Priority
>= MAXIMUM_PRIORITY
)
1030 Thread
->BasePriority
= HIGH_PRIORITY
;
1032 KeSetPriorityThread(Thread
, Priority
);
1041 KeSetPriorityThread(PKTHREAD Thread
,
1044 KPRIORITY OldPriority
;
1046 PKTHREAD CurrentThread
;
1051 if (Priority
< LOW_PRIORITY
|| Priority
>= MAXIMUM_PRIORITY
) {
1056 OldIrql
= KeAcquireDispatcherDatabaseLock();
1058 OldPriority
= Thread
->Priority
;
1060 if (OldPriority
!= Priority
) {
1062 CurrentThread
= KeGetCurrentThread();
1064 if (Thread
->State
== THREAD_STATE_READY
) {
1066 KiRemoveFromThreadList(Thread
);
1067 Thread
->BasePriority
= Thread
->Priority
= (CHAR
)Priority
;
1068 KiInsertIntoThreadList(Priority
, Thread
);
1070 if (CurrentThread
->Priority
< Priority
) {
1072 KiDispatchThreadNoLock(THREAD_STATE_READY
);
1073 KeLowerIrql(OldIrql
);
1074 return (OldPriority
);
1077 } else if (Thread
->State
== THREAD_STATE_RUNNING
) {
1079 Thread
->BasePriority
= Thread
->Priority
= (CHAR
)Priority
;
1081 if (Priority
< OldPriority
) {
1083 /* Check for threads with a higher priority */
1084 Mask
= ~((1 << (Priority
+ 1)) - 1);
1085 if (PriorityListMask
& Mask
) {
1087 if (Thread
== CurrentThread
) {
1089 KiDispatchThreadNoLock(THREAD_STATE_READY
);
1090 KeLowerIrql(OldIrql
);
1091 return (OldPriority
);
1095 for (i
= 0; i
< KeNumberProcessors
; i
++) {
1097 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1099 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
1101 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1102 KiRequestReschedule(i
);
1103 KeLowerIrql(OldIrql
);
1104 return (OldPriority
);
1112 Thread
->BasePriority
= Thread
->Priority
= (CHAR
)Priority
;
1116 KeReleaseDispatcherDatabaseLock(OldIrql
);
1117 return(OldPriority
);
1123 * Sets thread's affinity
1127 KeSetAffinityThread(PKTHREAD Thread
,
1133 KAFFINITY ProcessorMask
;
1135 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread
, Affinity
);
1137 ASSERT(Affinity
& ((1 << KeNumberProcessors
) - 1));
1139 OldIrql
= KeAcquireDispatcherDatabaseLock();
1141 Thread
->UserAffinity
= Affinity
;
1143 if (Thread
->SystemAffinityActive
== FALSE
) {
1145 Thread
->Affinity
= Affinity
;
1147 if (Thread
->State
== THREAD_STATE_RUNNING
) {
1149 ProcessorMask
= 1 << KeGetCurrentKPCR()->ProcessorNumber
;
1150 if (Thread
== KeGetCurrentThread()) {
1152 if (!(Affinity
& ProcessorMask
)) {
1154 KiDispatchThreadNoLock(THREAD_STATE_READY
);
1155 KeLowerIrql(OldIrql
);
1156 return STATUS_SUCCESS
;
1161 for (i
= 0; i
< KeNumberProcessors
; i
++) {
1163 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1164 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
1166 if (!(Affinity
& ProcessorMask
)) {
1168 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1169 KiRequestReschedule(i
);
1170 KeLowerIrql(OldIrql
);
1171 return STATUS_SUCCESS
;
1178 ASSERT (i
< KeNumberProcessors
);
1183 KeReleaseDispatcherDatabaseLock(OldIrql
);
1184 return STATUS_SUCCESS
;
1190 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1193 KeTerminateThread(IN KPRIORITY Increment
)
1196 PKTHREAD Thread
= KeGetCurrentThread();
1198 /* Lock the Dispatcher Database and the APC Queue */
1199 DPRINT("Terminating\n");
1200 OldIrql
= KeAcquireDispatcherDatabaseLock();
1202 /* Insert into the Reaper List */
1203 DPRINT("List: %p\n", PspReaperList
);
1204 ((PETHREAD
)Thread
)->ReaperLink
= PspReaperList
;
1205 PspReaperList
= (PETHREAD
)Thread
;
1206 DPRINT("List: %p\n", PspReaperList
);
1208 /* Check if it's active */
1209 if (PspReaping
== FALSE
) {
1211 /* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */
1213 DPRINT("Terminating\n");
1214 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1215 &PspReaperWorkItem
.List
,
1219 /* Handle Kernel Queues */
1220 if (Thread
->Queue
) {
1222 DPRINT("Waking Queue\n");
1223 RemoveEntryList(&Thread
->QueueListEntry
);
1224 KiWakeQueue(Thread
->Queue
);
1227 /* Signal the thread */
1228 Thread
->DispatcherHeader
.SignalState
= TRUE
;
1229 if (IsListEmpty(&Thread
->DispatcherHeader
.WaitListHead
) != TRUE
) {
1232 KiWaitTest((PVOID
)Thread
, Increment
);
1235 /* Find a new Thread */
1236 KiDispatchThreadNoLock(THREAD_STATE_TERMINATED_1
);
1240 * FUNCTION: Tests whether there are any pending APCs for the current thread
1241 * and if so the APCs will be delivered on exit from kernel mode
1245 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
1248 PKTHREAD Thread
= KeGetCurrentThread();
1251 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1253 /* Lock the Dispatcher Database and the APC Queue */
1254 OldIrql
= KeAcquireDispatcherDatabaseLock();
1255 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
1257 /* Save the old State */
1258 OldState
= Thread
->Alerted
[AlertMode
];
1260 /* If the Thread is Alerted, Clear it */
1263 Thread
->Alerted
[AlertMode
] = FALSE
;
1265 } else if ((AlertMode
== UserMode
) && (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))) {
1267 /* If the mode is User and the Queue isn't empty, set Pending */
1268 Thread
->ApcState
.UserApcPending
= TRUE
;
1271 /* Release Locks and return the Old State */
1272 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
1273 KeReleaseDispatcherDatabaseLock(OldIrql
);
1278 KiServiceCheck (VOID
)
1280 PKTHREAD Thread
= KeGetCurrentThread();
1282 /* Check if we need to inialize Win32 for this Thread */
1283 if (Thread
->ServiceTable
!= KeServiceDescriptorTableShadow
) {
1285 /* We do. Initialize it and save the new table */
1286 PsInitWin32Thread((PETHREAD
)Thread
);
1287 Thread
->ServiceTable
= KeServiceDescriptorTableShadow
;
1297 NtAlertResumeThread(IN HANDLE ThreadHandle
,
1298 OUT PULONG SuspendCount
)
1300 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1303 ULONG PreviousState
;
1305 /* Check if parameters are valid */
1306 if(PreviousMode
!= KernelMode
) {
1310 ProbeForWrite(SuspendCount
,
1316 Status
= _SEH_GetExceptionCode();
1321 /* Reference the Object */
1322 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1323 THREAD_SUSPEND_RESUME
,
1329 /* Check for Success */
1330 if (NT_SUCCESS(Status
)) {
1332 /* Call the Kernel Function */
1333 PreviousState
= KeAlertResumeThread(&Thread
->Tcb
);
1335 /* Dereference Object */
1336 ObDereferenceObject(Thread
);
1342 *SuspendCount
= PreviousState
;
1346 Status
= _SEH_GetExceptionCode();
1363 NtAlertThread (IN HANDLE ThreadHandle
)
1365 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1369 /* Reference the Object */
1370 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1371 THREAD_SUSPEND_RESUME
,
1377 /* Check for Success */
1378 if (NT_SUCCESS(Status
)) {
1381 * Do an alert depending on the processor mode. If some kmode code wants to
1382 * enforce a umode alert it should call KeAlertThread() directly. If kmode
1383 * code wants to do a kmode alert it's sufficient to call it with Zw or just
1384 * use KeAlertThread() directly
1386 KeAlertThread(&Thread
->Tcb
, PreviousMode
);
1388 /* Dereference Object */
1389 ObDereferenceObject(Thread
);
1398 NtDelayExecution(IN BOOLEAN Alertable
,
1399 IN PLARGE_INTEGER DelayInterval
)
1401 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1402 LARGE_INTEGER SafeInterval
;
1405 /* Check if parameters are valid */
1406 if(PreviousMode
!= KernelMode
) {
1410 ProbeForRead(DelayInterval
,
1411 sizeof(LARGE_INTEGER
),
1414 /* make a copy on the kernel stack and let DelayInterval point to it so
1415 we don't need to wrap KeDelayExecutionThread in SEH! */
1416 SafeInterval
= *DelayInterval
;
1420 Status
= _SEH_GetExceptionCode();
1424 /* Call the Kernel Function */
1425 Status
= KeDelayExecutionThread(PreviousMode
,