mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 13:01:40 +00:00
1469 lines
40 KiB
C
1469 lines
40 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 <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, 0x400, 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;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
KeReadStateThread(IN PKTHREAD Thread)
|
|
{
|
|
ASSERT_THREAD(Thread);
|
|
|
|
/* Return signal state */
|
|
return (BOOLEAN)Thread->Header.SignalState;
|
|
}
|
|
|
|
KPRIORITY
|
|
NTAPI
|
|
KeQueryBasePriorityThread(IN PKTHREAD Thread)
|
|
{
|
|
LONG BaseIncrement;
|
|
KIRQL OldIrql;
|
|
PKPROCESS Process;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Raise IRQL to synch level */
|
|
OldIrql = KeRaiseIrqlToSynchLevel();
|
|
|
|
/* Lock the thread */
|
|
KiAcquireThreadLock(Thread);
|
|
|
|
/* Get the Process */
|
|
Process = Thread->ApcStatePointer[0]->Process;
|
|
|
|
/* Calculate the base increment */
|
|
BaseIncrement = Thread->BasePriority - Process->BasePriority;
|
|
|
|
/* If saturation occured, return the saturation increment instead */
|
|
if (Thread->Saturation) BaseIncrement = (HIGH_PRIORITY + 1) / 2 *
|
|
Thread->Saturation;
|
|
|
|
/* Release thread lock */
|
|
KiReleaseThreadLock(Thread);
|
|
|
|
/* Lower IRQl and return Increment */
|
|
KeLowerIrql(OldIrql);
|
|
return BaseIncrement;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
KeSetDisableBoostThread(IN OUT PKTHREAD Thread,
|
|
IN BOOLEAN Disable)
|
|
{
|
|
ASSERT_THREAD(Thread);
|
|
|
|
/* Check if we're enabling or disabling */
|
|
if (Disable)
|
|
{
|
|
/* Set the bit */
|
|
return InterlockedBitTestAndSet(&Thread->ThreadFlags, 1);
|
|
}
|
|
else
|
|
{
|
|
/* Remove the bit */
|
|
return InterlockedBitTestAndReset(&Thread->ThreadFlags, 1);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KeReadyThread(IN PKTHREAD Thread)
|
|
{
|
|
KIRQL OldIrql;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KiAcquireDispatcherLock();
|
|
|
|
/* Make the thread ready */
|
|
KiReadyThread(Thread);
|
|
|
|
/* Unlock dispatcher database */
|
|
KiReleaseDispatcherLock(OldIrql);
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
KeAlertResumeThread(IN PKTHREAD Thread)
|
|
{
|
|
ULONG PreviousCount;
|
|
KLOCK_QUEUE_HANDLE ApcLock;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the Dispatcher Database and the APC Queue */
|
|
KiAcquireApcLock(Thread, &ApcLock);
|
|
KiAcquireDispatcherLockAtDpcLevel();
|
|
|
|
/* Return if Thread is already alerted. */
|
|
if (!Thread->Alerted[KernelMode])
|
|
{
|
|
/* If it's Blocked, unblock if it we should */
|
|
if ((Thread->State == Waiting) && (Thread->Alertable))
|
|
{
|
|
/* Abort the wait */
|
|
KiUnwaitThread(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 */
|
|
Thread->SuspendCount--;
|
|
if (!(Thread->SuspendCount) && !(Thread->FreezeCount))
|
|
{
|
|
/* Signal and satisfy */
|
|
Thread->SuspendSemaphore.Header.SignalState++;
|
|
KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
|
|
/* Release Locks and return the Old State */
|
|
KiReleaseDispatcherLockFromDpcLevel();
|
|
KiReleaseApcLockFromDpcLevel(&ApcLock);
|
|
KiExitDispatcher(ApcLock.OldIrql);
|
|
return PreviousCount;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
KeAlertThread(IN PKTHREAD Thread,
|
|
IN KPROCESSOR_MODE AlertMode)
|
|
{
|
|
BOOLEAN PreviousState;
|
|
KLOCK_QUEUE_HANDLE ApcLock;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the Dispatcher Database and the APC Queue */
|
|
KiAcquireApcLock(Thread, &ApcLock);
|
|
KiAcquireDispatcherLockAtDpcLevel();
|
|
|
|
/* Save the Previous State */
|
|
PreviousState = Thread->Alerted[AlertMode];
|
|
|
|
/* Check if it's already alerted */
|
|
if (!PreviousState)
|
|
{
|
|
/* Check if the thread is alertable, and blocked in the given mode */
|
|
if ((Thread->State == Waiting) &&
|
|
(Thread->Alertable) &&
|
|
(AlertMode <= Thread->WaitMode))
|
|
{
|
|
/* Abort the wait to alert the thread */
|
|
KiUnwaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, merely set the alerted state */
|
|
Thread->Alerted[AlertMode] = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Release the Dispatcher Lock */
|
|
KiReleaseDispatcherLockFromDpcLevel();
|
|
KiReleaseApcLockFromDpcLevel(&ApcLock);
|
|
KiExitDispatcher(ApcLock.OldIrql);
|
|
|
|
/* Return the old state */
|
|
return PreviousState;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KeBoostPriorityThread(IN PKTHREAD Thread,
|
|
IN KPRIORITY Increment)
|
|
{
|
|
KIRQL OldIrql;
|
|
KPRIORITY Priority;
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KiAcquireDispatcherLock();
|
|
|
|
/* Only threads in the dynamic range get boosts */
|
|
if (Thread->Priority < LOW_REALTIME_PRIORITY)
|
|
{
|
|
/* Lock the thread */
|
|
KiAcquireThreadLock(Thread);
|
|
|
|
/* Check again, and make sure there's not already a boost */
|
|
if ((Thread->Priority < LOW_REALTIME_PRIORITY) &&
|
|
!(Thread->PriorityDecrement))
|
|
{
|
|
/* Compute the new priority and see if it's higher */
|
|
Priority = Thread->BasePriority + Increment;
|
|
if (Priority > Thread->Priority)
|
|
{
|
|
if (Priority >= LOW_REALTIME_PRIORITY)
|
|
{
|
|
Priority = LOW_REALTIME_PRIORITY - 1;
|
|
}
|
|
|
|
/* Reset the quantum */
|
|
Thread->Quantum = Thread->QuantumReset;
|
|
|
|
/* Set the new Priority */
|
|
KiSetPriorityThread(Thread, Priority);
|
|
}
|
|
}
|
|
|
|
/* Release thread lock */
|
|
KiReleaseThreadLock(Thread);
|
|
}
|
|
|
|
/* Release the dispatcher lokc */
|
|
KiReleaseDispatcherLock(OldIrql);
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
KeForceResumeThread(IN PKTHREAD Thread)
|
|
{
|
|
KLOCK_QUEUE_HANDLE ApcLock;
|
|
ULONG PreviousCount;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the APC Queue */
|
|
KiAcquireApcLock(Thread, &ApcLock);
|
|
|
|
/* 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;
|
|
|
|
/* Lock the dispatcher */
|
|
KiAcquireDispatcherLockAtDpcLevel();
|
|
|
|
/* Signal and satisfy */
|
|
Thread->SuspendSemaphore.Header.SignalState++;
|
|
KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
|
|
|
|
/* Release the dispatcher */
|
|
KiReleaseDispatcherLockFromDpcLevel();
|
|
}
|
|
|
|
/* Release Lock and return the Old State */
|
|
KiReleaseApcLockFromDpcLevel(&ApcLock);
|
|
KiExitDispatcher(ApcLock.OldIrql);
|
|
return PreviousCount;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KeFreezeAllThreads(VOID)
|
|
{
|
|
KLOCK_QUEUE_HANDLE LockHandle, ApcLock;
|
|
PKTHREAD Current, CurrentThread = KeGetCurrentThread();
|
|
PKPROCESS Process = CurrentThread->ApcState.Process;
|
|
PLIST_ENTRY ListHead, NextEntry;
|
|
LONG OldCount;
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the process */
|
|
KiAcquireProcessLock(Process, &LockHandle);
|
|
|
|
/* If someone is already trying to free us, try again */
|
|
while (CurrentThread->FreezeCount)
|
|
{
|
|
/* Release and re-acquire the process lock so the APC will go through */
|
|
KiReleaseProcessLock(&LockHandle);
|
|
KiAcquireProcessLock(Process, &LockHandle);
|
|
}
|
|
|
|
/* Enter a critical region */
|
|
KeEnterCriticalRegion();
|
|
|
|
/* Loop the Process's Threads */
|
|
ListHead = &Process->ThreadListHead;
|
|
NextEntry = ListHead->Flink;
|
|
do
|
|
{
|
|
/* Get the current thread */
|
|
Current = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
|
|
|
|
/* Lock it */
|
|
KiAcquireApcLockAtDpcLevel(Current, &ApcLock);
|
|
|
|
/* Make sure it's not ours, and check if APCs are enabled */
|
|
if ((Current != CurrentThread) && (Current->ApcQueueable))
|
|
{
|
|
/* Sanity check */
|
|
OldCount = Current->SuspendCount;
|
|
ASSERT(OldCount != MAXIMUM_SUSPEND_COUNT);
|
|
|
|
/* Increase the freeze count */
|
|
Current->FreezeCount++;
|
|
|
|
/* Make sure it wasn't already suspended */
|
|
if (!(OldCount) && !(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
|
|
{
|
|
/* Lock the dispatcher */
|
|
KiAcquireDispatcherLockAtDpcLevel();
|
|
|
|
/* Unsignal the semaphore, the APC was already inserted */
|
|
Current->SuspendSemaphore.Header.SignalState--;
|
|
|
|
/* Release the dispatcher */
|
|
KiReleaseDispatcherLockFromDpcLevel();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Release the APC lock */
|
|
KiReleaseApcLockFromDpcLevel(&ApcLock);
|
|
|
|
/* Move to the next thread */
|
|
NextEntry = NextEntry->Flink;
|
|
} while (NextEntry != ListHead);
|
|
|
|
/* Release the process lock and exit the dispatcher */
|
|
KiReleaseProcessLockFromDpcLevel(&LockHandle);
|
|
KiExitDispatcher(LockHandle.OldIrql);
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
KeResumeThread(IN PKTHREAD Thread)
|
|
{
|
|
KLOCK_QUEUE_HANDLE ApcLock;
|
|
ULONG PreviousCount;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the APC Queue */
|
|
KiAcquireApcLock(Thread, &ApcLock);
|
|
|
|
/* Save the Old Count */
|
|
PreviousCount = Thread->SuspendCount;
|
|
|
|
/* Check if it existed */
|
|
if (PreviousCount)
|
|
{
|
|
/* Decrease the suspend count */
|
|
Thread->SuspendCount--;
|
|
|
|
/* Check if the thrad is still suspended or not */
|
|
if ((!Thread->SuspendCount) && (!Thread->FreezeCount))
|
|
{
|
|
/* Acquire the dispatcher lock */
|
|
KiAcquireDispatcherLockAtDpcLevel();
|
|
|
|
/* Signal the Suspend Semaphore */
|
|
Thread->SuspendSemaphore.Header.SignalState++;
|
|
KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
|
|
|
|
/* Release the dispatcher lock */
|
|
KiReleaseDispatcherLockFromDpcLevel();
|
|
}
|
|
}
|
|
|
|
/* Release APC Queue lock and return the Old State */
|
|
KiReleaseApcLockFromDpcLevel(&ApcLock);
|
|
KiExitDispatcher(ApcLock.OldIrql);
|
|
return PreviousCount;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KeRundownThread(VOID)
|
|
{
|
|
KIRQL OldIrql;
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
PLIST_ENTRY NextEntry, ListHead;
|
|
PKMUTANT Mutant;
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Optimized path if nothing is on the list at the moment */
|
|
if (IsListEmpty(&Thread->MutantListHead)) return;
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KiAcquireDispatcherLock();
|
|
|
|
/* Get the List Pointers */
|
|
ListHead = &Thread->MutantListHead;
|
|
NextEntry = ListHead->Flink;
|
|
while (NextEntry != ListHead)
|
|
{
|
|
/* Get the Mutant */
|
|
Mutant = CONTAINING_RECORD(NextEntry, KMUTANT, MutantListEntry);
|
|
ASSERT_MUTANT(Mutant);
|
|
|
|
/* Make sure it's not terminating with APCs off */
|
|
if (Mutant->ApcDisable)
|
|
{
|
|
/* Bugcheck the system */
|
|
KeBugCheckEx(THREAD_TERMINATE_HELD_MUTEX,
|
|
(ULONG_PTR)Thread,
|
|
(ULONG_PTR)Mutant,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
/* Now we can remove it */
|
|
RemoveEntryList(&Mutant->MutantListEntry);
|
|
|
|
/* Unconditionally abandon it */
|
|
Mutant->Header.SignalState = 1;
|
|
Mutant->Abandoned = TRUE;
|
|
Mutant->OwnerThread = NULL;
|
|
|
|
/* Check if the Wait List isn't empty */
|
|
if (!IsListEmpty(&Mutant->Header.WaitListHead))
|
|
{
|
|
/* Wake the Mutant */
|
|
KiWaitTest(&Mutant->Header, MUTANT_INCREMENT);
|
|
}
|
|
|
|
/* Move on */
|
|
NextEntry = Thread->MutantListHead.Flink;
|
|
}
|
|
|
|
/* Release the Lock */
|
|
KiReleaseDispatcherLock(OldIrql);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KeStartThread(IN OUT PKTHREAD Thread)
|
|
{
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|
#ifdef CONFIG_SMP
|
|
PKNODE Node;
|
|
PKPRCB NodePrcb;
|
|
ULONG Set, Mask;
|
|
#endif
|
|
UCHAR IdealProcessor = 0;
|
|
PKPROCESS Process = Thread->ApcState.Process;
|
|
|
|
/* Setup static fields from parent */
|
|
Thread->DisableBoost = Process->DisableBoost;
|
|
#if defined(_M_IX86)
|
|
Thread->Iopl = Process->Iopl;
|
|
#endif
|
|
Thread->Quantum = Process->QuantumReset;
|
|
Thread->QuantumReset = Process->QuantumReset;
|
|
Thread->SystemAffinityActive = FALSE;
|
|
|
|
/* Lock the process */
|
|
KiAcquireProcessLock(Process, &LockHandle);
|
|
|
|
/* Setup volatile data */
|
|
Thread->Priority = Process->BasePriority;
|
|
Thread->BasePriority = Process->BasePriority;
|
|
Thread->Affinity = Process->Affinity;
|
|
Thread->UserAffinity = Process->Affinity;
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* Get the KNODE and its PRCB */
|
|
Node = KeNodeBlock[Process->IdealNode];
|
|
NodePrcb = KiProcessorBlock[Process->ThreadSeed];
|
|
|
|
/* Calculate affinity mask */
|
|
#ifdef _M_ARM
|
|
DbgBreakPoint();
|
|
Set = 0;
|
|
#else
|
|
Set = ~NodePrcb->MultiThreadProcessorSet;
|
|
#endif
|
|
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)));
|
|
#endif
|
|
|
|
/* Set the Ideal Processor */
|
|
Thread->IdealProcessor = IdealProcessor;
|
|
Thread->UserIdealProcessor = IdealProcessor;
|
|
|
|
/* Lock the Dispatcher Database */
|
|
KiAcquireDispatcherLockAtDpcLevel();
|
|
|
|
/* 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 */
|
|
KiReleaseDispatcherLockFromDpcLevel();
|
|
KiReleaseProcessLock(&LockHandle);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KiSuspendRundown(IN PKAPC Apc)
|
|
{
|
|
/* Does nothing */
|
|
UNREFERENCED_PARAMETER(Apc);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KiSuspendNop(IN PKAPC Apc,
|
|
IN PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN PVOID *NormalContext,
|
|
IN PVOID *SystemArgument1,
|
|
IN PVOID *SystemArgument2)
|
|
{
|
|
/* Does nothing */
|
|
UNREFERENCED_PARAMETER(Apc);
|
|
UNREFERENCED_PARAMETER(NormalRoutine);
|
|
UNREFERENCED_PARAMETER(NormalContext);
|
|
UNREFERENCED_PARAMETER(SystemArgument1);
|
|
UNREFERENCED_PARAMETER(SystemArgument2);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KiSuspendThread(IN PVOID NormalContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2)
|
|
{
|
|
/* Non-alertable kernel-mode suspended wait */
|
|
KeWaitForSingleObject(&KeGetCurrentThread()->SuspendSemaphore,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
KeSuspendThread(PKTHREAD Thread)
|
|
{
|
|
KLOCK_QUEUE_HANDLE ApcLock;
|
|
ULONG PreviousCount;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the APC Queue */
|
|
KiAcquireApcLock(Thread, &ApcLock);
|
|
|
|
/* Save the Old Count */
|
|
PreviousCount = Thread->SuspendCount;
|
|
|
|
/* Handle the maximum */
|
|
if (PreviousCount == MAXIMUM_SUSPEND_COUNT)
|
|
{
|
|
/* Raise an exception */
|
|
KiReleaseApcLock(&ApcLock);
|
|
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
|
|
{
|
|
/* Lock the dispatcher */
|
|
KiAcquireDispatcherLockAtDpcLevel();
|
|
|
|
/* Unsignal the semaphore, the APC was already inserted */
|
|
Thread->SuspendSemaphore.Header.SignalState--;
|
|
|
|
/* Release the dispatcher */
|
|
KiReleaseDispatcherLockFromDpcLevel();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Release Lock and return the Old State */
|
|
KiReleaseApcLockFromDpcLevel(&ApcLock);
|
|
KiExitDispatcher(ApcLock.OldIrql);
|
|
return PreviousCount;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KeThawAllThreads(VOID)
|
|
{
|
|
KLOCK_QUEUE_HANDLE LockHandle, ApcLock;
|
|
PKTHREAD Current, CurrentThread = KeGetCurrentThread();
|
|
PKPROCESS Process = CurrentThread->ApcState.Process;
|
|
PLIST_ENTRY ListHead, NextEntry;
|
|
LONG OldCount;
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the process */
|
|
KiAcquireProcessLock(Process, &LockHandle);
|
|
|
|
/* Loop the Process's Threads */
|
|
ListHead = &Process->ThreadListHead;
|
|
NextEntry = ListHead->Flink;
|
|
do
|
|
{
|
|
/* Get the current thread */
|
|
Current = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
|
|
|
|
/* Lock it */
|
|
KiAcquireApcLockAtDpcLevel(Current, &ApcLock);
|
|
|
|
/* Make sure we are frozen */
|
|
OldCount = Current->FreezeCount;
|
|
if (OldCount)
|
|
{
|
|
/* Decrease the freeze count */
|
|
Current->FreezeCount--;
|
|
|
|
/* Check if both counts are zero now */
|
|
if (!(Current->SuspendCount) && (!Current->FreezeCount))
|
|
{
|
|
/* Lock the dispatcher */
|
|
KiAcquireDispatcherLockAtDpcLevel();
|
|
|
|
/* Signal the suspend semaphore and wake it */
|
|
Current->SuspendSemaphore.Header.SignalState++;
|
|
KiWaitTest(&Current->SuspendSemaphore, 0);
|
|
|
|
/* Unlock the dispatcher */
|
|
KiReleaseDispatcherLockFromDpcLevel();
|
|
}
|
|
}
|
|
|
|
/* Release the APC lock */
|
|
KiReleaseApcLockFromDpcLevel(&ApcLock);
|
|
|
|
/* Go to the next one */
|
|
NextEntry = NextEntry->Flink;
|
|
} while (NextEntry != ListHead);
|
|
|
|
/* Release the process lock and exit the dispatcher */
|
|
KiReleaseProcessLockFromDpcLevel(&LockHandle);
|
|
KiExitDispatcher(LockHandle.OldIrql);
|
|
|
|
/* Leave the critical region */
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
|
|
{
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
BOOLEAN OldState;
|
|
KLOCK_QUEUE_HANDLE ApcLock;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the Dispatcher Database and the APC Queue */
|
|
KiAcquireApcLock(Thread, &ApcLock);
|
|
|
|
/* Save the old State */
|
|
OldState = Thread->Alerted[AlertMode];
|
|
|
|
/* Check the Thread is alerted */
|
|
if (OldState)
|
|
{
|
|
/* Disable alert for this mode */
|
|
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 */
|
|
KiReleaseApcLock(&ApcLock);
|
|
return OldState;
|
|
}
|
|
|
|
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;
|
|
|
|
/* Initialize the Dispatcher Header */
|
|
Thread->Header.Type = ThreadObject;
|
|
Thread->Header.ThreadControlFlags = 0;
|
|
Thread->Header.DebugActive = FALSE;
|
|
Thread->Header.SignalState = 0;
|
|
InitializeListHead(&(Thread->Header.WaitListHead));
|
|
|
|
/* 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 = TRUE;
|
|
Thread->IdealProcessor = 1;
|
|
Thread->SwapBusy = FALSE;
|
|
Thread->KernelStackResident = TRUE;
|
|
Thread->AdjustReason = AdjustNone;
|
|
|
|
/* Initialize the lock */
|
|
KeInitializeSpinLock(&Thread->ThreadLock);
|
|
|
|
/* Setup the Service Descriptor Table for Native Calls */
|
|
Thread->ServiceTable = KeServiceDescriptorTable;
|
|
|
|
/* Setup APC Fields */
|
|
InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
|
|
InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
|
|
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,
|
|
KiSuspendNop,
|
|
KiSuspendRundown,
|
|
KiSuspendThread,
|
|
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 and process */
|
|
Thread->Teb = Teb;
|
|
Thread->Process = Process;
|
|
|
|
/* Check if we have a kernel stack */
|
|
if (!KernelStack)
|
|
{
|
|
/* We don't, allocate one */
|
|
KernelStack = MmCreateKernelStack(FALSE, 0);
|
|
if (!KernelStack) return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Remember for later */
|
|
AllocatedStack = TRUE;
|
|
}
|
|
|
|
/* Set the Thread Stacks */
|
|
Thread->InitialStack = KernelStack;
|
|
Thread->StackBase = KernelStack;
|
|
Thread->StackLimit = (ULONG_PTR)KernelStack - KERNEL_STACK_SIZE;
|
|
Thread->KernelStackResident = TRUE;
|
|
|
|
/* Enter SEH to avoid crashes due to user mode */
|
|
Status = STATUS_SUCCESS;
|
|
_SEH2_TRY
|
|
{
|
|
/* Initialize the Thread Context */
|
|
KiInitializeContextThread(Thread,
|
|
SystemRoutine,
|
|
StartRoutine,
|
|
StartContext,
|
|
Context);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Set failure status */
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
/* Check if a stack was allocated */
|
|
if (AllocatedStack)
|
|
{
|
|
/* Delete the stack */
|
|
MmDeleteKernelStack((PVOID)Thread->StackBase, FALSE);
|
|
Thread->InitialStack = NULL;
|
|
}
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the Thread to initialized */
|
|
Thread->State = Initialized;
|
|
return Status;
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* Initialize and start the thread on success */
|
|
if (NT_SUCCESS(KeInitThread(Thread,
|
|
KernelStack,
|
|
SystemRoutine,
|
|
StartRoutine,
|
|
StartContext,
|
|
Context,
|
|
Teb,
|
|
Process)))
|
|
{
|
|
/* Start it */
|
|
KeStartThread(Thread);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
KeUninitThread(IN PKTHREAD Thread)
|
|
{
|
|
/* Delete the stack */
|
|
MmDeleteKernelStack((PVOID)Thread->StackBase, FALSE);
|
|
Thread->InitialStack = NULL;
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
KeCapturePersistentThreadState(IN PVOID CurrentThread,
|
|
IN ULONG Setting1,
|
|
IN ULONG Setting2,
|
|
IN ULONG Setting3,
|
|
IN ULONG Setting4,
|
|
IN ULONG Setting5,
|
|
IN PVOID ThreadState)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
#undef KeGetCurrentThread
|
|
PKTHREAD
|
|
NTAPI
|
|
KeGetCurrentThread(VOID)
|
|
{
|
|
/* Return the current thread on this PCR */
|
|
return _KeGetCurrentThread();
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
#undef KeGetPreviousMode
|
|
UCHAR
|
|
NTAPI
|
|
KeGetPreviousMode(VOID)
|
|
{
|
|
/* Return the previous mode of this thread */
|
|
return _KeGetPreviousMode();
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
ULONG
|
|
NTAPI
|
|
KeQueryRuntimeThread(IN PKTHREAD Thread,
|
|
OUT PULONG UserTime)
|
|
{
|
|
ASSERT_THREAD(Thread);
|
|
|
|
/* Return the User Time */
|
|
*UserTime = Thread->UserTime;
|
|
|
|
/* Return the Kernel Time */
|
|
return Thread->KernelTime;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
KeSetKernelStackSwapEnable(IN BOOLEAN Enable)
|
|
{
|
|
BOOLEAN PreviousState;
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
|
|
/* Save Old State */
|
|
PreviousState = Thread->EnableStackSwap;
|
|
|
|
/* Set New State */
|
|
Thread->EnableStackSwap = Enable;
|
|
|
|
/* Return Old State */
|
|
return PreviousState;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
KPRIORITY
|
|
NTAPI
|
|
KeQueryPriorityThread(IN PKTHREAD Thread)
|
|
{
|
|
ASSERT_THREAD(Thread);
|
|
|
|
/* Return the current priority */
|
|
return Thread->Priority;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
KeRevertToUserAffinityThread(VOID)
|
|
{
|
|
KIRQL OldIrql;
|
|
PKPRCB Prcb;
|
|
PKTHREAD NextThread, CurrentThread = KeGetCurrentThread();
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
ASSERT(CurrentThread->SystemAffinityActive != FALSE);
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KiAcquireDispatcherLock();
|
|
|
|
/* Set the user affinity and processor and disable system affinity */
|
|
CurrentThread->Affinity = CurrentThread->UserAffinity;
|
|
CurrentThread->IdealProcessor = CurrentThread->UserIdealProcessor;
|
|
CurrentThread->SystemAffinityActive = FALSE;
|
|
|
|
/* Get the current PRCB and check if it doesn't match this affinity */
|
|
Prcb = KeGetCurrentPrcb();
|
|
if (!(Prcb->SetMember & CurrentThread->Affinity))
|
|
{
|
|
/* Lock the PRCB */
|
|
KiAcquirePrcbLock(Prcb);
|
|
|
|
/* Check if there's no next thread scheduled */
|
|
if (!Prcb->NextThread)
|
|
{
|
|
/* Select a new thread and set it on standby */
|
|
NextThread = KiSelectNextThread(Prcb);
|
|
NextThread->State = Standby;
|
|
Prcb->NextThread = NextThread;
|
|
}
|
|
|
|
/* Release the PRCB lock */
|
|
KiReleasePrcbLock(Prcb);
|
|
}
|
|
|
|
/* Unlock dispatcher database */
|
|
KiReleaseDispatcherLock(OldIrql);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
UCHAR
|
|
NTAPI
|
|
KeSetIdealProcessorThread(IN PKTHREAD Thread,
|
|
IN UCHAR Processor)
|
|
{
|
|
CCHAR OldIdealProcessor;
|
|
KIRQL OldIrql;
|
|
ASSERT(Processor <= MAXIMUM_PROCESSORS);
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KiAcquireDispatcherLock();
|
|
|
|
/* Save Old Ideal Processor */
|
|
OldIdealProcessor = Thread->UserIdealProcessor;
|
|
|
|
/* Make sure a valid CPU was given */
|
|
if (Processor < KeNumberProcessors)
|
|
{
|
|
/* Check if the user ideal CPU is in the affinity */
|
|
if (Thread->Affinity & AFFINITY_MASK(Processor))
|
|
{
|
|
/* Set the ideal processor */
|
|
Thread->IdealProcessor = Processor;
|
|
|
|
/* Check if system affinity is used */
|
|
if (!Thread->SystemAffinityActive)
|
|
{
|
|
/* It's not, so update the user CPU too */
|
|
Thread->UserIdealProcessor = Processor;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Release dispatcher lock and return the old ideal CPU */
|
|
KiReleaseDispatcherLock(OldIrql);
|
|
return OldIdealProcessor;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
KeSetSystemAffinityThread(IN KAFFINITY Affinity)
|
|
{
|
|
KIRQL OldIrql;
|
|
PKPRCB Prcb;
|
|
PKTHREAD NextThread, CurrentThread = KeGetCurrentThread();
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
ASSERT((Affinity & KeActiveProcessors) != 0);
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KiAcquireDispatcherLock();
|
|
|
|
/* Restore the affinity and enable system affinity */
|
|
CurrentThread->Affinity = Affinity;
|
|
CurrentThread->SystemAffinityActive = TRUE;
|
|
|
|
/* Check if the ideal processor is part of the affinity */
|
|
#ifdef CONFIG_SMP
|
|
if (!(Affinity & AFFINITY_MASK(CurrentThread->IdealProcessor)))
|
|
{
|
|
ULONG AffinitySet, NodeMask;
|
|
|
|
/* It's not! Get the PRCB */
|
|
Prcb = KiProcessorBlock[CurrentThread->IdealProcessor];
|
|
|
|
/* Calculate the affinity set */
|
|
AffinitySet = KeActiveProcessors & Affinity;
|
|
NodeMask = Prcb->ParentNode->ProcessorMask & AffinitySet;
|
|
if (NodeMask)
|
|
{
|
|
/* Use the Node set instead */
|
|
AffinitySet = NodeMask;
|
|
}
|
|
|
|
/* Calculate the ideal CPU from the affinity set */
|
|
BitScanReverse(&NodeMask, AffinitySet);
|
|
CurrentThread->IdealProcessor = (UCHAR)NodeMask;
|
|
}
|
|
#endif
|
|
|
|
/* Get the current PRCB and check if it doesn't match this affinity */
|
|
Prcb = KeGetCurrentPrcb();
|
|
if (!(Prcb->SetMember & CurrentThread->Affinity))
|
|
{
|
|
/* Lock the PRCB */
|
|
KiAcquirePrcbLock(Prcb);
|
|
|
|
/* Check if there's no next thread scheduled */
|
|
if (!Prcb->NextThread)
|
|
{
|
|
/* Select a new thread and set it on standby */
|
|
NextThread = KiSelectNextThread(Prcb);
|
|
NextThread->State = Standby;
|
|
Prcb->NextThread = NextThread;
|
|
}
|
|
|
|
/* Release the PRCB lock */
|
|
KiReleasePrcbLock(Prcb);
|
|
}
|
|
|
|
/* Unlock dispatcher database */
|
|
KiReleaseDispatcherLock(OldIrql);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LONG
|
|
NTAPI
|
|
KeSetBasePriorityThread(IN PKTHREAD Thread,
|
|
IN LONG Increment)
|
|
{
|
|
KIRQL OldIrql;
|
|
KPRIORITY OldBasePriority, Priority, BasePriority;
|
|
LONG OldIncrement;
|
|
PKPROCESS Process;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Get the process */
|
|
Process = Thread->ApcState.Process;
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KiAcquireDispatcherLock();
|
|
|
|
/* Lock the thread */
|
|
KiAcquireThreadLock(Thread);
|
|
|
|
/* Save the old base priority and increment */
|
|
OldBasePriority = Thread->BasePriority;
|
|
OldIncrement = OldBasePriority - Process->BasePriority;
|
|
|
|
/* If priority saturation happened, use the saturated increment */
|
|
if (Thread->Saturation) OldIncrement = (HIGH_PRIORITY + 1) / 2 *
|
|
Thread->Saturation;
|
|
|
|
/* Reset the saturation value */
|
|
Thread->Saturation = 0;
|
|
|
|
/* 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)
|
|
{
|
|
/* Set it to the lowest real time level */
|
|
BasePriority = LOW_REALTIME_PRIORITY;
|
|
}
|
|
|
|
/* Check if it's too high */
|
|
if (BasePriority > HIGH_PRIORITY) BasePriority = HIGH_PRIORITY;
|
|
|
|
/* We are at real time, so use the raw base priority */
|
|
Priority = BasePriority;
|
|
}
|
|
else
|
|
{
|
|
/* Check if it's entering the real time range */
|
|
if (BasePriority >= LOW_REALTIME_PRIORITY)
|
|
{
|
|
/* Set it to the highest dynamic level */
|
|
BasePriority = LOW_REALTIME_PRIORITY - 1;
|
|
}
|
|
|
|
/* Check if it's too low and normalize it */
|
|
if (BasePriority <= LOW_PRIORITY) BasePriority = 1;
|
|
|
|
/* Check if Saturation is used */
|
|
if (Thread->Saturation)
|
|
{
|
|
/* Use the raw base priority */
|
|
Priority = BasePriority;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, calculate the new priority */
|
|
Priority = KiComputeNewPriority(Thread, 0);
|
|
Priority += (BasePriority - OldBasePriority);
|
|
|
|
/* Check if it entered the real-time range */
|
|
if (Priority >= LOW_REALTIME_PRIORITY)
|
|
{
|
|
/* Normalize it down to the highest dynamic priority */
|
|
Priority = LOW_REALTIME_PRIORITY - 1;
|
|
}
|
|
else if (Priority <= LOW_PRIORITY)
|
|
{
|
|
/* It went too low, normalize it */
|
|
Priority = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Finally set the new base priority */
|
|
Thread->BasePriority = (SCHAR)BasePriority;
|
|
|
|
/* Reset the decrements */
|
|
Thread->PriorityDecrement = 0;
|
|
|
|
/* Check if we're changing priority after all */
|
|
if (Priority != Thread->Priority)
|
|
{
|
|
/* Reset the quantum and do the actual priority modification */
|
|
Thread->Quantum = Thread->QuantumReset;
|
|
KiSetPriorityThread(Thread, Priority);
|
|
}
|
|
|
|
/* Release thread lock */
|
|
KiReleaseThreadLock(Thread);
|
|
|
|
/* Release the dispatcher database and return old increment */
|
|
KiReleaseDispatcherLock(OldIrql);
|
|
return OldIncrement;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
KAFFINITY
|
|
NTAPI
|
|
KeSetAffinityThread(IN PKTHREAD Thread,
|
|
IN KAFFINITY Affinity)
|
|
{
|
|
KIRQL OldIrql;
|
|
KAFFINITY OldAffinity;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the dispatcher database */
|
|
OldIrql = KiAcquireDispatcherLock();
|
|
|
|
/* Call the internal function */
|
|
OldAffinity = KiSetAffinityThread(Thread, Affinity);
|
|
|
|
/* Release the dispatcher database and return old affinity */
|
|
KiReleaseDispatcherLock(OldIrql);
|
|
return OldAffinity;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
KPRIORITY
|
|
NTAPI
|
|
KeSetPriorityThread(IN PKTHREAD Thread,
|
|
IN KPRIORITY Priority)
|
|
{
|
|
KIRQL OldIrql;
|
|
KPRIORITY OldPriority;
|
|
ASSERT_THREAD(Thread);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
ASSERT((Priority <= HIGH_PRIORITY) && (Priority >= LOW_PRIORITY));
|
|
ASSERT(KeIsExecutingDpc() == FALSE);
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KiAcquireDispatcherLock();
|
|
|
|
/* Lock the thread */
|
|
KiAcquireThreadLock(Thread);
|
|
|
|
/* Save the old Priority and reset decrement */
|
|
OldPriority = Thread->Priority;
|
|
Thread->PriorityDecrement = 0;
|
|
|
|
/* Make sure that an actual change is being done */
|
|
if (Priority != Thread->Priority)
|
|
{
|
|
/* Reset the quantum */
|
|
Thread->Quantum = Thread->QuantumReset;
|
|
|
|
/* Check if priority is being set too low and normalize if so */
|
|
if ((Thread->BasePriority != 0) && !(Priority)) Priority = 1;
|
|
|
|
/* Set the new Priority */
|
|
KiSetPriorityThread(Thread, Priority);
|
|
}
|
|
|
|
/* Release thread lock */
|
|
KiReleaseThreadLock(Thread);
|
|
|
|
/* Release the dispatcher database */
|
|
KiReleaseDispatcherLock(OldIrql);
|
|
|
|
/* Return Old Priority */
|
|
return OldPriority;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
KeTerminateThread(IN KPRIORITY Increment)
|
|
{
|
|
PLIST_ENTRY *ListHead;
|
|
PETHREAD Entry, SavedEntry;
|
|
PETHREAD *ThreadAddr;
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
PKPROCESS Process = Thread->ApcState.Process;
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the process */
|
|
KiAcquireProcessLock(Process, &LockHandle);
|
|
|
|
/* Make sure we won't get Swapped */
|
|
KiSetThreadSwapBusy(Thread);
|
|
|
|
/* 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((PVOID*)ListHead,
|
|
ThreadAddr,
|
|
Entry);
|
|
|
|
/* Break out if the change was succesful */
|
|
} while (Entry != SavedEntry);
|
|
|
|
/* Acquire the dispatcher lock */
|
|
KiAcquireDispatcherLockAtDpcLevel();
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* Check the thread has an associated queue */
|
|
if (Thread->Queue)
|
|
{
|
|
/* Remove it from the list, and handle the queue */
|
|
RemoveEntryList(&Thread->QueueListEntry);
|
|
KiActivateWaiterQueue(Thread->Queue);
|
|
}
|
|
|
|
/* Signal the thread */
|
|
Thread->Header.SignalState = TRUE;
|
|
if (!IsListEmpty(&Thread->Header.WaitListHead))
|
|
{
|
|
/* Unwait the threads */
|
|
KxUnwaitThread(&Thread->Header, Increment);
|
|
}
|
|
|
|
/* Remove the thread from the list */
|
|
RemoveEntryList(&Thread->ThreadListEntry);
|
|
|
|
/* Release the process lock */
|
|
KiReleaseProcessLockFromDpcLevel(&LockHandle);
|
|
|
|
/* Set us as terminated, decrease the Process's stack count */
|
|
Thread->State = Terminated;
|
|
|
|
/* Decrease stack count */
|
|
ASSERT(Process->StackCount != 0);
|
|
ASSERT(Process->State == ProcessInMemory);
|
|
Process->StackCount--;
|
|
if (!(Process->StackCount) && !(IsListEmpty(&Process->ThreadListHead)))
|
|
{
|
|
/* FIXME: Swap stacks */
|
|
}
|
|
|
|
/* Rundown arch-specific parts */
|
|
KiRundownThread(Thread);
|
|
|
|
/* Swap to a new thread */
|
|
KiReleaseDispatcherLockFromDpcLevel();
|
|
KiSwapThread(Thread, KeGetCurrentPrcb());
|
|
}
|