NT4/private/ntos/ke/thredobj.c
2020-09-30 17:12:29 +02:00

2217 lines
51 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
threadobj.c
Abstract:
This module implements the machine independent functions to manipulate
the kernel thread object. Functions are provided to initialize, ready,
alert, test alert, boost priority, enable APC queuing, disable APC
queuing, confine, set affinity, set priority, suspend, resume, alert
resume, terminate, read thread state, freeze, unfreeze, query data
alignment handling mode, force resume, and enter and leave critical
regions for thread objects.
Author:
David N. Cutler (davec) 4-Mar-1989
Environment:
Kernel mode only.
Revision History:
--*/
#include "ki.h"
//
// The following assert macro is used to check that an input thread object is
// really a kthread and not something else, like deallocated pool.
//
#define ASSERT_THREAD(E) { \
ASSERT((E)->Header.Type == ThreadObject); \
}
VOID
KeInitializeThread (
IN PKTHREAD Thread,
IN PVOID KernelStack,
IN PKSYSTEM_ROUTINE SystemRoutine,
IN PKSTART_ROUTINE StartRoutine OPTIONAL,
IN PVOID StartContext OPTIONAL,
IN PCONTEXT ContextFrame OPTIONAL,
IN PVOID Teb OPTIONAL,
IN PKPROCESS Process
)
/*++
Routine Description:
This function initializes a thread object. The priority, affinity,
and initial quantum are taken from the parent process object. The
thread object is inserted at the end of the thread list for the
parent process.
N.B. This routine is carefully written so that if an access violation
occurs while reading the specified context frame, then no kernel
data structures will have been modified. It is the responsibility
of the caller to handle the exception and provide necessary clean
up.
N.B. It is assumed that the thread object is zeroed.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
KernelStack - Supplies a pointer to the base of a kernel stack on which
the context frame for the thread is to be constructed.
SystemRoutine - Supplies a pointer to the system function that is to be
called when the thread is first scheduled for execution.
StartRoutine - Supplies an optional pointer to a function that is to be
called after the system has finished initializing the thread. This
parameter is specified if the thread is a system thread and will
execute totally in kernel mode.
StartContext - Supplies an optional pointer to an arbitrary data structure
which will be passed to the StartRoutine as a parameter. This
parameter is specified if the thread is a system thread and will
execute totally in kernel mode.
ContextFrame - Supplies an optional pointer a context frame which contains
the initial user mode state of the thread. This parameter is specified
if the thread is a user thread and will execute in user mode. If this
parameter is not specified, then the Teb parameter is ignored.
Teb - Supplies an optional pointer to the user mode thread environment
block. This parameter is specified if the thread is a user thread and
will execute in user mode. This parameter is ignored if the ContextFrame
parameter is not specified.
Process - Supplies a pointer to a control object of type process.
Return Value:
None.
--*/
{
ULONG Index;
KIRQL OldIrql;
PKTIMER Timer;
PKWAIT_BLOCK WaitBlock;
//
// Initialize the standard dispatcher object header and set the initial
// state of the thread object.
//
Thread->Header.Type = ThreadObject;
Thread->Header.Size = sizeof(KTHREAD) / sizeof(LONG);
InitializeListHead(&Thread->Header.WaitListHead);
//
// Initialize the owned mutant listhead.
//
InitializeListHead(&Thread->MutantListHead);
//
// Initialize the thread field of all builtin wait blocks.
//
for (Index = 0; Index < (THREAD_WAIT_OBJECTS + 1); Index += 1) {
Thread->WaitBlock[Index].Thread = Thread;
}
//
// Initialize the alerted, preempted, debugactive, autoalignment,
// kernel stack resident, enable kernel stack swap, and process
// ready queue boolean values.
//
// N.B. Only nonzero values are initialized.
//
Thread->AutoAlignment = Process->AutoAlignment;
Thread->EnableStackSwap = TRUE;
Thread->KernelStackResident = TRUE;
//
// Set the system service table pointer to the address of the static
// system service descriptor table. If the thread is later converted
// to a Win32 thread this pointer will be change to a pointer to the
// shadow system service descriptor table.
//
Thread->ServiceTable = (PVOID)&KeServiceDescriptorTable[0];
//
// Initialize the APC state pointers, the current APC state, the saved
// APC state, and enable APC queuing.
//
Thread->ApcStatePointer[0] = &Thread->ApcState;
Thread->ApcStatePointer[1] = &Thread->SavedApcState;
InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
Thread->ApcState.Process = Process;
Thread->ApcQueueable = TRUE;
//
// Initialize the kernel mode suspend APC and the suspend semaphore object.
// and the builtin wait timeout timer object.
//
KeInitializeApc(&Thread->SuspendApc,
Thread,
OriginalApcEnvironment,
(PKKERNEL_ROUTINE)KiSuspendNop,
(PKRUNDOWN_ROUTINE)NULL,
KiSuspendThread,
KernelMode,
NULL);
KeInitializeSemaphore(&Thread->SuspendSemaphore, 0L, 2L);
//
// Initialize the builtin timer trimer wait wait block.
//
// N.B. This is the only time the wait block is initialized sincs this
// information is constant.
//
Timer = &Thread->Timer;
KeInitializeTimer(Timer);
WaitBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
WaitBlock->Object = Timer;
WaitBlock->WaitKey = (CSHORT)STATUS_TIMEOUT;
WaitBlock->WaitType = WaitAny;
WaitBlock->WaitListEntry.Flink = &Timer->Header.WaitListHead;
WaitBlock->WaitListEntry.Blink = &Timer->Header.WaitListHead;
//
// Initialize the APC queue spinlock.
//
KeInitializeSpinLock(&Thread->ApcQueueLock);
//
// Initialize the Thread Environment Block (TEB) pointer (can be NULL).
//
Thread->Teb = Teb;
//
// Set the initial kernel stack and the initial thread context.
//
Thread->InitialStack = KernelStack;
Thread->StackBase = KernelStack;
Thread->StackLimit = (PVOID)((ULONG)KernelStack - KERNEL_STACK_SIZE);
KiInitializeContextThread(Thread,
SystemRoutine,
StartRoutine,
StartContext,
ContextFrame);
//
// Set the base thread priority, the thread priority, the thread affinity,
// the thread quantum, and the scheduling state.
//
Thread->BasePriority = Process->BasePriority;
Thread->Priority = Thread->BasePriority;
Thread->Affinity = Process->Affinity;
Thread->UserAffinity = Process->Affinity;
Thread->SystemAffinityActive = FALSE;
Thread->Quantum = Process->ThreadQuantum;
Thread->State = Initialized;
Thread->DisableBoost = Process->DisableBoost;
#ifdef i386
Thread->Iopl = Process->Iopl;
#endif
//
// Lock the dispatcher database, insert the thread in the process
// thread list, increment the kernel stack count, and unlock the
// dispatcher database.
//
// N.B. The distinguished value MAXSHORT is used to signify that no
// threads have been created for a process.
//
KiLockDispatcherDatabase(&OldIrql);
InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
if (Process->StackCount == MAXSHORT) {
Process->StackCount = 1;
} else {
Process->StackCount += 1;
}
//
// Initialize the ideal processor number for the thread.
//
// N.B. This must be done under the dispatcher lock to prevent byte
// granularity problems on Alpha.
//
Process->ThreadSeed += 1;
Thread->IdealProcessor = (UCHAR)(Process->ThreadSeed % KeNumberProcessors);
KiUnlockDispatcherDatabase(OldIrql);
return;
}
BOOLEAN
KeAlertThread (
IN PKTHREAD Thread,
IN KPROCESSOR_MODE AlertMode
)
/*++
Routine Description:
This function attempts to alert a thread and cause its execution to
be continued if it is currently in an alertable Wait state. Otherwise
it just sets the alerted variable for the specified processor mode.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
AlertMode - Supplies the processor mode for which the thread is
to be alerted.
Return Value:
The previous state of the alerted variable for the specified processor
mode.
--*/
{
BOOLEAN Alerted;
KIRQL OldIrql;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level, lock dispatcher database, and lock
// APC queue.
//
KiLockDispatcherDatabase(&OldIrql);
KiAcquireSpinLock(&Thread->ApcQueueLock);
//
// Capture the current state of the alerted variable for the specified
// processor mode.
//
Alerted = Thread->Alerted[AlertMode];
//
// If the alerted state for the specified processor mode is Not-Alerted,
// then attempt to alert the thread.
//
if (Alerted == FALSE) {
//
// If the thread is currently in a Wait state, the Wait is alertable,
// and the specified processor mode is less than or equal to the Wait
// mode, then the thread is unwaited with a status of "alerted".
//
if ((Thread->State == Waiting) && (Thread->Alertable == TRUE) &&
(AlertMode <= Thread->WaitMode)) {
KiUnwaitThread(Thread, STATUS_ALERTED, ALERT_INCREMENT);
} else {
Thread->Alerted[AlertMode] = TRUE;
}
}
//
// Unlock APC queue, unlock dispatcher database, lower IRQL to its
// previous value, and return the previous alerted state for the
// specified mode.
//
KiReleaseSpinLock(&Thread->ApcQueueLock);
KiUnlockDispatcherDatabase(OldIrql);
return Alerted;
}
ULONG
KeAlertResumeThread (
IN PKTHREAD Thread
)
/*++
Routine Description:
This function attempts to alert a thread in kernel mode and cause its
execution to be continued if it is currently in an alertable Wait state.
In addition, a resume operation is performed on the specified thread.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Return Value:
The previous suspend count.
--*/
{
ULONG OldCount;
KIRQL OldIrql;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level, lock dispatcher database, and lock
// APC queue.
//
KiLockDispatcherDatabase(&OldIrql);
KiAcquireSpinLock(&Thread->ApcQueueLock);
//
// If the kernel mode alerted state is FALSE, then attempt to alert
// the thread for kernel mode.
//
if (Thread->Alerted[KernelMode] == FALSE) {
//
// If the thread is currently in a Wait state and the Wait is alertable,
// then the thread is unwaited with a status of "alerted". Else set the
// kernel mode alerted variable.
//
if ((Thread->State == Waiting) && (Thread->Alertable == TRUE)) {
KiUnwaitThread(Thread, STATUS_ALERTED, ALERT_INCREMENT);
} else {
Thread->Alerted[KernelMode] = TRUE;
}
}
//
// Capture the current suspend count.
//
OldCount = Thread->SuspendCount;
//
// If the thread is currently suspended, then decrement its suspend count.
//
if (OldCount != 0) {
Thread->SuspendCount -= 1;
//
// If the resultant suspend count is zero and the freeze count is
// zero, then resume the thread by releasing its suspend semaphore.
//
if ((Thread->SuspendCount == 0) && (Thread->FreezeCount == 0)) {
Thread->SuspendSemaphore.Header.SignalState += 1;
KiWaitTest(&Thread->SuspendSemaphore, RESUME_INCREMENT);
}
}
//
// Unlock APC queue, unlock dispatcher database, lower IRQL to its
// previous value, and return the previous suspend count.
//
KiReleaseSpinLock(&Thread->ApcQueueLock);
KiUnlockDispatcherDatabase(OldIrql);
return OldCount;
}
VOID
KeBoostPriorityThread (
IN PKTHREAD Thread,
IN KPRIORITY Increment
)
/*++
Routine Description:
This function boosts the priority of the specified thread using the
same algorithm used when a thread gets a boost from a wait operation.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Increment - Supplies the priority increment that is to be applied to
the thread's priority.
Return Value:
None.
--*/
{
KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// If the thread does not run at a realtime priority level, then boost
// the thread priority.
//
if (Thread->Priority < LOW_REALTIME_PRIORITY) {
KiBoostPriorityThread(Thread, Increment);
}
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
return;
}
KAFFINITY
KeConfineThread (
VOID
)
/*++
Routine Description:
This function confines the execution of the current thread to the current
processor.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Return Value:
The previous affinity value.
--*/
{
KAFFINITY Affinity;
KIRQL OldIrql;
PKTHREAD Thread;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
Thread = KeGetCurrentThread();
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the current affinity and compute new affinity value by
// shifting a one bit left by the current processor number.
//
Affinity = Thread->Affinity;
Thread->Affinity = (KAFFINITY)(1 << Thread->NextProcessor);
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return the previous affinity value.
//
return Affinity;
}
BOOLEAN
KeDisableApcQueuingThread (
IN PKTHREAD Thread
)
/*++
Routine Description:
This function disables the queuing of APC's to the specified thread.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Return Value:
The previous value of the APC queuing state variable.
--*/
{
BOOLEAN ApcQueueable;
KIRQL OldIrql;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the current state of the APC queueable state variable and
// set its state to FALSE.
//
ApcQueueable = Thread->ApcQueueable;
Thread->ApcQueueable = FALSE;
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return the previous APC queueable state.
//
return ApcQueueable;
}
BOOLEAN
KeEnableApcQueuingThread (
IN PKTHREAD Thread
)
/*++
Routine Description:
This function enables the queuing of APC's to the specified thread.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Return Value:
The previous value of the APC queuing state variable.
--*/
{
BOOLEAN ApcQueueable;
KIRQL OldIrql;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the current state of the APC queueable state variable and
// set its state to TRUE.
//
ApcQueueable = Thread->ApcQueueable;
Thread->ApcQueueable = TRUE;
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return previous APC queueable state.
//
return ApcQueueable;
}
ULONG
KeForceResumeThread (
IN PKTHREAD Thread
)
/*++
Routine Description:
This function forces resumption of thread execution if the thread is
suspended. If the specified thread is not suspended, then no operation
is performed.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Return Value:
The sum of the previous suspend count and the freeze count.
--*/
{
ULONG OldCount;
KIRQL OldIrql;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the current suspend count.
//
OldCount = Thread->SuspendCount + Thread->FreezeCount;
//
// If the thread is currently suspended, then force resumption of
// thread execution.
//
if (OldCount != 0) {
Thread->FreezeCount = 0;
Thread->SuspendCount = 0;
Thread->SuspendSemaphore.Header.SignalState += 1;
KiWaitTest(&Thread->SuspendSemaphore, RESUME_INCREMENT);
}
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return the previous suspend count.
//
return OldCount;
}
VOID
KeFreezeAllThreads (
VOID
)
/*++
Routine Description:
This function suspends the execution of all thread in the current
process except the current thread. If the freeze count overflows
the maximum suspend count, then a condition is raised.
Arguments:
None.
Return Value:
None.
--*/
{
PKTHREAD CurrentThread;
PLIST_ENTRY ListHead;
PLIST_ENTRY NextEntry;
PKPROCESS Process;
PKTHREAD Thread;
ULONG OldCount;
KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Get the address of the current thread object, the current process
// object, raise IRQL to dispatch level, lock dispatcher database,
// and freeze the execution of all threads in the process except the
// current thread.
//
CurrentThread = KeGetCurrentThread();
Process = CurrentThread->ApcState.Process;
KiLockDispatcherDatabase(&OldIrql);
//
// If the freeze count of the current thread is not zero, then there
// is another thread that is trying to freeze this thread. Unlock the
// dispatcher, lower IRQL to its previous value, allow the suspend
// APC to occur, then raise IRQL to dispatch level, lock the dispatcher
// database, and try again.
//
while (CurrentThread->FreezeCount != 0) {
KiUnlockDispatcherDatabase(OldIrql);
KiLockDispatcherDatabase(&OldIrql);
}
KeEnterCriticalRegion();
//
// Freeze all threads except the current thread.
//
ListHead = &Process->ThreadListHead;
NextEntry = ListHead->Flink;
do {
//
// Get the address of the next thread and suspend it if it is
// not the current thread.
//
Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
if (Thread != CurrentThread) {
//
// Increment the freeze count. If the thread was not previously
// suspended, then queue the thread's suspend APC.
//
OldCount = Thread->FreezeCount;
ASSERT(OldCount != MAXIMUM_SUSPEND_COUNT);
Thread->FreezeCount += 1;
if ((OldCount == 0) && (Thread->SuspendCount == 0)) {
if (KiInsertQueueApc(&Thread->SuspendApc, RESUME_INCREMENT) == FALSE) {
Thread->SuspendSemaphore.Header.SignalState -= 1;
}
}
}
NextEntry = NextEntry->Flink;
} while (NextEntry != ListHead);
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
return;
}
BOOLEAN
KeQueryAutoAlignmentThread (
IN PKTHREAD Thread
)
/*++
Routine Description:
This function returns the data alignment handling mode for the specified
thread.
Arguments:
None.
Return Value:
A value of TRUE is returned if data alignment exceptions are being
automatically handled by the kernel. Otherwise, a value of FALSE
is returned.
--*/
{
ASSERT_THREAD(Thread);
//
// Return the data alignment handling mode for the thread.
//
return Thread->AutoAlignment;
}
LONG
KeQueryBasePriorityThread (
IN PKTHREAD Thread
)
/*++
Routine Description:
This function returns the base priority increment of the specified
thread.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Return Value:
The base priority increment of the specified thread.
--*/
{
LONG Increment;
KIRQL OldIrql;
PKPROCESS Process;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// If priority saturation occured the last time the thread base priority
// was set, then return the saturation increment value. Ohterwise, compute
// the increment value as the difference between the thread base priority
// and the process base priority.
//
Process = Thread->ApcStatePointer[0]->Process;
Increment = Thread->BasePriority - Process->BasePriority;
if (Thread->Saturation != FALSE) {
if (Increment > 0) {
Increment = ((HIGH_PRIORITY + 1) / 2);
} else {
Increment = -((HIGH_PRIORITY + 1) / 2);
}
}
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return the previous thread base priority increment.
//
return Increment;
}
BOOLEAN
KeReadStateThread (
IN PKTHREAD Thread
)
/*++
Routine Description:
This function reads the current signal state of a thread object.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Return Value:
The current signal state of the thread object.
--*/
{
ASSERT_THREAD(Thread);
//
// Return current signal state of thread object.
//
return (BOOLEAN)Thread->Header.SignalState;
}
VOID
KeReadyThread (
IN PKTHREAD Thread
)
/*++
Routine Description:
This function readies a thread for execution. If the thread's process
is currently not in the balance set, then the thread is inserted in the
thread's process' ready queue. Else if the thread is higher priority than
another thread that is currently running on a processor then the thread
is selected for execution on that processor. Else the thread is inserted
in the dispatcher ready queue selected by its priority.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Return Value:
None.
--*/
{
KIRQL OldIrql;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Ready the specified thread for execution.
//
KiReadyThread(Thread);
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
return;
}
ULONG
KeResumeThread (
IN PKTHREAD Thread
)
/*++
Routine Description:
This function resumes the execution of a suspended thread. If the
specified thread is not suspended, then no operation is performed.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Return Value:
The previous suspend count.
--*/
{
ULONG OldCount;
KIRQL OldIrql;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the current suspend count.
//
OldCount = Thread->SuspendCount;
//
// If the thread is currently suspended, then decrement its suspend count.
//
if (OldCount != 0) {
Thread->SuspendCount -= 1;
//
// If the resultant suspend count is zero and the freeze count is
// zero, then resume the thread by releasing its suspend semaphore.
//
if ((Thread->SuspendCount == 0) && (Thread->FreezeCount == 0)) {
Thread->SuspendSemaphore.Header.SignalState += 1;
KiWaitTest(&Thread->SuspendSemaphore, RESUME_INCREMENT);
}
}
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return the previous suspend count.
//
return OldCount;
}
VOID
KeRevertToUserAffinityThread (
VOID
)
/*++
Routine Description:
This function setss the affinity of the current thread to its user
affinity.
Arguments:
None.
Return Value:
None.
--*/
{
PRKTHREAD CurrentThread;
PRKTHREAD NextThread;
KIRQL OldIrql;
PKPRCB Prcb;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
ASSERT(KeGetCurrentThread()->SystemAffinityActive != FALSE);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
CurrentThread = KeGetCurrentThread();
KiLockDispatcherDatabase(&OldIrql);
//
// Set the current affinity to the user affinity.
//
// If the current processor is not in the new affinity set and another
// thread has not already been selected for execution on the current
// processor, then select a new thread for the current processor.
//
CurrentThread->Affinity = CurrentThread->UserAffinity;
CurrentThread->SystemAffinityActive = FALSE;
Prcb = KeGetCurrentPrcb();
if (((Prcb->SetMember & CurrentThread->Affinity) == 0) &&
(Prcb->NextThread == NULL)) {
NextThread = KiSelectNextThread(CurrentThread);
NextThread->State = Standby;
Prcb->NextThread = NextThread;
}
//
// Unlock dispatcher database and lower IRQL to its previous value.
//
KiUnlockDispatcherDatabase(OldIrql);
return;
}
VOID
KeRundownThread (
)
/*++
Routine Description:
This function is called by the executive to rundown thread structures
which must be guarded by the dispatcher database lock and which must
be processed before actually terminating the thread. An example of such
a structure is the mutant ownership list that is anchored in the kernel
thread object.
Arguments:
None.
Return Value:
None.
--*/
{
PKMUTANT Mutant;
PLIST_ENTRY NextEntry;
KIRQL OldIrql;
PKTHREAD Thread;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Rundown possible associated channel object or receive buffer.
//
#if 0
KiRundownChannel();
#endif
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
Thread = KeGetCurrentThread();
KiLockDispatcherDatabase(&OldIrql);
//
// Scan the list of owned mutant objects and release the mutant objects
// with an abandoned status. If the mutant is a kernel mutex, then bug
// check.
//
NextEntry = Thread->MutantListHead.Flink;
while (NextEntry != &Thread->MutantListHead) {
Mutant = CONTAINING_RECORD(NextEntry, KMUTANT, MutantListEntry);
if (Mutant->ApcDisable != 0) {
KeBugCheckEx(THREAD_TERMINATE_HELD_MUTEX,
(ULONG)Thread,
(ULONG)Mutant, 0, 0);
}
RemoveEntryList(&Mutant->MutantListEntry);
Mutant->Header.SignalState = 1;
Mutant->Abandoned = TRUE;
Mutant->OwnerThread = (PKTHREAD)NULL;
if (IsListEmpty(&Mutant->Header.WaitListHead) != TRUE) {
KiWaitTest(Mutant, MUTANT_INCREMENT);
}
NextEntry = Thread->MutantListHead.Flink;
}
//
// Rundown any architectural specific structures
//
KiRundownThread(Thread);
//
// Release dispatcher database lock and lower IRQL to its previous value.
//
KiUnlockDispatcherDatabase(OldIrql);
return;
}
KAFFINITY
KeSetAffinityThread (
IN PKTHREAD Thread,
IN KAFFINITY Affinity
)
/*++
Routine Description:
This function sets the affinity of a specified thread to a new
value. If the new affinity is not a proper subset of the parent
process affinity, or is null, then an error condition is raised.
If the specified thread is running on, or about to run on, a
processor for which it is no longer able to run, then the target
processor is rescheduled. If the specified thread is in a ready
state and is not in the parent process ready queue, then it is
rereadied to reevaluate any additional processors it may run on.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Affinity - Supplies the new of set of processors on which the thread
can run.
Return Value:
The previous affinity of the specified thread.
--*/
{
KAFFINITY OldAffinity;
KIRQL OldIrql;
PKPRCB Prcb;
PKPROCESS Process;
ULONG Processor;
KPRIORITY ThreadPriority;
PRKTHREAD Thread1;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the current affinity of the specified thread and get address
// of parent process object;
//
OldAffinity = Thread->UserAffinity;
Process = Thread->ApcState.Process;
//
// If new affinity is not a proper subset of the parent process affinity,
// or the new affinity is null, then bugcheck.
//
if (((Affinity & Process->Affinity) != (Affinity)) || (!Affinity)) {
KeBugCheck(INVALID_AFFINITY_SET);
}
//
// Set the thread user affinity to the specified value.
//
// If the thread is not current executing with system affinity active,
// then set the thread current affinity and switch on the thread state.
//
Thread->UserAffinity = Affinity;
if (Thread->SystemAffinityActive == FALSE) {
Thread->Affinity = Affinity;
switch (Thread->State) {
//
// Ready State.
//
// If the thread is not in the process ready queue, then remove
// it from its current dispatcher ready queue and reready it for
// execution.
//
case Ready:
if (Thread->ProcessReadyQueue == FALSE) {
RemoveEntryList(&Thread->WaitListEntry);
ThreadPriority = Thread->Priority;
if (IsListEmpty(&KiDispatcherReadyListHead[ThreadPriority]) != FALSE) {
ClearMember(ThreadPriority, KiReadySummary);
}
KiReadyThread(Thread);
}
break;
//
// Standby State.
//
// If the target processor is not in the new affinity set, then
// set the next thread to null for the target processor, select
// a new thread to run on the target processor, and reready the
// thread for execution.
//
case Standby:
Processor = Thread->NextProcessor;
Prcb = KiProcessorBlock[Processor];
if ((Prcb->SetMember & Affinity) == 0) {
Prcb->NextThread = NULL;
Thread1 = KiSelectNextThread(Thread);
Thread1->State = Standby;
Prcb->NextThread = Thread1;
KiReadyThread(Thread);
}
break;
//
// Running State.
//
// If the target processor is not in the new affinity set and
// another thread has not already been selected for execution
// on the target processor, then select a new thread for the
// target processor, and cause the target processor to be
// redispatched.
//
case Running:
Processor = Thread->NextProcessor;
Prcb = KiProcessorBlock[Processor];
if (((Prcb->SetMember & Affinity) == 0) &&
(Prcb->NextThread == NULL)) {
Thread1 = KiSelectNextThread(Thread);
Thread1->State = Standby;
Prcb->NextThread = Thread1;
KiRequestDispatchInterrupt(Processor);
}
break;
//
// Initialized, Terminated, Waiting, Transition case - For these
// states it is sufficient to just set the new thread affinity.
//
default:
break;
}
}
//
// Unlock dispatcher database, lower IRQL to its previous value, and
// return the previous user affinity.
//
KiUnlockDispatcherDatabase(OldIrql);
return OldAffinity;
}
VOID
KeSetSystemAffinityThread (
IN KAFFINITY Affinity
)
/*++
Routine Description:
This function set the system affinity of the current thread.
Arguments:
Affinity - Supplies the new of set of processors on which the thread
can run.
Return Value:
None.
--*/
{
PRKTHREAD CurrentThread;
PRKTHREAD NextThread;
KIRQL OldIrql;
PKPRCB Prcb;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
ASSERT((Affinity & KeActiveProcessors) != 0);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
CurrentThread = KeGetCurrentThread();
KiLockDispatcherDatabase(&OldIrql);
//
// Set the current affinity to the specified affinity.
//
// If the current processor is not in the new affinity set and another
// thread has not already been selected for execution on the current
// processor, then select a new thread for the current processor.
//
CurrentThread->Affinity = Affinity;
CurrentThread->SystemAffinityActive = TRUE;
Prcb = KeGetCurrentPrcb();
if (((Prcb->SetMember & CurrentThread->Affinity) == 0) &&
(Prcb->NextThread == NULL)) {
NextThread = KiSelectNextThread(CurrentThread);
NextThread->State = Standby;
Prcb->NextThread = NextThread;
}
//
// Unlock dispatcher database and lower IRQL to its previous value.
//
KiUnlockDispatcherDatabase(OldIrql);
return;
}
LONG
KeSetBasePriorityThread (
IN PKTHREAD Thread,
IN LONG Increment
)
/*++
Routine Description:
This function sets the base priority of the specified thread to a
new value. The new base priority for the thread is the process base
priority plus the increment.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Increment - Supplies the base priority increment of the subject thread.
N.B. If the absolute value of the increment is such that saturation
of the base priority is forced, then subsequent changes to the
parent process base priority will not change the base priority
of the thread.
Return Value:
The previous base priority increment of the specified thread.
--*/
{
KPRIORITY NewBase;
KPRIORITY NewPriority;
KPRIORITY OldBase;
LONG OldIncrement;
KIRQL OldIrql;
PKPROCESS Process;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the base priority of the specified thread and determine
// whether saturation if being forced.
//
Process = Thread->ApcStatePointer[0]->Process;
OldBase = Thread->BasePriority;
OldIncrement = OldBase - Process->BasePriority;
Thread->Saturation = FALSE;
if (abs(Increment) >= (HIGH_PRIORITY + 1) / 2) {
Thread->Saturation = TRUE;
}
//
// Set the base priority of the specified thread. If the thread's process
// is in the realtime class, then limit the change to the realtime class.
// Otherwise, limit the change to the variable class.
//
NewBase = Process->BasePriority + Increment;
if (Process->BasePriority >= LOW_REALTIME_PRIORITY) {
if (NewBase < LOW_REALTIME_PRIORITY) {
NewBase = LOW_REALTIME_PRIORITY;
} else if (NewBase > HIGH_PRIORITY) {
NewBase = HIGH_PRIORITY;
}
//
// Set the new priority of the thread to the new base priority.
//
NewPriority = NewBase;
} else {
if (NewBase >= LOW_REALTIME_PRIORITY) {
NewBase = LOW_REALTIME_PRIORITY - 1;
} else if (NewBase <= LOW_PRIORITY) {
NewBase = 1;
}
//
// Compute the new thread priority. If the new priority is outside
// the variable class, then set the new priority to the highest
// variable priority.
//
NewPriority = Thread->Priority +
(NewBase - OldBase) - Thread->PriorityDecrement;
if (NewPriority >= LOW_REALTIME_PRIORITY) {
NewPriority = LOW_REALTIME_PRIORITY - 1;
}
}
Thread->BasePriority = (SCHAR)NewBase;
Thread->Quantum = Process->ThreadQuantum;
Thread->DecrementCount = 0;
Thread->PriorityDecrement = 0;
KiSetPriorityThread(Thread, NewPriority);
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return the previous thread base priority.
//
return OldIncrement;
}
LOGICAL
KeSetDisableBoostThread (
IN PKTHREAD Thread,
IN LOGICAL Disable
)
/*++
Routine Description:
This function disables priority boosts for the specified thread.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Disable - Supplies a logical value that determines whether priority
boosts for the thread are disabled or enabled.
Return Value:
The previous value of the disable boost state variable.
--*/
{
LOGICAL DisableBoost;
KIRQL OldIrql;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the current state of the disable boost variable and set its
// state to TRUE.
//
DisableBoost = Thread->DisableBoost;
Thread->DisableBoost = (BOOLEAN)Disable;
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return the previous disable boost state.
//
return DisableBoost;
}
CCHAR
KeSetIdealProcessorThread (
IN PKTHREAD Thread,
IN CCHAR Processor
)
/*++
Routine Description:
This function sets the ideal processor for the specified thread execution.
Arguments:
Thread - Supplies a pointer to the thread whose ideal processor number is
set to the specfied value.
Processor - Supplies the number of the ideal processor (the distinguished
value MAXIMUM_PROCESSORS indicates that there is no ideal processor).
Return Value:
The previous ideal processor number.
--*/
{
CCHAR OldProcessor;
KIRQL OldIrql;
PKPROCESS Process;
//
// Capture the previous ideal processor value, set the new ideal
// processor value, and return the old ideal processor value for the
// current thread;
//
// Note that this is done under the dispatcher lock in order to
// synchronize the updates with the other fields that share the
// same DWORD. Otherwise there is a granularity problem on Alpha.
//
ASSERT(Processor <= MAXIMUM_PROCESSORS);
KiLockDispatcherDatabase(&OldIrql);
OldProcessor = Thread->IdealProcessor;
if (Processor < MAXIMUM_PROCESSORS) {
Thread->IdealProcessor = Processor;
} else {
Process = Thread->ApcState.Process;
Process->ThreadSeed += 1;
Thread->IdealProcessor = (UCHAR)(Process->ThreadSeed % KeNumberProcessors);
}
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
return OldProcessor;
}
BOOLEAN
KeSetKernelStackSwapEnable (
IN BOOLEAN Enable
)
/*++
Routine Description:
This function sets the kernel stack swap enable value for the current
thread and returns the old swap enable value.
Arguments:
Enable - Supplies the new kernel stack swap enable value.
Return Value:
The previous kernel stack swap enable value.
--*/
{
BOOLEAN OldState;
PKTHREAD Thread;
//
// Capture the previous kernel stack swap enable value, set the new
// swap enable value, and return the old swap enable value for the
// current thread;
//
Thread = KeGetCurrentThread();
OldState = Thread->EnableStackSwap;
Thread->EnableStackSwap = Enable;
return OldState;
}
KPRIORITY
KeSetPriorityThread (
IN PKTHREAD Thread,
IN KPRIORITY Priority
)
/*++
Routine Description:
This function sets the priority of the specified thread to a new value.
If the new thread priority is lower than the old thread priority, then
resecheduling may take place if the thread is currently running on, or
about to run on, a processor.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Priority - Supplies the new priority of the subject thread.
Return Value:
The previous priority of the specified thread.
--*/
{
KIRQL OldIrql;
KPRIORITY OldPriority;
PKPROCESS Process;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
ASSERT(((Priority != 0) || (Thread->BasePriority == 0)) &&
(Priority <= HIGH_PRIORITY));
ASSERT(KeIsExecutingDpc() == FALSE);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the current thread priority, set the thread priority to the
// the new value, and replenish the thread quantum. It is assumed that
// the priority would not be set unless the thread had already lost it
// initial quantum.
//
OldPriority = Thread->Priority;
Process = Thread->ApcStatePointer[0]->Process;
Thread->Quantum = Process->ThreadQuantum;
Thread->DecrementCount = 0;
Thread->PriorityDecrement = 0;
KiSetPriorityThread(Thread, Priority);
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return the previous thread priority.
//
return OldPriority;
}
ULONG
KeSuspendThread (
IN PKTHREAD Thread
)
/*++
Routine Description:
This function suspends the execution of a thread. If the suspend count
overflows the maximum suspend count, then a condition is raised.
Arguments:
Thread - Supplies a pointer to a dispatcher object of type thread.
Return Value:
The previous suspend count.
--*/
{
ULONG OldCount;
KIRQL OldIrql;
ASSERT_THREAD(Thread);
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
KiLockDispatcherDatabase(&OldIrql);
//
// Capture the current suspend count.
//
OldCount = Thread->SuspendCount;
//
// If the suspend count is at its maximum value, then unlock dispatcher
// database, lower IRQL to its previous value, and raise an error
// condition.
//
if (OldCount == MAXIMUM_SUSPEND_COUNT) {
//
// Unlock the dispatcher database and raise an exception.
//
KiUnlockDispatcherDatabase(OldIrql);
ExRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED);
}
//
// Increment the suspend count. If the thread was not previously suspended,
// then queue the thread's suspend APC.
//
Thread->SuspendCount += 1;
if ((OldCount == 0) && (Thread->FreezeCount == 0)) {
if (KiInsertQueueApc(&Thread->SuspendApc, RESUME_INCREMENT) == FALSE) {
Thread->SuspendSemaphore.Header.SignalState -= 1;
}
}
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
//
// Return the previous suspend count.
//
return OldCount;
}
VOID
KeTerminateThread (
IN KPRIORITY Increment
)
/*++
Routine Description:
This function terminates the execution of the current thread, sets the
signal state of the thread to Signaled, and attempts to satisfy as many
Waits as possible. The scheduling state of the thread is set to terminated,
and a new thread is selected to run on the current processor. There is no
return from this function.
Arguments:
None.
Return Value:
None.
--*/
{
PRKTHREAD NextThread;
KIRQL OldIrql;
PKPROCESS Process;
PRKQUEUE Queue;
PRKTHREAD Thread;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock dispatcher database.
//
Thread = KeGetCurrentThread();
KiLockDispatcherDatabase(&OldIrql);
//
// Insert the thread in the reaper list.
//
// If a reaper thread is not currently active, then insert a work item in
// the hyper critical work queue.
//
// N.B. This code has knowledge of the reaper data structures and how
// worker threads are implemented.
//
// N.B. The dispatcher database lock is used to synchronize access to the
// reaper list.
//
InsertTailList(&PsReaperListHead, &((PETHREAD)Thread)->TerminationPortList);
if (PsReaperActive == FALSE) {
PsReaperActive = TRUE;
KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue],
&PsReaperWorkItem.List,
FALSE);
}
//
// If the current thread is processing a queue entry, then remove
// the thrread from the queue object thread list and attempt to
// activate another thread that is blocked on the queue object.
//
Queue = Thread->Queue;
if (Queue != NULL) {
RemoveEntryList(&Thread->QueueListEntry);
KiActivateWaiterQueue(Queue);
}
//
// Set the state of the current thread object to Signaled, and attempt
// to satisfy as many Waits as possible.
//
Thread->Header.SignalState = TRUE;
if (IsListEmpty(&Thread->Header.WaitListHead) != TRUE) {
KiWaitTest((PVOID)Thread, Increment);
}
//
// Remove thread from its parent process' thread list.
//
RemoveEntryList(&Thread->ThreadListEntry);
//
// Set thread scheduling state to terminated, decrement the process'
// stack count, select a new thread to run on the current processor,
// and swap context to the new thread.
//
Thread->State = Terminated;
Process = Thread->ApcState.Process;
Process->StackCount -= 1;
if (Process->StackCount == 0) {
if (Process->ThreadListHead.Flink != &Process->ThreadListHead) {
Process->State = ProcessInTransition;
InsertTailList(&KiProcessOutSwapListHead, &Process->SwapListEntry);
KiSwapEvent.Header.SignalState = 1;
if (IsListEmpty(&KiSwapEvent.Header.WaitListHead) == FALSE) {
KiWaitTest(&KiSwapEvent, BALANCE_INCREMENT);
}
}
}
KiSwapThread();
return;
}
BOOLEAN
KeTestAlertThread (
IN KPROCESSOR_MODE AlertMode
)
/*++
Routine Description:
This function tests to determine if the alerted variable for the
specified processor mode has a value of TRUE or whether a user mode
APC should be delivered to the current thread.
Arguments:
AlertMode - Supplies the processor mode which is to be tested
for an alerted condition.
Return Value:
The previous state of the alerted variable for the specified processor
mode.
--*/
{
BOOLEAN Alerted;
KIRQL OldIrql;
PKTHREAD Thread;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Raise IRQL to dispatcher level and lock APC queue.
//
Thread = KeGetCurrentThread();
KiLockApcQueue(Thread, &OldIrql);
//
// If the current thread is alerted for the specified processor mode,
// then clear the alerted state. Else if the specified processor mode
// is user and the current thread's user mode APC queue contains an entry,
// then set user APC pending.
//
Alerted = Thread->Alerted[AlertMode];
if (Alerted == TRUE) {
Thread->Alerted[AlertMode] = FALSE;
} else if ((AlertMode == UserMode) &&
(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) != TRUE)) {
Thread->ApcState.UserApcPending = TRUE;
}
//
// Unlock APC queue, lower IRQL to its previous value, and return the
// previous alerted state for the specified mode.
//
KiUnlockApcQueue(Thread, OldIrql);
return Alerted;
}
VOID
KeThawAllThreads (
VOID
)
/*++
Routine Description:
This function resumes the execution of all suspended froozen threads
in the current process.
Arguments:
None.
Return Value:
None.
--*/
{
PLIST_ENTRY ListHead;
PLIST_ENTRY NextEntry;
PKPROCESS Process;
PKTHREAD Thread;
ULONG OldCount;
KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Get the address of the current current process object, raise IRQL
// to dispatch level, lock dispatcher database, and thaw the execution
// of all threads in the current process that have been frozzen.
//
Process = KeGetCurrentThread()->ApcState.Process;
KiLockDispatcherDatabase(&OldIrql);
ListHead = &Process->ThreadListHead;
NextEntry = ListHead->Flink;
do {
//
// Get the address of the next thread and thaw its execution if
// if was previously froozen.
//
Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
OldCount = Thread->FreezeCount;
if (OldCount != 0) {
Thread->FreezeCount -= 1;
//
// If the resultant suspend count is zero and the freeze count is
// zero, then resume the thread by releasing its suspend semaphore.
//
if ((Thread->SuspendCount == 0) && (Thread->FreezeCount == 0)) {
Thread->SuspendSemaphore.Header.SignalState += 1;
KiWaitTest(&Thread->SuspendSemaphore, RESUME_INCREMENT);
}
}
NextEntry = NextEntry->Flink;
} while (NextEntry != ListHead);
//
// Unlock dispatcher database and lower IRQL to its previous
// value.
//
KiUnlockDispatcherDatabase(OldIrql);
KeLeaveCriticalRegion();
return;
}