reactos/reactos/ntoskrnl/ke/thrdobj.c
Alex Ionescu aafcd436d5 - Thread Support cleanup 1: Separate kthread.c into thrdobj.c (Exported and globally accesible functions dealing with the KTHREAD object) and thrdschd.c (Fully internal thread scheduling routines for Ke* only).
- Also fix KeSetAffinityThread to return the old affinity, instead of NTSTATUS and move NtYieldExecution from Ps to Ke, and move NtDelayExecution to wait.c
- No code changes.

svn path=/trunk/; revision=24024
2006-09-10 15:23:20 +00:00

1198 lines
33 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/thrdobj.c
* PURPOSE: Implements routines to manage the Kernel Thread Object
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <internal/debug.h>
extern EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
extern LIST_ENTRY PspReaperListHead;
ULONG KiMask32Array[MAXIMUM_PRIORITY] =
{
0x1, 0x2, 0x4, 0x8, 0x10, 0x20,
0x40, 0x80, 0x100, 0x200, 0x4000, 0x800,
0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000,
0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000,
0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000, 0x20000000,
0x40000000, 0x80000000
};
/* FUNCTIONS *****************************************************************/
UCHAR
NTAPI
KeFindNextRightSetAffinity(IN UCHAR Number,
IN ULONG Set)
{
ULONG Bit, Result;
ASSERT(Set != 0);
/* Calculate the mask */
Bit = (AFFINITY_MASK(Number) - 1) & Set;
/* If it's 0, use the one we got */
if (!Bit) Bit = Set;
/* Now find the right set and return it */
BitScanReverse(&Result, Bit);
return (UCHAR)Result;
}
VOID
STDCALL
KiSuspendThreadKernelRoutine(PKAPC Apc,
PKNORMAL_ROUTINE* NormalRoutine,
PVOID* NormalContext,
PVOID* SystemArgument1,
PVOID* SystemArguemnt2)
{
}
VOID
STDCALL
KiSuspendThreadNormalRoutine(PVOID NormalContext,
PVOID SystemArgument1,
PVOID SystemArgument2)
{
PKTHREAD CurrentThread = KeGetCurrentThread();
/* Non-alertable kernel-mode suspended wait */
DPRINT("Waiting...\n");
KeWaitForSingleObject(&CurrentThread->SuspendSemaphore,
Suspended,
KernelMode,
FALSE,
NULL);
DPRINT("Done Waiting\n");
}
#ifdef KeGetCurrentThread
#undef KeGetCurrentThread
#endif
/*
* @implemented
*/
PKTHREAD
STDCALL
KeGetCurrentThread(VOID)
{
#ifdef CONFIG_SMP
ULONG Flags;
PKTHREAD Thread;
Ke386SaveFlags(Flags);
Ke386DisableInterrupts();
Thread = KeGetCurrentPrcb()->CurrentThread;
Ke386RestoreFlags(Flags);
return Thread;
#else
return(KeGetCurrentPrcb()->CurrentThread);
#endif
}
VOID
STDCALL
KeSetPreviousMode(ULONG Mode)
{
PsGetCurrentThread()->Tcb.PreviousMode = (UCHAR)Mode;
}
/*
* @implemented
*/
KPROCESSOR_MODE
STDCALL
KeGetPreviousMode(VOID)
{
return (ULONG)PsGetCurrentThread()->Tcb.PreviousMode;
}
BOOLEAN
STDCALL
KeDisableThreadApcQueueing(IN PKTHREAD Thread)
{
KIRQL OldIrql;
BOOLEAN PreviousState;
/* Lock the Dispatcher Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Save old state */
PreviousState = Thread->ApcQueueable;
/* Disable it now */
Thread->ApcQueueable = FALSE;
/* Release the Lock */
KeReleaseDispatcherDatabaseLock(OldIrql);
/* Return old state */
return PreviousState;
}
VOID
STDCALL
KeRundownThread(VOID)
{
KIRQL OldIrql;
PKTHREAD Thread = KeGetCurrentThread();
PLIST_ENTRY NextEntry, ListHead;
PKMUTANT Mutant;
DPRINT("KeRundownThread: %x\n", Thread);
/* Optimized path if nothing is on the list at the moment */
if (IsListEmpty(&Thread->MutantListHead)) return;
/* Lock the Dispatcher Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Get the List Pointers */
ListHead = &Thread->MutantListHead;
NextEntry = ListHead->Flink;
while (NextEntry != ListHead)
{
/* Get the Mutant */
Mutant = CONTAINING_RECORD(NextEntry, KMUTANT, MutantListEntry);
DPRINT1("Mutant: %p. Type, Size %x %x\n",
Mutant,
Mutant->Header.Type,
Mutant->Header.Size);
/* Make sure it's not terminating with APCs off */
if (Mutant->ApcDisable)
{
/* Bugcheck the system */
KEBUGCHECKEX(0,//THREAD_TERMINATE_HELD_MUTEX,
(ULONG_PTR)Thread,
(ULONG_PTR)Mutant,
0,
0);
}
/* Now we can remove it */
RemoveEntryList(&Mutant->MutantListEntry);
/* Unconditionally abandon it */
DPRINT("Abandonning the Mutant\n");
Mutant->Header.SignalState = 1;
Mutant->Abandoned = TRUE;
Mutant->OwnerThread = NULL;
/* Check if the Wait List isn't empty */
DPRINT("Checking whether to wake the Mutant\n");
if (!IsListEmpty(&Mutant->Header.WaitListHead))
{
/* Wake the Mutant */
DPRINT("Waking the Mutant\n");
KiWaitTest(&Mutant->Header, MUTANT_INCREMENT);
}
/* Move on */
NextEntry = NextEntry->Flink;
}
/* Release the Lock */
KeReleaseDispatcherDatabaseLock(OldIrql);
}
ULONG
NTAPI
KeResumeThread(IN PKTHREAD Thread)
{
ULONG PreviousCount;
KIRQL OldIrql;
DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread,
Thread->SuspendCount, Thread->FreezeCount);
/* Lock the Dispatcher */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Save the Old Count */
PreviousCount = Thread->SuspendCount;
/* Check if it existed */
if (PreviousCount) {
Thread->SuspendCount--;
/* Decrease the current Suspend Count and Check Freeze Count */
if ((!Thread->SuspendCount) && (!Thread->FreezeCount)) {
/* Signal the Suspend Semaphore */
Thread->SuspendSemaphore.Header.SignalState++;
KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
}
}
/* Release Lock and return the Old State */
KeReleaseDispatcherDatabaseLock(OldIrql);
return PreviousCount;
}
/*
* Used by the debugging code to freeze all the process's threads
* while the debugger is examining their state.
*/
VOID
STDCALL
KeFreezeAllThreads(PKPROCESS Process)
{
KIRQL OldIrql;
PKTHREAD Current;
PKTHREAD CurrentThread = KeGetCurrentThread();
/* Acquire Lock */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* If someone is already trying to free us, try again */
while (CurrentThread->FreezeCount)
{
/* Release and re-acquire the lock so the APC will go through */
KeReleaseDispatcherDatabaseLock(OldIrql);
OldIrql = KeAcquireDispatcherDatabaseLock();
}
/* Enter a critical region */
KeEnterCriticalRegion();
/* Loop the Process's Threads */
LIST_FOR_EACH(Current, &Process->ThreadListHead, KTHREAD, ThreadListEntry)
{
/* Make sure it's not ours */
if (Current != CurrentThread)
{
/* Should be bother inserting the APC? */
if (Current->ApcQueueable)
{
/* Make sure it wasn't already frozen, and that it's not suspended */
if (!(++Current->FreezeCount) && !(Current->SuspendCount))
{
/* Did we already insert it? */
if (!Current->SuspendApc.Inserted)
{
/* Insert the APC */
Current->SuspendApc.Inserted = TRUE;
KiInsertQueueApc(&Current->SuspendApc, IO_NO_INCREMENT);
}
else
{
/* Unsignal the Semaphore, the APC already got inserted */
Current->SuspendSemaphore.Header.SignalState--;
}
}
}
}
}
/* Release the lock */
KeReleaseDispatcherDatabaseLock(OldIrql);
}
NTSTATUS
STDCALL
KeSuspendThread(PKTHREAD Thread)
{
ULONG PreviousCount;
KIRQL OldIrql;
DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread, Thread->SuspendCount, Thread->FreezeCount);
/* Lock the Dispatcher */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Save the Old Count */
PreviousCount = Thread->SuspendCount;
/* Handle the maximum */
if (PreviousCount == MAXIMUM_SUSPEND_COUNT)
{
/* Raise an exception */
KeReleaseDispatcherDatabaseLock(OldIrql);
RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED);
}
/* Should we bother to queue at all? */
if (Thread->ApcQueueable)
{
/* Increment the suspend count */
Thread->SuspendCount++;
/* Check if we should suspend it */
if (!PreviousCount && !Thread->FreezeCount)
{
/* Is the APC already inserted? */
if (!Thread->SuspendApc.Inserted)
{
/* Not inserted, insert it */
Thread->SuspendApc.Inserted = TRUE;
KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT);
}
else
{
/* Unsignal the Semaphore, the APC already got inserted */
Thread->SuspendSemaphore.Header.SignalState--;
}
}
}
/* Release Lock and return the Old State */
KeReleaseDispatcherDatabaseLock(OldIrql);
return PreviousCount;
}
ULONG
STDCALL
KeForceResumeThread(IN PKTHREAD Thread)
{
KIRQL OldIrql;
ULONG PreviousCount;
/* Lock the Dispatcher Database and the APC Queue */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Save the old Suspend Count */
PreviousCount = Thread->SuspendCount + Thread->FreezeCount;
/* If the thread is suspended, wake it up!!! */
if (PreviousCount) {
/* Unwait it completely */
Thread->SuspendCount = 0;
Thread->FreezeCount = 0;
/* Signal and satisfy */
Thread->SuspendSemaphore.Header.SignalState++;
KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
}
/* Release Lock and return the Old State */
KeReleaseDispatcherDatabaseLock(OldIrql);
return PreviousCount;
}
ULONG
NTAPI
KeAlertResumeThread(IN PKTHREAD Thread)
{
ULONG PreviousCount;
KIRQL OldIrql;
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
/* Lock the Dispatcher Database and the APC Queue */
OldIrql = KeAcquireDispatcherDatabaseLock();
KiAcquireSpinLock(&Thread->ApcQueueLock);
/* Return if Thread is already alerted. */
if (Thread->Alerted[KernelMode] == FALSE) {
/* If it's Blocked, unblock if it we should */
if (Thread->State == Waiting && Thread->Alertable) {
DPRINT("Aborting Wait\n");
KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
} else {
/* If not, simply Alert it */
Thread->Alerted[KernelMode] = TRUE;
}
}
/* Save the old Suspend Count */
PreviousCount = Thread->SuspendCount;
/* If the thread is suspended, decrease one of the suspend counts */
if (PreviousCount) {
/* Decrease count. If we are now zero, unwait it completely */
if (--Thread->SuspendCount) {
/* Signal and satisfy */
Thread->SuspendSemaphore.Header.SignalState++;
KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
}
}
/* Release Locks and return the Old State */
KiReleaseSpinLock(&Thread->ApcQueueLock);
KeReleaseDispatcherDatabaseLock(OldIrql);
return PreviousCount;
}
BOOLEAN
NTAPI
KeAlertThread(IN PKTHREAD Thread,
IN KPROCESSOR_MODE AlertMode)
{
KIRQL OldIrql;
BOOLEAN PreviousState;
/* Acquire the Dispatcher Database Lock */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Save the Previous State */
PreviousState = Thread->Alerted[AlertMode];
/* Return if Thread is already alerted. */
if (PreviousState == FALSE) {
/* If it's Blocked, unblock if it we should */
if (Thread->State == Waiting &&
(AlertMode == KernelMode || Thread->WaitMode == AlertMode) &&
Thread->Alertable) {
DPRINT("Aborting Wait\n");
KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
} else {
/* If not, simply Alert it */
Thread->Alerted[AlertMode] = TRUE;
}
}
/* Release the Dispatcher Lock */
KeReleaseDispatcherDatabaseLock(OldIrql);
/* Return the old state */
return PreviousState;
}
/*
* @unimplemented
*/
VOID
STDCALL
KeCapturePersistentThreadState(IN PVOID CurrentThread,
IN ULONG Setting1,
IN ULONG Setting2,
IN ULONG Setting3,
IN ULONG Setting4,
IN ULONG Setting5,
IN PVOID ThreadState)
{
UNIMPLEMENTED;
}
VOID
NTAPI
KeUninitThread(IN PKTHREAD Thread)
{
/* Delete the stack */
MmDeleteKernelStack(Thread->StackBase, FALSE);
Thread->InitialStack = NULL;
}
NTSTATUS
NTAPI
KeInitThread(IN OUT PKTHREAD Thread,
IN PVOID KernelStack,
IN PKSYSTEM_ROUTINE SystemRoutine,
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext,
IN PCONTEXT Context,
IN PVOID Teb,
IN PKPROCESS Process)
{
BOOLEAN AllocatedStack = FALSE;
ULONG i;
PKWAIT_BLOCK TimerWaitBlock;
PKTIMER Timer;
NTSTATUS Status;
/* Initalize the Dispatcher Header */
KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
ThreadObject,
sizeof(KTHREAD) / sizeof(LONG),
FALSE);
/* Initialize the Mutant List */
InitializeListHead(&Thread->MutantListHead);
/* Initialize the wait blocks */
for (i = 0; i< (THREAD_WAIT_OBJECTS + 1); i++)
{
/* Put our pointer */
Thread->WaitBlock[i].Thread = Thread;
}
/* Set swap settings */
Thread->EnableStackSwap = FALSE;//TRUE;
Thread->IdealProcessor = 1;
Thread->SwapBusy = FALSE;
Thread->AdjustReason = 0;
/* Initialize the lock */
KeInitializeSpinLock(&Thread->ThreadLock);
/* Setup the Service Descriptor Table for Native Calls */
Thread->ServiceTable = KeServiceDescriptorTable;
/* Setup APC Fields */
InitializeListHead(&Thread->ApcState.ApcListHead[0]);
InitializeListHead(&Thread->ApcState.ApcListHead[1]);
Thread->ApcState.Process = Process;
Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
Thread->ApcStateIndex = OriginalApcEnvironment;
Thread->ApcQueueable = TRUE;
KeInitializeSpinLock(&Thread->ApcQueueLock);
/* Initialize the Suspend APC */
KeInitializeApc(&Thread->SuspendApc,
Thread,
OriginalApcEnvironment,
KiSuspendThreadKernelRoutine,
NULL,
KiSuspendThreadNormalRoutine,
KernelMode,
NULL);
/* Initialize the Suspend Semaphore */
KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 2);
/* Setup the timer */
Timer = &Thread->Timer;
KeInitializeTimer(Timer);
TimerWaitBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
TimerWaitBlock->Object = Timer;
TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
TimerWaitBlock->WaitType = WaitAny;
TimerWaitBlock->NextWaitBlock = NULL;
/* Link the two wait lists together */
TimerWaitBlock->WaitListEntry.Flink = &Timer->Header.WaitListHead;
TimerWaitBlock->WaitListEntry.Blink = &Timer->Header.WaitListHead;
/* Set the TEB */
Thread->Teb = Teb;
/* Check if we have a kernel stack */
if (!KernelStack)
{
/* We don't, allocate one */
KernelStack = (PVOID)((ULONG_PTR)MmCreateKernelStack(FALSE) +
KERNEL_STACK_SIZE);
if (!KernelStack) return STATUS_INSUFFICIENT_RESOURCES;
/* Remember for later */
AllocatedStack = TRUE;
}
/* Set the Thread Stacks */
Thread->InitialStack = (PCHAR)KernelStack;
Thread->StackBase = (PCHAR)KernelStack;
Thread->StackLimit = (ULONG_PTR)KernelStack - KERNEL_STACK_SIZE;
Thread->KernelStackResident = TRUE;
/* ROS Mm HACK */
MmUpdatePageDir((PEPROCESS)Process,
(PVOID)Thread->StackLimit,
KERNEL_STACK_SIZE);
MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
/* Enter SEH to avoid crashes due to user mode */
Status = STATUS_SUCCESS;
_SEH_TRY
{
/* Initalize the Thread Context */
Ke386InitThreadWithContext(Thread,
SystemRoutine,
StartRoutine,
StartContext,
Context);
}
_SEH_HANDLE
{
/* Set failure status */
Status = STATUS_UNSUCCESSFUL;
/* Check if a stack was allocated */
if (AllocatedStack)
{
/* Delete the stack */
MmDeleteKernelStack(Thread->StackBase, FALSE);
Thread->InitialStack = NULL;
}
}
_SEH_END;
/* Set the Thread to initalized */
Thread->State = Initialized;
return Status;
}
VOID
NTAPI
KeStartThread(IN OUT PKTHREAD Thread)
{
KIRQL OldIrql;
PKPROCESS Process = Thread->ApcState.Process;
PKNODE Node;
PKPRCB NodePrcb;
ULONG Set, Mask;
UCHAR IdealProcessor;
/* Setup static fields from parent */
Thread->Iopl = Process->Iopl;
Thread->Quantum = Process->QuantumReset;
Thread->QuantumReset = Process->QuantumReset;
Thread->SystemAffinityActive = FALSE;
/* Lock the process */
KeAcquireSpinLock(&Process->ProcessLock, &OldIrql);
/* Setup volatile data */
Thread->Priority = Process->BasePriority;
Thread->BasePriority = Process->BasePriority;
Thread->Affinity = Process->Affinity;
Thread->UserAffinity = Process->Affinity;
/* Get the KNODE and its PRCB */
Node = KeNodeBlock[Process->IdealNode];
NodePrcb = (PKPRCB)(KPCR_BASE + (Process->ThreadSeed * PAGE_SIZE));
/* Calculate affinity mask */
Set = ~NodePrcb->MultiThreadProcessorSet;
Mask = (ULONG)(Node->ProcessorMask & Process->Affinity);
Set &= Mask;
if (Set) Mask = Set;
/* Get the new thread seed */
IdealProcessor = KeFindNextRightSetAffinity(Process->ThreadSeed, Mask);
Process->ThreadSeed = IdealProcessor;
/* Sanity check */
ASSERT((Thread->UserAffinity & AFFINITY_MASK(IdealProcessor)));
/* Set the Ideal Processor */
Thread->IdealProcessor = IdealProcessor;
Thread->UserIdealProcessor = IdealProcessor;
/* Lock the Dispatcher Database */
KeAcquireDispatcherDatabaseLockAtDpcLevel();
/* Insert the thread into the process list */
InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
/* Increase the stack count */
ASSERT(Process->StackCount != MAXULONG_PTR);
Process->StackCount++;
/* Release locks and return */
KeReleaseDispatcherDatabaseLockFromDpcLevel();
KeReleaseSpinLock(&Process->ProcessLock, OldIrql);
}
VOID
NTAPI
KeInitializeThread(IN PKPROCESS Process,
IN OUT PKTHREAD Thread,
IN PKSYSTEM_ROUTINE SystemRoutine,
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext,
IN PCONTEXT Context,
IN PVOID Teb,
IN PVOID KernelStack)
{
/* Initailize and start the thread on success */
if (NT_SUCCESS(KeInitThread(Thread,
KernelStack,
SystemRoutine,
StartRoutine,
StartContext,
Context,
Teb,
Process)))
{
/* Start it */
KeStartThread(Thread);
}
}
/*
* @implemented
*/
KPRIORITY
STDCALL
KeQueryPriorityThread (IN PKTHREAD Thread)
{
return Thread->Priority;
}
/*
* @implemented
*/
ULONG
STDCALL
KeQueryRuntimeThread(IN PKTHREAD Thread,
OUT PULONG UserTime)
{
/* Return the User Time */
*UserTime = Thread->UserTime;
/* Return the Kernel Time */
return Thread->KernelTime;
}
/*
* @implemented
*/
BOOLEAN
STDCALL
KeSetKernelStackSwapEnable(IN BOOLEAN Enable)
{
PKTHREAD Thread = KeGetCurrentThread();
BOOLEAN PreviousState;
KIRQL OldIrql;
/* Lock the Dispatcher Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Save Old State */
PreviousState = Thread->EnableStackSwap;
/* Set New State */
Thread->EnableStackSwap = Enable;
/* No, Release Lock */
KeReleaseDispatcherDatabaseLock(OldIrql);
/* Return Old State */
return PreviousState;
}
/*
* @implemented
*/
VOID
STDCALL
KeRevertToUserAffinityThread(VOID)
{
PKTHREAD CurrentThread = KeGetCurrentThread();
KIRQL OldIrql;
ASSERT(CurrentThread->SystemAffinityActive != FALSE);
/* Lock the Dispatcher Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Return to User Affinity */
CurrentThread->Affinity = CurrentThread->UserAffinity;
/* Disable System Affinity */
CurrentThread->SystemAffinityActive = FALSE;
/* Check if we need to Dispatch a New thread */
if (CurrentThread->Affinity & (1 << KeGetCurrentProcessorNumber())) {
/* No, just release */
KeReleaseDispatcherDatabaseLock(OldIrql);
} else {
/* We need to dispatch a new thread */
CurrentThread->WaitIrql = OldIrql;
KiDispatchThreadNoLock(Ready);
KeLowerIrql(OldIrql);
}
}
/*
* @implemented
*/
UCHAR
STDCALL
KeSetIdealProcessorThread(IN PKTHREAD Thread,
IN UCHAR Processor)
{
CCHAR PreviousIdealProcessor;
KIRQL OldIrql;
/* Lock the Dispatcher Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Save Old Ideal Processor */
PreviousIdealProcessor = Thread->IdealProcessor;
/* Set New Ideal Processor */
Thread->IdealProcessor = Processor;
/* Release Lock */
KeReleaseDispatcherDatabaseLock(OldIrql);
/* Return Old Ideal Processor */
return PreviousIdealProcessor;
}
/*
* @implemented
*/
VOID
STDCALL
KeSetSystemAffinityThread(IN KAFFINITY Affinity)
{
PKTHREAD CurrentThread = KeGetCurrentThread();
KIRQL OldIrql;
ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
/* Lock the Dispatcher Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Set the System Affinity Specified */
CurrentThread->Affinity = Affinity;
/* Enable System Affinity */
CurrentThread->SystemAffinityActive = TRUE;
/* Check if we need to Dispatch a New thread */
if (Affinity & (1 << KeGetCurrentProcessorNumber())) {
/* No, just release */
KeReleaseDispatcherDatabaseLock(OldIrql);
} else {
/* We need to dispatch a new thread */
CurrentThread->WaitIrql = OldIrql;
KiDispatchThreadNoLock(Ready);
KeLowerIrql(OldIrql);
}
}
LONG
STDCALL
KeQueryBasePriorityThread(IN PKTHREAD Thread)
{
LONG BasePriorityIncrement;
KIRQL OldIrql;
PKPROCESS Process;
/* Lock the Dispatcher Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Get the Process */
Process = Thread->ApcStatePointer[0]->Process;
/* Calculate the BPI */
BasePriorityIncrement = Thread->BasePriority - Process->BasePriority;
/* If saturation occured, return the SI instead */
if (Thread->Saturation) BasePriorityIncrement = (HIGH_PRIORITY + 1) / 2 *
Thread->Saturation;
/* Release Lock */
KeReleaseDispatcherDatabaseLock(OldIrql);
/* Return Increment */
return BasePriorityIncrement;
}
/*
* @implemented
*/
LONG
STDCALL
KeSetBasePriorityThread(PKTHREAD Thread,
LONG Increment)
{
KIRQL OldIrql;
PKPROCESS Process;
KPRIORITY Priority;
KPRIORITY CurrentBasePriority;
KPRIORITY BasePriority;
BOOLEAN Released = FALSE;
LONG CurrentIncrement;
/* Lock the Dispatcher Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Get the process and calculate current BP and BPI */
Process = Thread->ApcStatePointer[0]->Process;
CurrentBasePriority = Thread->BasePriority;
CurrentIncrement = CurrentBasePriority - Process->BasePriority;
/* Change to use the SI if Saturation was used */
if (Thread->Saturation) CurrentIncrement = (HIGH_PRIORITY + 1) / 2 *
Thread->Saturation;
/* Now check if saturation is being used for the new value */
if (abs(Increment) >= ((HIGH_PRIORITY + 1) / 2))
{
/* Check if we need positive or negative saturation */
Thread->Saturation = (Increment > 0) ? 1 : -1;
}
/* Normalize the Base Priority */
BasePriority = Process->BasePriority + Increment;
if (Process->BasePriority >= LOW_REALTIME_PRIORITY)
{
/* Check if it's too low */
if (BasePriority < LOW_REALTIME_PRIORITY)
BasePriority = LOW_REALTIME_PRIORITY;
/* Check if it's too high */
if (BasePriority > HIGH_PRIORITY) BasePriority = HIGH_PRIORITY;
/* We are at RTP, so use the raw BP */
Priority = BasePriority;
}
else
{
/* Check if it's entering RTP */
if (BasePriority >= LOW_REALTIME_PRIORITY)
BasePriority = LOW_REALTIME_PRIORITY - 1;
/* Check if it's too low */
if (BasePriority <= LOW_PRIORITY)
BasePriority = 1;
/* If Saturation is used, then use the raw BP */
if (Thread->Saturation)
{
Priority = BasePriority;
}
else
{
/* Calculate the new priority */
Priority = Thread->Priority + (BasePriority - CurrentBasePriority)-
Thread->PriorityDecrement;
/* Make sure it won't enter RTP ranges */
if (Priority >= LOW_REALTIME_PRIORITY)
Priority = LOW_REALTIME_PRIORITY - 1;
}
}
/* Finally set the new base priority */
Thread->BasePriority = BasePriority;
/* Reset the decrements */
Thread->PriorityDecrement = 0;
/* If the priority will change, reset quantum and change it for real */
if (Priority != Thread->Priority)
{
Thread->Quantum = Thread->QuantumReset;
KiSetPriorityThread(Thread, Priority, &Released);
}
/* Release Lock if needed */
if (!Released)
{
KeReleaseDispatcherDatabaseLock(OldIrql);
}
else
{
KeLowerIrql(OldIrql);
}
/* Return the Old Increment */
return CurrentIncrement;
}
/*
* @implemented
*/
KPRIORITY
STDCALL
KeSetPriorityThread(PKTHREAD Thread,
KPRIORITY Priority)
{
KPRIORITY OldPriority;
BOOLEAN Released = FALSE;
KIRQL OldIrql;
/* Lock the Dispatcher Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Save the old Priority */
OldPriority = Thread->Priority;
/* Reset the Quantum and Decrements */
Thread->Quantum = Thread->QuantumReset;
Thread->PriorityDecrement = 0;
/* Set the new Priority */
KiSetPriorityThread(Thread, Priority, &Released);
/* Release Lock if needed */
if (!Released)
{
KeReleaseDispatcherDatabaseLock(OldIrql);
}
else
{
KeLowerIrql(OldIrql);
}
/* Return Old Priority */
return OldPriority;
}
/*
* @implemented
*
* Sets thread's affinity
*/
KAFFINITY
STDCALL
KeSetAffinityThread(PKTHREAD Thread,
KAFFINITY Affinity)
{
KIRQL OldIrql;
KAFFINITY OldAffinity;
BOOLEAN Released;
DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread, Affinity);
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Call the internal function */
OldAffinity = KiSetAffinityThread(Thread, Affinity, &Released);
/* Release Lock if needed */
if (!Released)
{
KeReleaseDispatcherDatabaseLock(OldIrql);
}
else
{
KeLowerIrql(OldIrql);
}
return OldAffinity;
}
/*
* @implemented
*/
/* The Increment Argument seems to be ignored by NT and always 0 when called */
VOID
STDCALL
KeTerminateThread(IN KPRIORITY Increment)
{
KIRQL OldIrql;
PKTHREAD Thread = KeGetCurrentThread();
PKPROCESS Process = Thread->ApcState.Process;
PLIST_ENTRY *ListHead;
PETHREAD Entry, SavedEntry;
PETHREAD *ThreadAddr;
DPRINT("Terminating\n");
/* Lock the Dispatcher Database and the APC Queue */
ASSERT_IRQL(DISPATCH_LEVEL);
OldIrql = KeAcquireDispatcherDatabaseLock();
ASSERT(Thread->SwapBusy == FALSE);
/* Make sure we won't get Swapped */
Thread->SwapBusy = TRUE;
/* Save the Kernel and User Times */
Process->KernelTime += Thread->KernelTime;
Process->UserTime += Thread->UserTime;
/* Get the current entry and our Port */
Entry = (PETHREAD)PspReaperListHead.Flink;
ThreadAddr = &((PETHREAD)Thread)->ReaperLink;
/* Add it to the reaper's list */
do
{
/* Get the list head */
ListHead = &PspReaperListHead.Flink;
/* Link ourselves */
*ThreadAddr = Entry;
SavedEntry = Entry;
/* Now try to do the exchange */
Entry = InterlockedCompareExchangePointer(ListHead, ThreadAddr, Entry);
/* Break out if the change was succesful */
} while (Entry != SavedEntry);
/* Check if the reaper wasn't active */
if (!Entry)
{
/* Activate it as a work item, directly through its Queue */
KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue,
&PspReaperWorkItem.List,
FALSE);
}
/* Handle Kernel Queues */
if (Thread->Queue)
{
DPRINT("Waking Queue\n");
RemoveEntryList(&Thread->QueueListEntry);
KiWakeQueue(Thread->Queue);
}
/* Signal the thread */
Thread->DispatcherHeader.SignalState = TRUE;
if (IsListEmpty(&Thread->DispatcherHeader.WaitListHead) != TRUE)
{
/* Satisfy waits */
KiWaitTest((PVOID)Thread, Increment);
}
/* Remove the thread from the list */
RemoveEntryList(&Thread->ThreadListEntry);
/* Set us as terminated, decrease the Process's stack count */
Thread->State = Terminated;
Process->StackCount--;
/* Find a new Thread */
KiDispatchThreadNoLock(Terminated);
}
/*
* FUNCTION: Tests whether there are any pending APCs for the current thread
* and if so the APCs will be delivered on exit from kernel mode
*/
BOOLEAN
STDCALL
KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
{
KIRQL OldIrql;
PKTHREAD Thread = KeGetCurrentThread();
BOOLEAN OldState;
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
/* Lock the Dispatcher Database and the APC Queue */
OldIrql = KeAcquireDispatcherDatabaseLock();
KiAcquireSpinLock(&Thread->ApcQueueLock);
/* Save the old State */
OldState = Thread->Alerted[AlertMode];
/* If the Thread is Alerted, Clear it */
if (OldState) {
Thread->Alerted[AlertMode] = FALSE;
} else if ((AlertMode != KernelMode) && (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) {
/* If the mode is User and the Queue isn't empty, set Pending */
Thread->ApcState.UserApcPending = TRUE;
}
/* Release Locks and return the Old State */
KiReleaseSpinLock(&Thread->ApcQueueLock);
KeReleaseDispatcherDatabaseLock(OldIrql);
return OldState;
}