2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/kthread.c
5 * PURPOSE: Microkernel thread support
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
11 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
17 extern EX_WORK_QUEUE ExWorkerQueue
[MaximumWorkQueue
];
20 * PURPOSE: List of threads associated with each priority level
22 LIST_ENTRY PriorityListHead
[MAXIMUM_PRIORITY
];
23 static ULONG PriorityListMask
= 0;
24 ULONG IdleProcessorMask
= 0;
25 extern LIST_ENTRY PspReaperListHead
;
27 ULONG KiMask32Array
[MAXIMUM_PRIORITY
] =
29 0x1, 0x2, 0x4, 0x8, 0x10, 0x20,
30 0x40, 0x80, 0x100, 0x200, 0x4000, 0x800,
31 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000,
32 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000,
33 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000, 0x20000000,
34 0x40000000, 0x80000000
37 /* FUNCTIONS *****************************************************************/
41 KeFindNextRightSetAffinity(IN UCHAR Number
,
47 /* Calculate the mask */
48 Bit
= (AFFINITY_MASK(Number
) - 1) & Set
;
50 /* If it's 0, use the one we got */
53 /* Now find the right set and return it */
54 BitScanReverse(&Result
, Bit
);
60 KiRequestReschedule(CCHAR Processor
)
64 Pcr
= (PKPCR
)(KPCR_BASE
+ Processor
* PAGE_SIZE
);
65 Pcr
->Prcb
->QuantumEnd
= TRUE
;
66 KiIpiSendRequest(1 << Processor
, IPI_DPC
);
71 KiInsertIntoThreadList(KPRIORITY Priority
,
74 ASSERT(Ready
== Thread
->State
);
75 ASSERT(Thread
->Priority
== Priority
);
77 if (Priority
>= MAXIMUM_PRIORITY
|| Priority
< LOW_PRIORITY
) {
79 DPRINT1("Invalid thread priority (%d)\n", Priority
);
83 InsertTailList(&PriorityListHead
[Priority
], &Thread
->WaitListEntry
);
84 PriorityListMask
|= (1 << Priority
);
89 KiRemoveFromThreadList(PKTHREAD Thread
)
91 ASSERT(Ready
== Thread
->State
);
92 RemoveEntryList(&Thread
->WaitListEntry
);
93 if (IsListEmpty(&PriorityListHead
[(ULONG
)Thread
->Priority
])) {
95 PriorityListMask
&= ~(1 << Thread
->Priority
);
101 KiScanThreadList(KPRIORITY Priority
,
107 Mask
= (1 << Priority
);
109 if (PriorityListMask
& Mask
) {
111 LIST_FOR_EACH(current
, &PriorityListHead
[Priority
], KTHREAD
, WaitListEntry
) {
113 if (current
->State
!= Ready
) {
115 DPRINT1("%p/%d\n", current
, current
->State
);
118 ASSERT(current
->State
== Ready
);
120 if (current
->Affinity
& Affinity
) {
122 KiRemoveFromThreadList(current
);
133 KiDispatchThreadNoLock(ULONG NewThreadStatus
)
135 KPRIORITY CurrentPriority
;
138 PKTHREAD CurrentThread
= KeGetCurrentThread();
141 DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
142 CurrentThread
, NewThreadStatus
, CurrentThread
->State
);
144 CurrentThread
->State
= (UCHAR
)NewThreadStatus
;
146 if (NewThreadStatus
== Ready
) {
148 KiInsertIntoThreadList(CurrentThread
->Priority
,
152 Affinity
= 1 << KeGetCurrentProcessorNumber();
154 for (CurrentPriority
= HIGH_PRIORITY
; CurrentPriority
>= LOW_PRIORITY
; CurrentPriority
--) {
156 Candidate
= KiScanThreadList(CurrentPriority
, Affinity
);
158 if (Candidate
== CurrentThread
) {
160 Candidate
->State
= Running
;
161 KeReleaseDispatcherDatabaseLockFromDpcLevel();
165 if (Candidate
!= NULL
) {
170 DPRINT("Scheduling %x(%d)\n",Candidate
, CurrentPriority
);
172 Candidate
->State
= Running
;
174 OldThread
= CurrentThread
;
175 CurrentThread
= Candidate
;
176 IdleThread
= KeGetCurrentPrcb()->IdleThread
;
178 if (OldThread
== IdleThread
) {
180 IdleProcessorMask
&= ~Affinity
;
182 } else if (CurrentThread
== IdleThread
) {
184 IdleProcessorMask
|= Affinity
;
187 MmUpdatePageDir((PEPROCESS
)PsGetCurrentProcess(),((PETHREAD
)CurrentThread
)->ThreadsProcess
, sizeof(EPROCESS
));
189 /* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */
190 DPRINT("You are : %x, swapping to: %x.\n", OldThread
, CurrentThread
);
191 ApcState
= KiSwapContext(OldThread
, CurrentThread
);
192 DPRINT("You are : %x, swapped from: %x\n", OldThread
, CurrentThread
);
197 DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber());
206 PKTHREAD CurrentThread
= KeGetCurrentThread();
209 /* Find a new thread to run */
210 DPRINT("Dispatching Thread as blocked\n");
211 ApcState
= KiDispatchThreadNoLock(Waiting
);
213 /* Check if we need to deliver APCs */
216 /* Lower to APC_LEVEL */
217 KeLowerIrql(APC_LEVEL
);
220 KiDeliverApc(KernelMode
, NULL
, NULL
);
221 ASSERT(CurrentThread
->WaitIrql
== 0);
224 /* Lower IRQL back to what it was */
225 KfLowerIrql(CurrentThread
->WaitIrql
);
227 /* Return the wait status */
228 return CurrentThread
->WaitStatus
;
233 KiDispatchThread(ULONG NewThreadStatus
)
237 if (KeGetCurrentPrcb()->IdleThread
== NULL
) {
241 OldIrql
= KeAcquireDispatcherDatabaseLock();
242 KiDispatchThreadNoLock(NewThreadStatus
);
243 KeLowerIrql(OldIrql
);
248 KiReadyThread(IN PKTHREAD Thread
)
250 /* Makes a thread ready */
251 Thread
->State
= Ready
;
252 KiInsertIntoThreadList(Thread
->Priority
, Thread
);
257 KiAdjustQuantumThread(IN PKTHREAD Thread
)
261 /* Don't adjust for RT threads */
262 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
263 Thread
->BasePriority
< LOW_REALTIME_PRIORITY
- 2)
265 /* Decrease Quantum by one and see if we've ran out */
266 if (--Thread
->Quantum
<= 0)
269 Thread
->Quantum
= Thread
->QuantumReset
;
271 /* Calculate new Priority */
272 Priority
= Thread
->Priority
- (Thread
->PriorityDecrement
+ 1);
274 /* Normalize it if we've gone too low */
275 if (Priority
< Thread
->BasePriority
) Priority
= Thread
->BasePriority
;
277 /* Reset the priority decrement, we've done it */
278 Thread
->PriorityDecrement
= 0;
280 /* Set the new priority, if needed */
281 if (Priority
!= Thread
->Priority
)
284 * FIXME: This should be a call to KiSetPriorityThread but
285 * due to the current ""scheduler"" in ROS, it can't be done
286 * cleanly since it actualyl dispatches threads instead.
288 Thread
->Priority
= Priority
;
292 /* FIXME: Priority hasn't changed, find a new thread */
297 /* Nothing to do... */
304 KiSuspendThreadKernelRoutine(PKAPC Apc
,
305 PKNORMAL_ROUTINE
* NormalRoutine
,
306 PVOID
* NormalContext
,
307 PVOID
* SystemArgument1
,
308 PVOID
* SystemArguemnt2
)
314 KiSuspendThreadNormalRoutine(PVOID NormalContext
,
315 PVOID SystemArgument1
,
316 PVOID SystemArgument2
)
318 PKTHREAD CurrentThread
= KeGetCurrentThread();
320 /* Non-alertable kernel-mode suspended wait */
321 DPRINT("Waiting...\n");
322 KeWaitForSingleObject(&CurrentThread
->SuspendSemaphore
,
327 DPRINT("Done Waiting\n");
330 #ifdef KeGetCurrentThread
331 #undef KeGetCurrentThread
338 KeGetCurrentThread(VOID
)
343 Ke386SaveFlags(Flags
);
344 Ke386DisableInterrupts();
345 Thread
= KeGetCurrentPrcb()->CurrentThread
;
346 Ke386RestoreFlags(Flags
);
349 return(KeGetCurrentPrcb()->CurrentThread
);
355 KeSetPreviousMode(ULONG Mode
)
357 PsGetCurrentThread()->Tcb
.PreviousMode
= (UCHAR
)Mode
;
365 KeGetPreviousMode(VOID
)
367 return (ULONG
)PsGetCurrentThread()->Tcb
.PreviousMode
;
372 KeDisableThreadApcQueueing(IN PKTHREAD Thread
)
375 BOOLEAN PreviousState
;
377 /* Lock the Dispatcher Database */
378 OldIrql
= KeAcquireDispatcherDatabaseLock();
381 PreviousState
= Thread
->ApcQueueable
;
384 Thread
->ApcQueueable
= FALSE
;
386 /* Release the Lock */
387 KeReleaseDispatcherDatabaseLock(OldIrql
);
389 /* Return old state */
390 return PreviousState
;
395 KeRundownThread(VOID
)
398 PKTHREAD Thread
= KeGetCurrentThread();
399 PLIST_ENTRY NextEntry
, ListHead
;
401 DPRINT("KeRundownThread: %x\n", Thread
);
403 /* Optimized path if nothing is on the list at the moment */
404 if (IsListEmpty(&Thread
->MutantListHead
)) return;
406 /* Lock the Dispatcher Database */
407 OldIrql
= KeAcquireDispatcherDatabaseLock();
409 /* Get the List Pointers */
410 ListHead
= &Thread
->MutantListHead
;
411 NextEntry
= ListHead
->Flink
;
412 while (NextEntry
!= ListHead
)
415 Mutant
= CONTAINING_RECORD(NextEntry
, KMUTANT
, MutantListEntry
);
416 DPRINT1("Mutant: %p. Type, Size %x %x\n",
419 Mutant
->Header
.Size
);
421 /* Make sure it's not terminating with APCs off */
422 if (Mutant
->ApcDisable
)
424 /* Bugcheck the system */
425 KEBUGCHECKEX(0,//THREAD_TERMINATE_HELD_MUTEX,
432 /* Now we can remove it */
433 RemoveEntryList(&Mutant
->MutantListEntry
);
435 /* Unconditionally abandon it */
436 DPRINT("Abandonning the Mutant\n");
437 Mutant
->Header
.SignalState
= 1;
438 Mutant
->Abandoned
= TRUE
;
439 Mutant
->OwnerThread
= NULL
;
441 /* Check if the Wait List isn't empty */
442 DPRINT("Checking whether to wake the Mutant\n");
443 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
))
445 /* Wake the Mutant */
446 DPRINT("Waking the Mutant\n");
447 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
451 NextEntry
= NextEntry
->Flink
;
454 /* Release the Lock */
455 KeReleaseDispatcherDatabaseLock(OldIrql
);
460 KeResumeThread(IN PKTHREAD Thread
)
465 DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread
,
466 Thread
->SuspendCount
, Thread
->FreezeCount
);
468 /* Lock the Dispatcher */
469 OldIrql
= KeAcquireDispatcherDatabaseLock();
471 /* Save the Old Count */
472 PreviousCount
= Thread
->SuspendCount
;
474 /* Check if it existed */
477 Thread
->SuspendCount
--;
479 /* Decrease the current Suspend Count and Check Freeze Count */
480 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
)) {
482 /* Signal the Suspend Semaphore */
483 Thread
->SuspendSemaphore
.Header
.SignalState
++;
484 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
488 /* Release Lock and return the Old State */
489 KeReleaseDispatcherDatabaseLock(OldIrql
);
490 return PreviousCount
;
495 KiInsertQueueApc(PKAPC Apc
,
496 KPRIORITY PriorityBoost
);
499 * Used by the debugging code to freeze all the process's threads
500 * while the debugger is examining their state.
504 KeFreezeAllThreads(PKPROCESS Process
)
508 PKTHREAD CurrentThread
= KeGetCurrentThread();
511 OldIrql
= KeAcquireDispatcherDatabaseLock();
513 /* If someone is already trying to free us, try again */
514 while (CurrentThread
->FreezeCount
)
516 /* Release and re-acquire the lock so the APC will go through */
517 KeReleaseDispatcherDatabaseLock(OldIrql
);
518 OldIrql
= KeAcquireDispatcherDatabaseLock();
521 /* Enter a critical region */
522 KeEnterCriticalRegion();
524 /* Loop the Process's Threads */
525 LIST_FOR_EACH(Current
, &Process
->ThreadListHead
, KTHREAD
, ThreadListEntry
)
527 /* Make sure it's not ours */
528 if (Current
!= CurrentThread
)
530 /* Should be bother inserting the APC? */
531 if (Current
->ApcQueueable
)
533 /* Make sure it wasn't already frozen, and that it's not suspended */
534 if (!(++Current
->FreezeCount
) && !(Current
->SuspendCount
))
536 /* Did we already insert it? */
537 if (!Current
->SuspendApc
.Inserted
)
540 Current
->SuspendApc
.Inserted
= TRUE
;
541 KiInsertQueueApc(&Current
->SuspendApc
, IO_NO_INCREMENT
);
545 /* Unsignal the Semaphore, the APC already got inserted */
546 Current
->SuspendSemaphore
.Header
.SignalState
--;
553 /* Release the lock */
554 KeReleaseDispatcherDatabaseLock(OldIrql
);
559 KeSuspendThread(PKTHREAD Thread
)
564 DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread
, Thread
->SuspendCount
, Thread
->FreezeCount
);
566 /* Lock the Dispatcher */
567 OldIrql
= KeAcquireDispatcherDatabaseLock();
569 /* Save the Old Count */
570 PreviousCount
= Thread
->SuspendCount
;
572 /* Handle the maximum */
573 if (PreviousCount
== MAXIMUM_SUSPEND_COUNT
)
575 /* Raise an exception */
576 KeReleaseDispatcherDatabaseLock(OldIrql
);
577 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED
);
580 /* Should we bother to queue at all? */
581 if (Thread
->ApcQueueable
)
583 /* Increment the suspend count */
584 Thread
->SuspendCount
++;
586 /* Check if we should suspend it */
587 if (!PreviousCount
&& !Thread
->FreezeCount
)
589 /* Is the APC already inserted? */
590 if (!Thread
->SuspendApc
.Inserted
)
592 /* Not inserted, insert it */
593 Thread
->SuspendApc
.Inserted
= TRUE
;
594 KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
);
598 /* Unsignal the Semaphore, the APC already got inserted */
599 Thread
->SuspendSemaphore
.Header
.SignalState
--;
604 /* Release Lock and return the Old State */
605 KeReleaseDispatcherDatabaseLock(OldIrql
);
606 return PreviousCount
;
611 KeForceResumeThread(IN PKTHREAD Thread
)
616 /* Lock the Dispatcher Database and the APC Queue */
617 OldIrql
= KeAcquireDispatcherDatabaseLock();
619 /* Save the old Suspend Count */
620 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
622 /* If the thread is suspended, wake it up!!! */
625 /* Unwait it completely */
626 Thread
->SuspendCount
= 0;
627 Thread
->FreezeCount
= 0;
629 /* Signal and satisfy */
630 Thread
->SuspendSemaphore
.Header
.SignalState
++;
631 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
634 /* Release Lock and return the Old State */
635 KeReleaseDispatcherDatabaseLock(OldIrql
);
636 return PreviousCount
;
641 KeAlertResumeThread(IN PKTHREAD Thread
)
646 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
648 /* Lock the Dispatcher Database and the APC Queue */
649 OldIrql
= KeAcquireDispatcherDatabaseLock();
650 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
652 /* Return if Thread is already alerted. */
653 if (Thread
->Alerted
[KernelMode
] == FALSE
) {
655 /* If it's Blocked, unblock if it we should */
656 if (Thread
->State
== Waiting
&& Thread
->Alertable
) {
658 DPRINT("Aborting Wait\n");
659 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
663 /* If not, simply Alert it */
664 Thread
->Alerted
[KernelMode
] = TRUE
;
668 /* Save the old Suspend Count */
669 PreviousCount
= Thread
->SuspendCount
;
671 /* If the thread is suspended, decrease one of the suspend counts */
674 /* Decrease count. If we are now zero, unwait it completely */
675 if (--Thread
->SuspendCount
) {
677 /* Signal and satisfy */
678 Thread
->SuspendSemaphore
.Header
.SignalState
++;
679 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
683 /* Release Locks and return the Old State */
684 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
685 KeReleaseDispatcherDatabaseLock(OldIrql
);
686 return PreviousCount
;
691 KeAlertThread(IN PKTHREAD Thread
,
692 IN KPROCESSOR_MODE AlertMode
)
695 BOOLEAN PreviousState
;
697 /* Acquire the Dispatcher Database Lock */
698 OldIrql
= KeAcquireDispatcherDatabaseLock();
700 /* Save the Previous State */
701 PreviousState
= Thread
->Alerted
[AlertMode
];
703 /* Return if Thread is already alerted. */
704 if (PreviousState
== FALSE
) {
706 /* If it's Blocked, unblock if it we should */
707 if (Thread
->State
== Waiting
&&
708 (AlertMode
== KernelMode
|| Thread
->WaitMode
== AlertMode
) &&
711 DPRINT("Aborting Wait\n");
712 KiAbortWaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
716 /* If not, simply Alert it */
717 Thread
->Alerted
[AlertMode
] = TRUE
;
721 /* Release the Dispatcher Lock */
722 KeReleaseDispatcherDatabaseLock(OldIrql
);
724 /* Return the old state */
725 return PreviousState
;
733 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
739 IN PVOID ThreadState
)
746 KeUninitThread(IN PKTHREAD Thread
)
748 /* Delete the stack */
749 MmDeleteKernelStack(Thread
->StackBase
, FALSE
);
750 Thread
->InitialStack
= NULL
;
755 KeInitThread(IN OUT PKTHREAD Thread
,
756 IN PVOID KernelStack
,
757 IN PKSYSTEM_ROUTINE SystemRoutine
,
758 IN PKSTART_ROUTINE StartRoutine
,
759 IN PVOID StartContext
,
762 IN PKPROCESS Process
)
764 BOOLEAN AllocatedStack
= FALSE
;
766 PKWAIT_BLOCK TimerWaitBlock
;
770 /* Initalize the Dispatcher Header */
771 KeInitializeDispatcherHeader(&Thread
->DispatcherHeader
,
773 sizeof(KTHREAD
) / sizeof(LONG
),
776 /* Initialize the Mutant List */
777 InitializeListHead(&Thread
->MutantListHead
);
779 /* Initialize the wait blocks */
780 for (i
= 0; i
< (THREAD_WAIT_OBJECTS
+ 1); i
++)
782 /* Put our pointer */
783 Thread
->WaitBlock
[i
].Thread
= Thread
;
786 /* Set swap settings */
787 Thread
->EnableStackSwap
= FALSE
;//TRUE;
788 Thread
->IdealProcessor
= 1;
789 Thread
->SwapBusy
= FALSE
;
790 Thread
->AdjustReason
= 0;
792 /* Initialize the lock */
793 KeInitializeSpinLock(&Thread
->ThreadLock
);
795 /* Setup the Service Descriptor Table for Native Calls */
796 Thread
->ServiceTable
= KeServiceDescriptorTable
;
798 /* Setup APC Fields */
799 InitializeListHead(&Thread
->ApcState
.ApcListHead
[0]);
800 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
801 Thread
->ApcState
.Process
= Process
;
802 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
803 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
804 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
805 Thread
->ApcQueueable
= TRUE
;
806 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
808 /* Initialize the Suspend APC */
809 KeInitializeApc(&Thread
->SuspendApc
,
811 OriginalApcEnvironment
,
812 KiSuspendThreadKernelRoutine
,
814 KiSuspendThreadNormalRoutine
,
818 /* Initialize the Suspend Semaphore */
819 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 2);
821 /* Setup the timer */
822 Timer
= &Thread
->Timer
;
823 KeInitializeTimer(Timer
);
824 TimerWaitBlock
= &Thread
->WaitBlock
[TIMER_WAIT_BLOCK
];
825 TimerWaitBlock
->Object
= Timer
;
826 TimerWaitBlock
->WaitKey
= STATUS_TIMEOUT
;
827 TimerWaitBlock
->WaitType
= WaitAny
;
828 TimerWaitBlock
->NextWaitBlock
= NULL
;
830 /* Link the two wait lists together */
831 TimerWaitBlock
->WaitListEntry
.Flink
= &Timer
->Header
.WaitListHead
;
832 TimerWaitBlock
->WaitListEntry
.Blink
= &Timer
->Header
.WaitListHead
;
837 /* Check if we have a kernel stack */
840 /* We don't, allocate one */
841 KernelStack
= (PVOID
)((ULONG_PTR
)MmCreateKernelStack(FALSE
) +
843 if (!KernelStack
) return STATUS_INSUFFICIENT_RESOURCES
;
845 /* Remember for later */
846 AllocatedStack
= TRUE
;
849 /* Set the Thread Stacks */
850 Thread
->InitialStack
= (PCHAR
)KernelStack
;
851 Thread
->StackBase
= (PCHAR
)KernelStack
;
852 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
- KERNEL_STACK_SIZE
;
853 Thread
->KernelStackResident
= TRUE
;
856 MmUpdatePageDir((PEPROCESS
)Process
,
857 (PVOID
)Thread
->StackLimit
,
859 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
861 /* Enter SEH to avoid crashes due to user mode */
862 Status
= STATUS_SUCCESS
;
865 /* Initalize the Thread Context */
866 Ke386InitThreadWithContext(Thread
,
874 /* Set failure status */
875 Status
= STATUS_UNSUCCESSFUL
;
877 /* Check if a stack was allocated */
880 /* Delete the stack */
881 MmDeleteKernelStack(Thread
->StackBase
, FALSE
);
882 Thread
->InitialStack
= NULL
;
887 /* Set the Thread to initalized */
888 Thread
->State
= Initialized
;
894 KeStartThread(IN OUT PKTHREAD Thread
)
897 PKPROCESS Process
= Thread
->ApcState
.Process
;
901 UCHAR IdealProcessor
;
903 /* Setup static fields from parent */
904 Thread
->Iopl
= Process
->Iopl
;
905 Thread
->Quantum
= Process
->QuantumReset
;
906 Thread
->QuantumReset
= Process
->QuantumReset
;
907 Thread
->SystemAffinityActive
= FALSE
;
909 /* Lock the process */
910 KeAcquireSpinLock(&Process
->ProcessLock
, &OldIrql
);
912 /* Setup volatile data */
913 Thread
->Priority
= Process
->BasePriority
;
914 Thread
->BasePriority
= Process
->BasePriority
;
915 Thread
->Affinity
= Process
->Affinity
;
916 Thread
->UserAffinity
= Process
->Affinity
;
918 /* Get the KNODE and its PRCB */
919 Node
= KeNodeBlock
[Process
->IdealNode
];
920 NodePrcb
= (PKPRCB
)(KPCR_BASE
+ (Process
->ThreadSeed
* PAGE_SIZE
));
922 /* Calculate affinity mask */
923 Set
= ~NodePrcb
->MultiThreadProcessorSet
;
924 Mask
= (ULONG
)(Node
->ProcessorMask
& Process
->Affinity
);
928 /* Get the new thread seed */
929 IdealProcessor
= KeFindNextRightSetAffinity(Process
->ThreadSeed
, Mask
);
930 Process
->ThreadSeed
= IdealProcessor
;
933 ASSERT((Thread
->UserAffinity
& AFFINITY_MASK(IdealProcessor
)));
935 /* Set the Ideal Processor */
936 Thread
->IdealProcessor
= IdealProcessor
;
937 Thread
->UserIdealProcessor
= IdealProcessor
;
939 /* Lock the Dispatcher Database */
940 KeAcquireDispatcherDatabaseLockAtDpcLevel();
942 /* Insert the thread into the process list */
943 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
945 /* Increase the stack count */
946 ASSERT(Process
->StackCount
!= MAXULONG_PTR
);
947 Process
->StackCount
++;
949 /* Release locks and return */
950 KeReleaseDispatcherDatabaseLockFromDpcLevel();
951 KeReleaseSpinLock(&Process
->ProcessLock
, OldIrql
);
956 KeInitializeThread(IN PKPROCESS Process
,
957 IN OUT PKTHREAD Thread
,
958 IN PKSYSTEM_ROUTINE SystemRoutine
,
959 IN PKSTART_ROUTINE StartRoutine
,
960 IN PVOID StartContext
,
963 IN PVOID KernelStack
)
965 /* Initailize and start the thread on success */
966 if (NT_SUCCESS(KeInitThread(Thread
,
976 KeStartThread(Thread
);
985 KeQueryPriorityThread (IN PKTHREAD Thread
)
987 return Thread
->Priority
;
995 KeQueryRuntimeThread(IN PKTHREAD Thread
,
998 /* Return the User Time */
999 *UserTime
= Thread
->UserTime
;
1001 /* Return the Kernel Time */
1002 return Thread
->KernelTime
;
1010 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
1012 PKTHREAD Thread
= KeGetCurrentThread();
1013 BOOLEAN PreviousState
;
1016 /* Lock the Dispatcher Database */
1017 OldIrql
= KeAcquireDispatcherDatabaseLock();
1019 /* Save Old State */
1020 PreviousState
= Thread
->EnableStackSwap
;
1023 Thread
->EnableStackSwap
= Enable
;
1025 /* No, Release Lock */
1026 KeReleaseDispatcherDatabaseLock(OldIrql
);
1028 /* Return Old State */
1029 return PreviousState
;
1037 KeRevertToUserAffinityThread(VOID
)
1039 PKTHREAD CurrentThread
= KeGetCurrentThread();
1042 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
1044 /* Lock the Dispatcher Database */
1045 OldIrql
= KeAcquireDispatcherDatabaseLock();
1047 /* Return to User Affinity */
1048 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
1050 /* Disable System Affinity */
1051 CurrentThread
->SystemAffinityActive
= FALSE
;
1053 /* Check if we need to Dispatch a New thread */
1054 if (CurrentThread
->Affinity
& (1 << KeGetCurrentProcessorNumber())) {
1056 /* No, just release */
1057 KeReleaseDispatcherDatabaseLock(OldIrql
);
1061 /* We need to dispatch a new thread */
1062 CurrentThread
->WaitIrql
= OldIrql
;
1063 KiDispatchThreadNoLock(Ready
);
1064 KeLowerIrql(OldIrql
);
1073 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
1076 CCHAR PreviousIdealProcessor
;
1079 /* Lock the Dispatcher Database */
1080 OldIrql
= KeAcquireDispatcherDatabaseLock();
1082 /* Save Old Ideal Processor */
1083 PreviousIdealProcessor
= Thread
->IdealProcessor
;
1085 /* Set New Ideal Processor */
1086 Thread
->IdealProcessor
= Processor
;
1089 KeReleaseDispatcherDatabaseLock(OldIrql
);
1091 /* Return Old Ideal Processor */
1092 return PreviousIdealProcessor
;
1100 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
1102 PKTHREAD CurrentThread
= KeGetCurrentThread();
1105 ASSERT(Affinity
& ((1 << KeNumberProcessors
) - 1));
1107 /* Lock the Dispatcher Database */
1108 OldIrql
= KeAcquireDispatcherDatabaseLock();
1110 /* Set the System Affinity Specified */
1111 CurrentThread
->Affinity
= Affinity
;
1113 /* Enable System Affinity */
1114 CurrentThread
->SystemAffinityActive
= TRUE
;
1116 /* Check if we need to Dispatch a New thread */
1117 if (Affinity
& (1 << KeGetCurrentProcessorNumber())) {
1119 /* No, just release */
1120 KeReleaseDispatcherDatabaseLock(OldIrql
);
1124 /* We need to dispatch a new thread */
1125 CurrentThread
->WaitIrql
= OldIrql
;
1126 KiDispatchThreadNoLock(Ready
);
1127 KeLowerIrql(OldIrql
);
1133 KeQueryBasePriorityThread(IN PKTHREAD Thread
)
1135 LONG BasePriorityIncrement
;
1139 /* Lock the Dispatcher Database */
1140 OldIrql
= KeAcquireDispatcherDatabaseLock();
1142 /* Get the Process */
1143 Process
= Thread
->ApcStatePointer
[0]->Process
;
1145 /* Calculate the BPI */
1146 BasePriorityIncrement
= Thread
->BasePriority
- Process
->BasePriority
;
1148 /* If saturation occured, return the SI instead */
1149 if (Thread
->Saturation
) BasePriorityIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1153 KeReleaseDispatcherDatabaseLock(OldIrql
);
1155 /* Return Increment */
1156 return BasePriorityIncrement
;
1161 KiSetPriorityThread(PKTHREAD Thread
,
1165 KPRIORITY OldPriority
= Thread
->Priority
;
1169 DPRINT("Changing prio to : %lx\n", Priority
);
1171 /* Check if priority changed */
1172 if (OldPriority
!= Priority
)
1175 Thread
->Priority
= Priority
;
1177 /* Choose action based on thread's state */
1178 if (Thread
->State
== Ready
)
1180 /* Remove it from the current queue */
1181 KiRemoveFromThreadList(Thread
);
1183 /* Re-insert it at its current priority */
1184 KiInsertIntoThreadList(Priority
, Thread
);
1186 /* Check if the old priority was lower */
1187 if (KeGetCurrentThread()->Priority
< Priority
)
1189 /* Dispatch it immediately */
1190 KiDispatchThreadNoLock(Ready
);
1195 else if (Thread
->State
== Running
)
1197 /* Check if the new priority is lower */
1198 if (Priority
< OldPriority
)
1200 /* Check for threads with a higher priority */
1201 Mask
= ~((1 << (Priority
+ 1)) - 1);
1202 if (PriorityListMask
& Mask
)
1204 /* Found a thread, is it us? */
1205 if (Thread
== KeGetCurrentThread())
1208 KiDispatchThreadNoLock(Ready
);
1214 /* Loop every CPU */
1215 for (i
= 0; i
< KeNumberProcessors
; i
++)
1217 /* Get the PCR for this CPU */
1218 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1220 /* Reschedule if the new one is already on a CPU */
1221 if (Pcr
->Prcb
->CurrentThread
== Thread
)
1223 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1224 KiRequestReschedule(i
);
1235 /* Return to caller */
1244 KeSetBasePriorityThread(PKTHREAD Thread
,
1250 KPRIORITY CurrentBasePriority
;
1251 KPRIORITY BasePriority
;
1252 BOOLEAN Released
= FALSE
;
1253 LONG CurrentIncrement
;
1255 /* Lock the Dispatcher Database */
1256 OldIrql
= KeAcquireDispatcherDatabaseLock();
1258 /* Get the process and calculate current BP and BPI */
1259 Process
= Thread
->ApcStatePointer
[0]->Process
;
1260 CurrentBasePriority
= Thread
->BasePriority
;
1261 CurrentIncrement
= CurrentBasePriority
- Process
->BasePriority
;
1263 /* Change to use the SI if Saturation was used */
1264 if (Thread
->Saturation
) CurrentIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1267 /* Now check if saturation is being used for the new value */
1268 if (abs(Increment
) >= ((HIGH_PRIORITY
+ 1) / 2))
1270 /* Check if we need positive or negative saturation */
1271 Thread
->Saturation
= (Increment
> 0) ? 1 : -1;
1274 /* Normalize the Base Priority */
1275 BasePriority
= Process
->BasePriority
+ Increment
;
1276 if (Process
->BasePriority
>= LOW_REALTIME_PRIORITY
)
1278 /* Check if it's too low */
1279 if (BasePriority
< LOW_REALTIME_PRIORITY
)
1280 BasePriority
= LOW_REALTIME_PRIORITY
;
1282 /* Check if it's too high */
1283 if (BasePriority
> HIGH_PRIORITY
) BasePriority
= HIGH_PRIORITY
;
1285 /* We are at RTP, so use the raw BP */
1286 Priority
= BasePriority
;
1290 /* Check if it's entering RTP */
1291 if (BasePriority
>= LOW_REALTIME_PRIORITY
)
1292 BasePriority
= LOW_REALTIME_PRIORITY
- 1;
1294 /* Check if it's too low */
1295 if (BasePriority
<= LOW_PRIORITY
)
1298 /* If Saturation is used, then use the raw BP */
1299 if (Thread
->Saturation
)
1301 Priority
= BasePriority
;
1305 /* Calculate the new priority */
1306 Priority
= Thread
->Priority
+ (BasePriority
- CurrentBasePriority
)-
1307 Thread
->PriorityDecrement
;
1309 /* Make sure it won't enter RTP ranges */
1310 if (Priority
>= LOW_REALTIME_PRIORITY
)
1311 Priority
= LOW_REALTIME_PRIORITY
- 1;
1315 /* Finally set the new base priority */
1316 Thread
->BasePriority
= BasePriority
;
1318 /* Reset the decrements */
1319 Thread
->PriorityDecrement
= 0;
1321 /* If the priority will change, reset quantum and change it for real */
1322 if (Priority
!= Thread
->Priority
)
1324 Thread
->Quantum
= Thread
->QuantumReset
;
1325 KiSetPriorityThread(Thread
, Priority
, &Released
);
1328 /* Release Lock if needed */
1331 KeReleaseDispatcherDatabaseLock(OldIrql
);
1335 KeLowerIrql(OldIrql
);
1338 /* Return the Old Increment */
1339 return CurrentIncrement
;
1347 KeSetPriorityThread(PKTHREAD Thread
,
1350 KPRIORITY OldPriority
;
1351 BOOLEAN Released
= FALSE
;
1354 /* Lock the Dispatcher Database */
1355 OldIrql
= KeAcquireDispatcherDatabaseLock();
1357 /* Save the old Priority */
1358 OldPriority
= Thread
->Priority
;
1360 /* Reset the Quantum and Decrements */
1361 Thread
->Quantum
= Thread
->QuantumReset
;
1362 Thread
->PriorityDecrement
= 0;
1364 /* Set the new Priority */
1365 KiSetPriorityThread(Thread
, Priority
, &Released
);
1367 /* Release Lock if needed */
1370 KeReleaseDispatcherDatabaseLock(OldIrql
);
1374 KeLowerIrql(OldIrql
);
1377 /* Return Old Priority */
1384 * Sets thread's affinity
1388 KeSetAffinityThread(PKTHREAD Thread
,
1394 KAFFINITY ProcessorMask
;
1396 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread
, Affinity
);
1398 /* Verify correct affinity */
1399 if ((Affinity
& Thread
->ApcStatePointer
[0]->Process
->Affinity
) !=
1400 Affinity
|| !Affinity
)
1402 KEBUGCHECK(INVALID_AFFINITY_SET
);
1405 OldIrql
= KeAcquireDispatcherDatabaseLock();
1407 Thread
->UserAffinity
= Affinity
;
1409 if (Thread
->SystemAffinityActive
== FALSE
) {
1411 Thread
->Affinity
= Affinity
;
1413 if (Thread
->State
== Running
) {
1415 ProcessorMask
= 1 << KeGetCurrentProcessorNumber();
1416 if (Thread
== KeGetCurrentThread()) {
1418 if (!(Affinity
& ProcessorMask
)) {
1420 KiDispatchThreadNoLock(Ready
);
1421 KeLowerIrql(OldIrql
);
1422 return STATUS_SUCCESS
;
1427 for (i
= 0; i
< KeNumberProcessors
; i
++) {
1429 Pcr
= (PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
);
1430 if (Pcr
->Prcb
->CurrentThread
== Thread
) {
1432 if (!(Affinity
& ProcessorMask
)) {
1434 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1435 KiRequestReschedule(i
);
1436 KeLowerIrql(OldIrql
);
1437 return STATUS_SUCCESS
;
1444 ASSERT (i
< KeNumberProcessors
);
1449 KeReleaseDispatcherDatabaseLock(OldIrql
);
1450 return STATUS_SUCCESS
;
1456 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1459 KeTerminateThread(IN KPRIORITY Increment
)
1462 PKTHREAD Thread
= KeGetCurrentThread();
1463 PKPROCESS Process
= Thread
->ApcState
.Process
;
1464 PLIST_ENTRY
*ListHead
;
1465 PETHREAD Entry
, SavedEntry
;
1466 PETHREAD
*ThreadAddr
;
1467 DPRINT("Terminating\n");
1469 /* Lock the Dispatcher Database and the APC Queue */
1470 ASSERT_IRQL(DISPATCH_LEVEL
);
1471 OldIrql
= KeAcquireDispatcherDatabaseLock();
1472 ASSERT(Thread
->SwapBusy
== FALSE
);
1474 /* Make sure we won't get Swapped */
1475 Thread
->SwapBusy
= TRUE
;
1477 /* Save the Kernel and User Times */
1478 Process
->KernelTime
+= Thread
->KernelTime
;
1479 Process
->UserTime
+= Thread
->UserTime
;
1481 /* Get the current entry and our Port */
1482 Entry
= (PETHREAD
)PspReaperListHead
.Flink
;
1483 ThreadAddr
= &((PETHREAD
)Thread
)->ReaperLink
;
1485 /* Add it to the reaper's list */
1488 /* Get the list head */
1489 ListHead
= &PspReaperListHead
.Flink
;
1491 /* Link ourselves */
1492 *ThreadAddr
= Entry
;
1495 /* Now try to do the exchange */
1496 Entry
= InterlockedCompareExchangePointer(ListHead
, ThreadAddr
, Entry
);
1498 /* Break out if the change was succesful */
1499 } while (Entry
!= SavedEntry
);
1501 /* Check if the reaper wasn't active */
1504 /* Activate it as a work item, directly through its Queue */
1505 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1506 &PspReaperWorkItem
.List
,
1510 /* Handle Kernel Queues */
1513 DPRINT("Waking Queue\n");
1514 RemoveEntryList(&Thread
->QueueListEntry
);
1515 KiWakeQueue(Thread
->Queue
);
1518 /* Signal the thread */
1519 Thread
->DispatcherHeader
.SignalState
= TRUE
;
1520 if (IsListEmpty(&Thread
->DispatcherHeader
.WaitListHead
) != TRUE
)
1523 KiWaitTest((PVOID
)Thread
, Increment
);
1526 /* Remove the thread from the list */
1527 RemoveEntryList(&Thread
->ThreadListEntry
);
1529 /* Set us as terminated, decrease the Process's stack count */
1530 Thread
->State
= Terminated
;
1531 Process
->StackCount
--;
1533 /* Find a new Thread */
1534 KiDispatchThreadNoLock(Terminated
);
1538 * FUNCTION: Tests whether there are any pending APCs for the current thread
1539 * and if so the APCs will be delivered on exit from kernel mode
1543 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
1546 PKTHREAD Thread
= KeGetCurrentThread();
1549 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1551 /* Lock the Dispatcher Database and the APC Queue */
1552 OldIrql
= KeAcquireDispatcherDatabaseLock();
1553 KiAcquireSpinLock(&Thread
->ApcQueueLock
);
1555 /* Save the old State */
1556 OldState
= Thread
->Alerted
[AlertMode
];
1558 /* If the Thread is Alerted, Clear it */
1561 Thread
->Alerted
[AlertMode
] = FALSE
;
1563 } else if ((AlertMode
!= KernelMode
) && (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
]))) {
1565 /* If the mode is User and the Queue isn't empty, set Pending */
1566 Thread
->ApcState
.UserApcPending
= TRUE
;
1569 /* Release Locks and return the Old State */
1570 KiReleaseSpinLock(&Thread
->ApcQueueLock
);
1571 KeReleaseDispatcherDatabaseLock(OldIrql
);
1577 NtDelayExecution(IN BOOLEAN Alertable
,
1578 IN PLARGE_INTEGER DelayInterval
)
1580 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1581 LARGE_INTEGER SafeInterval
;
1584 /* Check if parameters are valid */
1585 if(PreviousMode
!= KernelMode
) {
1587 Status
= STATUS_SUCCESS
;
1591 /* make a copy on the kernel stack and let DelayInterval point to it so
1592 we don't need to wrap KeDelayExecutionThread in SEH! */
1593 SafeInterval
= ProbeForReadLargeInteger(DelayInterval
);
1594 DelayInterval
= &SafeInterval
;
1598 Status
= _SEH_GetExceptionCode();
1601 if (!NT_SUCCESS(Status
))
1607 /* Call the Kernel Function */
1608 Status
= KeDelayExecutionThread(PreviousMode
,