-/*\r
- * PROJECT: ReactOS Kernel\r
- * LICENSE: GPL - See COPYING in the top level directory\r
- * FILE: ntoskrnl/ke/thrdobj.c\r
- * PURPOSE: Implements routines to manage the Kernel Thread Object\r
- * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)\r
- */\r
-\r
-/* INCLUDES ******************************************************************/\r
-\r
-#include <ntoskrnl.h>\r
-#define NDEBUG\r
-#include <internal/debug.h>\r
-\r
-extern EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];\r
-extern LIST_ENTRY PspReaperListHead;\r
-\r
-ULONG KiMask32Array[MAXIMUM_PRIORITY] =\r
-{\r
- 0x1, 0x2, 0x4, 0x8, 0x10, 0x20,\r
- 0x40, 0x80, 0x100, 0x200, 0x4000, 0x800,\r
- 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000,\r
- 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000,\r
- 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000, 0x20000000,\r
- 0x40000000, 0x80000000\r
-};\r
-\r
-/* FUNCTIONS *****************************************************************/\r
-\r
-UCHAR\r
-NTAPI\r
-KeFindNextRightSetAffinity(IN UCHAR Number,\r
- IN ULONG Set)\r
-{\r
- ULONG Bit, Result;\r
- ASSERT(Set != 0);\r
-\r
- /* Calculate the mask */\r
- Bit = (AFFINITY_MASK(Number) - 1) & Set;\r
-\r
- /* If it's 0, use the one we got */\r
- if (!Bit) Bit = Set;\r
-\r
- /* Now find the right set and return it */\r
- BitScanReverse(&Result, Bit);\r
- return (UCHAR)Result;\r
-}\r
-\r
-KPRIORITY\r
-NTAPI\r
-KeQueryBasePriorityThread(IN PKTHREAD Thread)\r
-{\r
- LONG BaseIncrement;\r
- KIRQL OldIrql;\r
- PKPROCESS Process;\r
- ASSERT_THREAD(Thread);\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Raise IRQL to synch level */\r
- OldIrql = KeRaiseIrqlToSynchLevel();\r
-\r
- /* Lock the thread */\r
- KiAcquireThreadLock(Thread);\r
-\r
- /* Get the Process */\r
- Process = Thread->ApcStatePointer[0]->Process;\r
-\r
- /* Calculate the base increment */\r
- BaseIncrement = Thread->BasePriority - Process->BasePriority;\r
-\r
- /* If saturation occured, return the saturation increment instead */\r
- if (Thread->Saturation) BaseIncrement = (HIGH_PRIORITY + 1) / 2 *\r
- Thread->Saturation;\r
-\r
- /* Release thread lock */\r
- KiReleaseThreadLock(Thread);\r
-\r
- /* Lower IRQl and return Increment */\r
- KeLowerIrql(OldIrql);\r
- return BaseIncrement;\r
-}\r
-\r
-ULONG\r
-NTAPI\r
-KeAlertResumeThread(IN PKTHREAD Thread)\r
-{\r
- ULONG PreviousCount;\r
- KLOCK_QUEUE_HANDLE ApcLock;\r
- ASSERT_THREAD(Thread);\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Lock the Dispatcher Database and the APC Queue */\r
- KiAcquireApcLock(Thread, &ApcLock);\r
- KiAcquireDispatcherLockAtDpcLevel();\r
-\r
- /* Return if Thread is already alerted. */\r
- if (!Thread->Alerted[KernelMode])\r
- {\r
- /* If it's Blocked, unblock if it we should */\r
- if ((Thread->State == Waiting) && (Thread->Alertable))\r
- {\r
- /* Abort the wait */\r
- KiUnwaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);\r
- }\r
- else\r
- {\r
- /* If not, simply Alert it */\r
- Thread->Alerted[KernelMode] = TRUE;\r
- }\r
- }\r
-\r
- /* Save the old Suspend Count */\r
- PreviousCount = Thread->SuspendCount;\r
-\r
- /* If the thread is suspended, decrease one of the suspend counts */\r
- if (PreviousCount)\r
- {\r
- /* Decrease count. If we are now zero, unwait it completely */\r
- if (--Thread->SuspendCount)\r
- {\r
- /* Signal and satisfy */\r
- Thread->SuspendSemaphore.Header.SignalState++;\r
- KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);\r
- }\r
- }\r
-\r
- /* Release Locks and return the Old State */\r
- KiReleaseDispatcherLockFromDpcLevel();\r
- KiReleaseApcLockFromDpcLevel(&ApcLock);\r
- KiExitDispatcher(ApcLock.OldIrql);\r
- return PreviousCount;\r
-}\r
-\r
-BOOLEAN\r
-NTAPI\r
-KeAlertThread(IN PKTHREAD Thread,\r
- IN KPROCESSOR_MODE AlertMode)\r
-{\r
- BOOLEAN PreviousState;\r
- KLOCK_QUEUE_HANDLE ApcLock;\r
- ASSERT_THREAD(Thread);\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Lock the Dispatcher Database and the APC Queue */\r
- KiAcquireApcLock(Thread, &ApcLock);\r
- KiAcquireDispatcherLockAtDpcLevel();\r
-\r
- /* Save the Previous State */\r
- PreviousState = Thread->Alerted[AlertMode];\r
-\r
- /* Check if it's already alerted */\r
- if (!PreviousState)\r
- {\r
- /* Check if the thread is alertable, and blocked in the given mode */\r
- if ((Thread->State == Waiting) &&\r
- ((AlertMode == KernelMode) || (Thread->WaitMode == AlertMode)) &&\r
- (Thread->Alertable))\r
- {\r
- /* Abort the wait to alert the thread */\r
- KiUnwaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);\r
- }\r
- else\r
- {\r
- /* Otherwise, merely set the alerted state */\r
- Thread->Alerted[AlertMode] = TRUE;\r
- }\r
- }\r
-\r
- /* Release the Dispatcher Lock */\r
- KiReleaseDispatcherLockFromDpcLevel();\r
- KiReleaseApcLockFromDpcLevel(&ApcLock);\r
- KiExitDispatcher(ApcLock.OldIrql);\r
-\r
- /* Return the old state */\r
- return PreviousState;\r
-}\r
-\r
-ULONG\r
-NTAPI\r
-KeForceResumeThread(IN PKTHREAD Thread)\r
-{\r
- KLOCK_QUEUE_HANDLE ApcLock;\r
- ULONG PreviousCount;\r
- ASSERT_THREAD(Thread);\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Lock the APC Queue */\r
- KiAcquireApcLock(Thread, &ApcLock);\r
-\r
- /* Save the old Suspend Count */\r
- PreviousCount = Thread->SuspendCount + Thread->FreezeCount;\r
-\r
- /* If the thread is suspended, wake it up!!! */\r
- if (PreviousCount)\r
- {\r
- /* Unwait it completely */\r
- Thread->SuspendCount = 0;\r
- Thread->FreezeCount = 0;\r
-\r
- /* Lock the dispatcher */\r
- KiAcquireDispatcherLockAtDpcLevel();\r
-\r
- /* Signal and satisfy */\r
- Thread->SuspendSemaphore.Header.SignalState++;\r
- KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);\r
-\r
- /* Release the dispatcher */\r
- KiReleaseDispatcherLockFromDpcLevel();\r
- }\r
-\r
- /* Release Lock and return the Old State */\r
- KiReleaseApcLockFromDpcLevel(&ApcLock);\r
- KiExitDispatcher(ApcLock.OldIrql);\r
- return PreviousCount;\r
-}\r
-\r
-/*\r
- * Used by the debugging code to freeze all the process's threads\r
- * while the debugger is examining their state.\r
- */\r
-VOID\r
-NTAPI\r
-KeFreezeAllThreads(IN PKPROCESS Process)\r
-{\r
- KLOCK_QUEUE_HANDLE LockHandle, ApcLock;\r
- PKTHREAD Current, CurrentThread = KeGetCurrentThread();\r
- PLIST_ENTRY ListHead, NextEntry;\r
- LONG OldCount;\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Lock the process */\r
- KiAcquireProcessLock(Process, &LockHandle);\r
-\r
- /* If someone is already trying to free us, try again */\r
- while (CurrentThread->FreezeCount)\r
- {\r
- /* Release and re-acquire the process lock so the APC will go through */\r
- KiReleaseProcessLock(&LockHandle);\r
- KiAcquireProcessLock(Process, &LockHandle);\r
- }\r
-\r
- /* Enter a critical region */\r
- KeEnterCriticalRegion();\r
-\r
- /* Loop the Process's Threads */\r
- ListHead = &Process->ThreadListHead;\r
- NextEntry = ListHead->Flink;\r
- while (NextEntry != ListHead)\r
- {\r
- /* Get the current thread */\r
- Current = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);\r
-\r
- /* Lock it */\r
- KiAcquireApcLockAtDpcLevel(Current, &ApcLock);\r
-\r
- /* Make sure it's not ours, and check if APCs are enabled */\r
- if ((Current != CurrentThread) && (Current->ApcQueueable))\r
- {\r
- /* Sanity check */\r
- OldCount = Current->SuspendCount;\r
- ASSERT(OldCount != MAXIMUM_SUSPEND_COUNT);\r
-\r
- /* Increase the freeze count */\r
- Current->FreezeCount++;\r
-\r
- /* Make sure it wasn't already suspended */\r
- if (!(OldCount) && !(Current->SuspendCount))\r
- {\r
- /* Did we already insert it? */\r
- if (!Current->SuspendApc.Inserted)\r
- {\r
- /* Insert the APC */\r
- Current->SuspendApc.Inserted = TRUE;\r
- KiInsertQueueApc(&Current->SuspendApc, IO_NO_INCREMENT);\r
- }\r
- else\r
- {\r
- /* Lock the dispatcher */\r
- KiAcquireDispatcherLockAtDpcLevel();\r
-\r
- /* Unsignal the semaphore, the APC was already inserted */\r
- Current->SuspendSemaphore.Header.SignalState--;\r
-\r
- /* Release the dispatcher */\r
- KiReleaseDispatcherLockFromDpcLevel();\r
- }\r
- }\r
- }\r
-\r
- /* Release the APC lock */\r
- KiReleaseApcLockFromDpcLevel(&ApcLock);\r
- }\r
-\r
- /* Release the process lock and exit the dispatcher */\r
- KiReleaseProcessLock(&LockHandle);\r
- KiExitDispatcher(LockHandle.OldIrql);\r
-}\r
-\r
-ULONG\r
-NTAPI\r
-KeResumeThread(IN PKTHREAD Thread)\r
-{\r
- KLOCK_QUEUE_HANDLE ApcLock;\r
- ULONG PreviousCount;\r
- ASSERT_THREAD(Thread);\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Lock the APC Queue */\r
- KiAcquireApcLock(Thread, &ApcLock);\r
-\r
- /* Save the Old Count */\r
- PreviousCount = Thread->SuspendCount;\r
-\r
- /* Check if it existed */\r
- if (PreviousCount)\r
- {\r
- /* Decrease the suspend count */\r
- Thread->SuspendCount--;\r
-\r
- /* Check if the thrad is still suspended or not */\r
- if ((!Thread->SuspendCount) && (!Thread->FreezeCount))\r
- {\r
- /* Acquire the dispatcher lock */\r
- KiAcquireDispatcherLockAtDpcLevel();\r
-\r
- /* Signal the Suspend Semaphore */\r
- Thread->SuspendSemaphore.Header.SignalState++;\r
- KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);\r
-\r
- /* Release the dispatcher lock */\r
- KiReleaseDispatcherLockFromDpcLevel();\r
- }\r
- }\r
-\r
- /* Release APC Queue lock and return the Old State */\r
- KiReleaseApcLockFromDpcLevel(&ApcLock);\r
- KiExitDispatcher(ApcLock.OldIrql);\r
- return PreviousCount;\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KeRundownThread(VOID)\r
-{\r
- KIRQL OldIrql;\r
- PKTHREAD Thread = KeGetCurrentThread();\r
- PLIST_ENTRY NextEntry, ListHead;\r
- PKMUTANT Mutant;\r
-\r
- /* Optimized path if nothing is on the list at the moment */\r
- if (IsListEmpty(&Thread->MutantListHead)) return;\r
-\r
- /* Lock the Dispatcher Database */\r
- OldIrql = KiAcquireDispatcherLock();\r
-\r
- /* Get the List Pointers */\r
- ListHead = &Thread->MutantListHead;\r
- NextEntry = ListHead->Flink;\r
- while (NextEntry != ListHead)\r
- {\r
- /* Get the Mutant */\r
- Mutant = CONTAINING_RECORD(NextEntry, KMUTANT, MutantListEntry);\r
-\r
- /* Make sure it's not terminating with APCs off */\r
- if (Mutant->ApcDisable)\r
- {\r
- /* Bugcheck the system */\r
- KEBUGCHECKEX(0, //THREAD_TERMINATE_HELD_MUTEX,\r
- (ULONG_PTR)Thread,\r
- (ULONG_PTR)Mutant,\r
- 0,\r
- 0);\r
- }\r
-\r
- /* Now we can remove it */\r
- RemoveEntryList(&Mutant->MutantListEntry);\r
-\r
- /* Unconditionally abandon it */\r
- Mutant->Header.SignalState = 1;\r
- Mutant->Abandoned = TRUE;\r
- Mutant->OwnerThread = NULL;\r
-\r
- /* Check if the Wait List isn't empty */\r
- if (!IsListEmpty(&Mutant->Header.WaitListHead))\r
- {\r
- /* Wake the Mutant */\r
- KiWaitTest(&Mutant->Header, MUTANT_INCREMENT);\r
- }\r
-\r
- /* Move on */\r
- NextEntry = NextEntry->Flink;\r
- }\r
-\r
- /* Release the Lock */\r
- KiReleaseDispatcherLock(OldIrql);\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KeStartThread(IN OUT PKTHREAD Thread)\r
-{\r
- KLOCK_QUEUE_HANDLE LockHandle;\r
-#ifdef CONFIG_SMP\r
- PKNODE Node;\r
- PKPRCB NodePrcb;\r
- ULONG Set, Mask;\r
-#endif\r
- UCHAR IdealProcessor = 0;\r
- PKPROCESS Process = Thread->ApcState.Process;\r
-\r
- /* Setup static fields from parent */\r
- Thread->Iopl = Process->Iopl;\r
- Thread->Quantum = Process->QuantumReset;\r
- Thread->QuantumReset = Process->QuantumReset;\r
- Thread->SystemAffinityActive = FALSE;\r
-\r
- /* Lock the process */\r
- KiAcquireProcessLock(Process, &LockHandle);\r
-\r
- /* Setup volatile data */\r
- Thread->Priority = Process->BasePriority;\r
- Thread->BasePriority = Process->BasePriority;\r
- Thread->Affinity = Process->Affinity;\r
- Thread->UserAffinity = Process->Affinity;\r
-\r
-#ifdef CONFIG_SMP\r
- /* Get the KNODE and its PRCB */\r
- Node = KeNodeBlock[Process->IdealNode];\r
- NodePrcb = (PKPRCB)(KPCR_BASE + (Process->ThreadSeed * PAGE_SIZE));\r
-\r
- /* Calculate affinity mask */\r
- Set = ~NodePrcb->MultiThreadProcessorSet;\r
- Mask = (ULONG)(Node->ProcessorMask & Process->Affinity);\r
- Set &= Mask;\r
- if (Set) Mask = Set;\r
-\r
- /* Get the new thread seed */\r
- IdealProcessor = KeFindNextRightSetAffinity(Process->ThreadSeed, Mask);\r
- Process->ThreadSeed = IdealProcessor;\r
-\r
- /* Sanity check */\r
- ASSERT((Thread->UserAffinity & AFFINITY_MASK(IdealProcessor)));\r
-#endif\r
-\r
- /* Set the Ideal Processor */\r
- Thread->IdealProcessor = IdealProcessor;\r
- Thread->UserIdealProcessor = IdealProcessor;\r
-\r
- /* Lock the Dispatcher Database */\r
- KiAcquireDispatcherLockAtDpcLevel();\r
-\r
- /* Insert the thread into the process list */\r
- InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);\r
-\r
- /* Increase the stack count */\r
- ASSERT(Process->StackCount != MAXULONG_PTR);\r
- Process->StackCount++;\r
-\r
- /* Release locks and return */\r
- KiReleaseDispatcherLockFromDpcLevel();\r
- KiReleaseProcessLock(&LockHandle);\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KiSuspendRundown(IN PKAPC Apc)\r
-{\r
- /* Does nothing */\r
- UNREFERENCED_PARAMETER(Apc);\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KiSuspendNop(IN PKAPC Apc,\r
- IN PKNORMAL_ROUTINE *NormalRoutine,\r
- IN PVOID *NormalContext,\r
- IN PVOID *SystemArgument1,\r
- IN PVOID *SystemArgument2)\r
-{\r
- /* Does nothing */\r
- UNREFERENCED_PARAMETER(Apc);\r
- UNREFERENCED_PARAMETER(NormalRoutine);\r
- UNREFERENCED_PARAMETER(NormalContext);\r
- UNREFERENCED_PARAMETER(SystemArgument1);\r
- UNREFERENCED_PARAMETER(SystemArgument2);\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KiSuspendThread(IN PVOID NormalContext,\r
- IN PVOID SystemArgument1,\r
- IN PVOID SystemArgument2)\r
-{\r
- /* Non-alertable kernel-mode suspended wait */\r
- KeWaitForSingleObject(&KeGetCurrentThread()->SuspendSemaphore,\r
- Suspended,\r
- KernelMode,\r
- FALSE,\r
- NULL);\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-KeSuspendThread(PKTHREAD Thread)\r
-{\r
- KLOCK_QUEUE_HANDLE ApcLock;\r
- ULONG PreviousCount;\r
- ASSERT_THREAD(Thread);\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Lock the APC Queue */\r
- KiAcquireApcLock(Thread, &ApcLock);\r
-\r
- /* Save the Old Count */\r
- PreviousCount = Thread->SuspendCount;\r
-\r
- /* Handle the maximum */\r
- if (PreviousCount == MAXIMUM_SUSPEND_COUNT)\r
- {\r
- /* Raise an exception */\r
- KiReleaseApcLock(&ApcLock);\r
- RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED);\r
- }\r
-\r
- /* Should we bother to queue at all? */\r
- if (Thread->ApcQueueable)\r
- {\r
- /* Increment the suspend count */\r
- Thread->SuspendCount++;\r
-\r
- /* Check if we should suspend it */\r
- if (!(PreviousCount) && !(Thread->FreezeCount))\r
- {\r
- /* Is the APC already inserted? */\r
- if (!Thread->SuspendApc.Inserted)\r
- {\r
- /* Not inserted, insert it */\r
- Thread->SuspendApc.Inserted = TRUE;\r
- KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT);\r
- }\r
- else\r
- {\r
- /* Lock the dispatcher */\r
- KiAcquireDispatcherLockAtDpcLevel();\r
-\r
- /* Unsignal the semaphore, the APC was already inserted */\r
- Thread->SuspendSemaphore.Header.SignalState--;\r
-\r
- /* Release the dispatcher */\r
- KiReleaseDispatcherLockFromDpcLevel();\r
- }\r
- }\r
- }\r
-\r
- /* Release Lock and return the Old State */\r
- KiReleaseApcLockFromDpcLevel(&ApcLock);\r
- KiExitDispatcher(ApcLock.OldIrql);\r
- return PreviousCount;\r
-}\r
-\r
-BOOLEAN\r
-NTAPI\r
-KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)\r
-{\r
- PKTHREAD Thread = KeGetCurrentThread();\r
- BOOLEAN OldState;\r
- KLOCK_QUEUE_HANDLE ApcLock;\r
- ASSERT_THREAD(Thread);\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Lock the Dispatcher Database and the APC Queue */\r
- KiAcquireApcLock(Thread, &ApcLock);\r
- KiAcquireDispatcherLockAtDpcLevel();\r
-\r
- /* Save the old State */\r
- OldState = Thread->Alerted[AlertMode];\r
-\r
- /* Check the Thread is alerted */\r
- if (OldState)\r
- {\r
- /* Disable alert for this mode */\r
- Thread->Alerted[AlertMode] = FALSE;\r
- }\r
- else if ((AlertMode != KernelMode) &&\r
- (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))\r
- {\r
- /* If the mode is User and the Queue isn't empty, set Pending */\r
- Thread->ApcState.UserApcPending = TRUE;\r
- }\r
-\r
- /* Release Locks and return the Old State */\r
- KiReleaseDispatcherLockFromDpcLevel();\r
- KiReleaseApcLockFromDpcLevel(&ApcLock);\r
- KiExitDispatcher(ApcLock.OldIrql);\r
- return OldState;\r
-}\r
-\r
-NTSTATUS\r
-NTAPI\r
-KeInitThread(IN OUT PKTHREAD Thread,\r
- IN PVOID KernelStack,\r
- IN PKSYSTEM_ROUTINE SystemRoutine,\r
- IN PKSTART_ROUTINE StartRoutine,\r
- IN PVOID StartContext,\r
- IN PCONTEXT Context,\r
- IN PVOID Teb,\r
- IN PKPROCESS Process)\r
-{\r
- BOOLEAN AllocatedStack = FALSE;\r
- ULONG i;\r
- PKWAIT_BLOCK TimerWaitBlock;\r
- PKTIMER Timer;\r
- NTSTATUS Status;\r
-\r
- /* Initalize the Dispatcher Header */\r
- KeInitializeDispatcherHeader(&Thread->DispatcherHeader,\r
- ThreadObject,\r
- sizeof(KTHREAD) / sizeof(LONG),\r
- FALSE);\r
-\r
- /* Initialize the Mutant List */\r
- InitializeListHead(&Thread->MutantListHead);\r
-\r
- /* Initialize the wait blocks */\r
- for (i = 0; i< (THREAD_WAIT_OBJECTS + 1); i++)\r
- {\r
- /* Put our pointer */\r
- Thread->WaitBlock[i].Thread = Thread;\r
- }\r
-\r
- /* Set swap settings */\r
- Thread->EnableStackSwap = FALSE;//TRUE;\r
- Thread->IdealProcessor = 1;\r
- Thread->SwapBusy = FALSE;\r
- Thread->AdjustReason = 0;\r
-\r
- /* Initialize the lock */\r
- KeInitializeSpinLock(&Thread->ThreadLock);\r
-\r
- /* Setup the Service Descriptor Table for Native Calls */\r
- Thread->ServiceTable = KeServiceDescriptorTable;\r
-\r
- /* Setup APC Fields */\r
- InitializeListHead(&Thread->ApcState.ApcListHead[0]);\r
- InitializeListHead(&Thread->ApcState.ApcListHead[1]);\r
- Thread->ApcState.Process = Process;\r
- Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;\r
- Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;\r
- Thread->ApcStateIndex = OriginalApcEnvironment;\r
- Thread->ApcQueueable = TRUE;\r
- KeInitializeSpinLock(&Thread->ApcQueueLock);\r
-\r
- /* Initialize the Suspend APC */\r
- KeInitializeApc(&Thread->SuspendApc,\r
- Thread,\r
- OriginalApcEnvironment,\r
- KiSuspendNop,\r
- KiSuspendRundown,\r
- KiSuspendThread,\r
- KernelMode,\r
- NULL);\r
-\r
- /* Initialize the Suspend Semaphore */\r
- KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 2);\r
-\r
- /* Setup the timer */\r
- Timer = &Thread->Timer;\r
- KeInitializeTimer(Timer);\r
- TimerWaitBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];\r
- TimerWaitBlock->Object = Timer;\r
- TimerWaitBlock->WaitKey = STATUS_TIMEOUT;\r
- TimerWaitBlock->WaitType = WaitAny;\r
- TimerWaitBlock->NextWaitBlock = NULL;\r
-\r
- /* Link the two wait lists together */\r
- TimerWaitBlock->WaitListEntry.Flink = &Timer->Header.WaitListHead;\r
- TimerWaitBlock->WaitListEntry.Blink = &Timer->Header.WaitListHead;\r
-\r
- /* Set the TEB */\r
- Thread->Teb = Teb;\r
-\r
- /* Check if we have a kernel stack */\r
- if (!KernelStack)\r
- {\r
- /* We don't, allocate one */\r
- KernelStack = (PVOID)((ULONG_PTR)MmCreateKernelStack(FALSE) +\r
- KERNEL_STACK_SIZE);\r
- if (!KernelStack) return STATUS_INSUFFICIENT_RESOURCES;\r
-\r
- /* Remember for later */\r
- AllocatedStack = TRUE;\r
- }\r
-\r
- /* Set the Thread Stacks */\r
- Thread->InitialStack = (PCHAR)KernelStack;\r
- Thread->StackBase = (PCHAR)KernelStack;\r
- Thread->StackLimit = (ULONG_PTR)KernelStack - KERNEL_STACK_SIZE;\r
- Thread->KernelStackResident = TRUE;\r
-\r
- /* ROS Mm HACK */\r
- MmUpdatePageDir((PEPROCESS)Process,\r
- (PVOID)Thread->StackLimit,\r
- KERNEL_STACK_SIZE);\r
- MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));\r
-\r
- /* Enter SEH to avoid crashes due to user mode */\r
- Status = STATUS_SUCCESS;\r
- _SEH_TRY\r
- {\r
- /* Initalize the Thread Context */\r
- Ke386InitThreadWithContext(Thread,\r
- SystemRoutine,\r
- StartRoutine,\r
- StartContext,\r
- Context);\r
- }\r
- _SEH_HANDLE\r
- {\r
- /* Set failure status */\r
- Status = STATUS_UNSUCCESSFUL;\r
-\r
- /* Check if a stack was allocated */\r
- if (AllocatedStack)\r
- {\r
- /* Delete the stack */\r
- MmDeleteKernelStack(Thread->StackBase, FALSE);\r
- Thread->InitialStack = NULL;\r
- }\r
- }\r
- _SEH_END;\r
-\r
- /* Set the Thread to initalized */\r
- Thread->State = Initialized;\r
- return Status;\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KeInitializeThread(IN PKPROCESS Process,\r
- IN OUT PKTHREAD Thread,\r
- IN PKSYSTEM_ROUTINE SystemRoutine,\r
- IN PKSTART_ROUTINE StartRoutine,\r
- IN PVOID StartContext,\r
- IN PCONTEXT Context,\r
- IN PVOID Teb,\r
- IN PVOID KernelStack)\r
-{\r
- /* Initailize and start the thread on success */\r
- if (NT_SUCCESS(KeInitThread(Thread,\r
- KernelStack,\r
- SystemRoutine,\r
- StartRoutine,\r
- StartContext,\r
- Context,\r
- Teb,\r
- Process)))\r
- {\r
- /* Start it */\r
- KeStartThread(Thread);\r
- }\r
-}\r
-\r
-VOID\r
-NTAPI\r
-KeUninitThread(IN PKTHREAD Thread)\r
-{\r
- /* Delete the stack */\r
- MmDeleteKernelStack(Thread->StackBase, FALSE);\r
- Thread->InitialStack = NULL;\r
-}\r
-\r
-/* PUBLIC FUNCTIONS **********************************************************/\r
-\r
-/*\r
- * @unimplemented\r
- */\r
-VOID\r
-NTAPI\r
-KeCapturePersistentThreadState(IN PVOID CurrentThread,\r
- IN ULONG Setting1,\r
- IN ULONG Setting2,\r
- IN ULONG Setting3,\r
- IN ULONG Setting4,\r
- IN ULONG Setting5,\r
- IN PVOID ThreadState)\r
-{\r
- UNIMPLEMENTED;\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-#undef KeGetCurrentThread\r
-PKTHREAD\r
-NTAPI\r
-KeGetCurrentThread(VOID)\r
-{\r
- /* Return the current thread on this PCR */\r
- return ((PKIPCR)KeGetPcr())->PrcbData.CurrentThread;\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-KPROCESSOR_MODE\r
-NTAPI\r
-KeGetPreviousMode(VOID)\r
-{\r
- /* Return the previous mode of this thread */\r
- return KeGetCurrentThread()->PreviousMode;\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-ULONG\r
-NTAPI\r
-KeQueryRuntimeThread(IN PKTHREAD Thread,\r
- OUT PULONG UserTime)\r
-{\r
- ASSERT_THREAD(Thread);\r
-\r
- /* Return the User Time */\r
- *UserTime = Thread->UserTime;\r
-\r
- /* Return the Kernel Time */\r
- return Thread->KernelTime;\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-BOOLEAN\r
-NTAPI\r
-KeSetKernelStackSwapEnable(IN BOOLEAN Enable)\r
-{\r
- BOOLEAN PreviousState;\r
- PKTHREAD Thread = KeGetCurrentThread();\r
-\r
- /* Save Old State */\r
- PreviousState = Thread->EnableStackSwap;\r
-\r
- /* Set New State */\r
- Thread->EnableStackSwap = Enable;\r
-\r
- /* Return Old State */\r
- return PreviousState;\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-KPRIORITY\r
-NTAPI\r
-KeQueryPriorityThread(IN PKTHREAD Thread)\r
-{\r
- ASSERT_THREAD(Thread);\r
-\r
- /* Return the current priority */\r
- return Thread->Priority;\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-VOID\r
-NTAPI\r
-KeRevertToUserAffinityThread(VOID)\r
-{\r
- KIRQL OldIrql;\r
- PKPRCB Prcb;\r
- PKTHREAD NextThread, CurrentThread = KeGetCurrentThread();\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
- ASSERT(CurrentThread->SystemAffinityActive != FALSE);\r
-\r
- /* Lock the Dispatcher Database */\r
- OldIrql = KiAcquireDispatcherLock();\r
-\r
- /* Set the user affinity and processor and disable system affinity */\r
- CurrentThread->Affinity = CurrentThread->UserAffinity;\r
- CurrentThread->IdealProcessor = CurrentThread->UserIdealProcessor;\r
- CurrentThread->SystemAffinityActive = FALSE;\r
-\r
- /* Get the current PRCB and check if it doesn't match this affinity */\r
- Prcb = KeGetCurrentPrcb();\r
- if (!(Prcb->SetMember & CurrentThread->Affinity))\r
- {\r
- /* Lock the PRCB */\r
- KiAcquirePrcbLock(Prcb);\r
-\r
-#ifdef NEW_SCHEDULER\r
- /* Check if there's no next thread scheduled */\r
- if (!Prcb->NextThread)\r
- {\r
- /* Select a new thread and set it on standby */\r
- NextThread = KiSelectNextThread(Prcb);\r
- NextThread->State = Standby;\r
- Prcb->NextThread = NextThread;\r
- }\r
-#else\r
- /* We need to dispatch a new thread */\r
- NextThread = NULL;\r
- CurrentThread->WaitIrql = OldIrql;\r
- KiDispatchThreadNoLock(Ready);\r
- KeLowerIrql(OldIrql);\r
- return;\r
-#endif\r
-\r
- /* Release the PRCB lock */\r
- KiReleasePrcbLock(Prcb);\r
- }\r
-\r
- /* Unlock dispatcher database */\r
- KiReleaseDispatcherLock(OldIrql);\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-UCHAR\r
-NTAPI\r
-KeSetIdealProcessorThread(IN PKTHREAD Thread,\r
- IN UCHAR Processor)\r
-{\r
- CCHAR OldIdealProcessor;\r
- KIRQL OldIrql;\r
- ASSERT(Processor <= MAXIMUM_PROCESSORS);\r
-\r
- /* Lock the Dispatcher Database */\r
- OldIrql = KiAcquireDispatcherLock();\r
-\r
- /* Save Old Ideal Processor */\r
- OldIdealProcessor = Thread->UserIdealProcessor;\r
-\r
- /* Make sure a valid CPU was given */\r
- if (Processor < MAXIMUM_PROCESSORS)\r
- {\r
- /* Check if the user ideal CPU is in the affinity */\r
- if (Thread->UserIdealProcessor & AFFINITY_MASK(Processor))\r
- {\r
- /* Set the ideal processor */\r
- Thread->IdealProcessor = Processor;\r
-\r
- /* Check if system affinity is used */\r
- if (!Thread->SystemAffinityActive)\r
- {\r
- /* It's not, so update the user CPU too */\r
- Thread->UserIdealProcessor = Processor;\r
- }\r
- }\r
- }\r
-\r
- /* Release dispatcher lock and return the old ideal CPU */\r
- KiReleaseDispatcherLock(OldIrql);\r
- return OldIdealProcessor;\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-VOID\r
-NTAPI\r
-KeSetSystemAffinityThread(IN KAFFINITY Affinity)\r
-{\r
- KIRQL OldIrql;\r
- PKPRCB Prcb;\r
- PKTHREAD NextThread, CurrentThread = KeGetCurrentThread();\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
- ASSERT((Affinity & KeActiveProcessors) != 0);\r
-\r
- /* Lock the Dispatcher Database */\r
- OldIrql = KiAcquireDispatcherLock();\r
-\r
- /* Restore the affinity and enable system affinity */\r
- CurrentThread->Affinity = Affinity;\r
- CurrentThread->SystemAffinityActive = TRUE;\r
-\r
- /* Check if the ideal processor is part of the affinity */\r
-#ifdef CONFIG_SMP\r
- if (!(Affinity & AFFINITY_MASK(CurrentThread->IdealProcessor)))\r
- {\r
- ULONG AffinitySet, NodeMask;\r
-\r
- /* It's not! Get the PRCB */\r
- Prcb = KiProcessorBlock[CurrentThread->IdealProcessor];\r
-\r
- /* Calculate the affinity set */\r
- AffinitySet = KeActiveProcessors & Affinity;\r
- NodeMask = Prcb->ParentNode->ProcessorMask & AffinitySet;\r
- if (NodeMask)\r
- {\r
- /* Use the Node set instead */\r
- AffinitySet = NodeMask;\r
- }\r
-\r
- /* Calculate the ideal CPU from the affinity set */\r
- BitScanReverse(&NodeMask, AffinitySet);\r
- CurrentThread->IdealProcessor = (UCHAR)NodeMask;\r
- }\r
-#endif\r
-\r
- /* Get the current PRCB and check if it doesn't match this affinity */\r
- Prcb = KeGetCurrentPrcb();\r
- if (!(Prcb->SetMember & CurrentThread->Affinity))\r
- {\r
- /* Lock the PRCB */\r
- KiAcquirePrcbLock(Prcb);\r
-\r
-#ifdef NEW_SCHEDULER\r
- /* Check if there's no next thread scheduled */\r
- if (!Prcb->NextThread)\r
- {\r
- /* Select a new thread and set it on standby */\r
- NextThread = KiSelectNextThread(Prcb);\r
- NextThread->State = Standby;\r
- Prcb->NextThread = NextThread;\r
- }\r
-#else\r
- /* We need to dispatch a new thread */\r
- NextThread = NULL;\r
- CurrentThread->WaitIrql = OldIrql;\r
- KiDispatchThreadNoLock(Ready);\r
- KeLowerIrql(OldIrql);\r
- return;\r
-#endif\r
-\r
- /* Release the PRCB lock */\r
- KiReleasePrcbLock(Prcb);\r
- }\r
-\r
- /* Unlock dispatcher database */\r
- KiReleaseDispatcherLock(OldIrql);\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-LONG\r
-NTAPI\r
-KeSetBasePriorityThread(IN PKTHREAD Thread,\r
- IN LONG Increment)\r
-{\r
- KIRQL OldIrql;\r
- KPRIORITY OldBasePriority, Priority, BasePriority;\r
- LONG OldIncrement;\r
- PKPROCESS Process;\r
- BOOLEAN Released;\r
- ASSERT_THREAD(Thread);\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Get the process */\r
- Process = Thread->ApcState.Process;\r
-\r
- /* Lock the Dispatcher Database */\r
- OldIrql = KiAcquireDispatcherLock();\r
-\r
- /* Lock the thread */\r
- KiAcquireThreadLock(Thread);\r
-\r
- /* Save the old base priority and increment */\r
- OldBasePriority = Thread->BasePriority;\r
- OldIncrement = OldBasePriority - Process->BasePriority;\r
-\r
- /* If priority saturation happened, use the saturated increment */\r
- if (Thread->Saturation) OldIncrement = (HIGH_PRIORITY + 1) / 2 *\r
- Thread->Saturation;\r
-\r
- /* Now check if saturation is being used for the new value */\r
- if (abs(Increment) >= ((HIGH_PRIORITY + 1) / 2))\r
- {\r
- /* Check if we need positive or negative saturation */\r
- Thread->Saturation = (Increment > 0) ? 1 : -1;\r
- }\r
-\r
- /* Normalize the Base Priority */\r
- BasePriority = Process->BasePriority + Increment;\r
- if (Process->BasePriority >= LOW_REALTIME_PRIORITY)\r
- {\r
- /* Check if it's too low */\r
- if (BasePriority < LOW_REALTIME_PRIORITY)\r
- {\r
- /* Set it to the lowest real time level */\r
- BasePriority = LOW_REALTIME_PRIORITY;\r
- }\r
-\r
- /* Check if it's too high */\r
- if (BasePriority > HIGH_PRIORITY) BasePriority = HIGH_PRIORITY;\r
-\r
- /* We are at real time, so use the raw base priority */\r
- Priority = BasePriority;\r
- }\r
- else\r
- {\r
- /* Check if it's entering the real time range */\r
- if (BasePriority >= LOW_REALTIME_PRIORITY)\r
- {\r
- /* Set it to the highest dynamic level */\r
- BasePriority = LOW_REALTIME_PRIORITY - 1;\r
- }\r
-\r
- /* Check if it's too low and normalize it */\r
- if (BasePriority <= LOW_PRIORITY) BasePriority = 1;\r
-\r
- /* Check if Saturation is used */\r
- if (Thread->Saturation)\r
- {\r
- /* Use the raw base priority */\r
- Priority = BasePriority;\r
- }\r
- else\r
- {\r
- /* Otherwise, calculate the new priority */\r
- Priority = KiComputeNewPriority(Thread);\r
-\r
- /* Check if it entered the real-time range */\r
- if (Priority >= LOW_REALTIME_PRIORITY)\r
- {\r
- /* Normalize it down to the highest dynamic priority */\r
- Priority = LOW_REALTIME_PRIORITY - 1;\r
- }\r
- }\r
- }\r
-\r
- /* Finally set the new base priority */\r
- Thread->BasePriority = (SCHAR)BasePriority;\r
-\r
- /* Reset the decrements */\r
- Thread->PriorityDecrement = 0;\r
-\r
- /* Check if we're changing priority after all */\r
- if (Priority != Thread->Priority)\r
- {\r
- /* Reset the quantum and do the actual priority modification */\r
- Thread->Quantum = Thread->QuantumReset;\r
- KiSetPriorityThread(Thread, Priority, &Released);\r
- }\r
-\r
- /* Release thread lock */\r
- KiReleaseThreadLock(Thread);\r
-\r
- /* Check if lock was released */\r
- if (!Released)\r
- {\r
- /* Release the dispatcher database */\r
- KiReleaseDispatcherLock(OldIrql);\r
- }\r
- else\r
- {\r
- /* Lower IRQL only */\r
- KeLowerIrql(OldIrql);\r
- }\r
-\r
- /* Return old increment */\r
- return OldIncrement;\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-KAFFINITY\r
-NTAPI\r
-KeSetAffinityThread(IN PKTHREAD Thread,\r
- IN KAFFINITY Affinity)\r
-{\r
- KIRQL OldIrql;\r
- KAFFINITY OldAffinity;\r
- BOOLEAN Released;\r
- ASSERT_THREAD(Thread);\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Lock the dispatcher database */\r
- OldIrql = KiAcquireDispatcherLock();\r
-\r
- /* Call the internal function */\r
- OldAffinity = KiSetAffinityThread(Thread, Affinity, &Released);\r
-\r
- /* Check if lock was released */\r
- if (!Released)\r
- {\r
- /* Release the dispatcher database */\r
- KiReleaseDispatcherLock(OldIrql);\r
- }\r
- else\r
- {\r
- /* Lower IRQL only */\r
- KeLowerIrql(OldIrql);\r
- }\r
-\r
- /* Return old affinity */\r
- return OldAffinity;\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-KPRIORITY\r
-NTAPI\r
-KeSetPriorityThread(IN PKTHREAD Thread,\r
- IN KPRIORITY Priority)\r
-{\r
- KIRQL OldIrql;\r
- KPRIORITY OldPriority;\r
- BOOLEAN Released;\r
- ASSERT_THREAD(Thread);\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
- ASSERT((Priority <= HIGH_PRIORITY) && (Priority >= LOW_PRIORITY));\r
-\r
- /* Lock the Dispatcher Database */\r
- OldIrql = KiAcquireDispatcherLock();\r
-\r
- /* Lock the thread */\r
- KiAcquireThreadLock(Thread);\r
-\r
- /* Save the old Priority */\r
- OldPriority = Thread->Priority;\r
-\r
- /* Make sure that an actual change is being done */\r
- if (OldPriority != Priority)\r
- {\r
- /* Reset the Quantum and Decrements */\r
- Thread->Quantum = Thread->QuantumReset;\r
- Thread->PriorityDecrement = 0;\r
-\r
- /* Set the new Priority */\r
- KiSetPriorityThread(Thread, Priority, &Released);\r
- }\r
-\r
- /* Release thread lock */\r
- KiReleaseThreadLock(Thread);\r
-\r
- /* Check if lock was released */\r
- if (!Released)\r
- {\r
- /* Release the dispatcher database */\r
- KiReleaseDispatcherLock(OldIrql);\r
- }\r
- else\r
- {\r
- /* Lower IRQL only */\r
- KeLowerIrql(OldIrql);\r
- }\r
-\r
- /* Return Old Priority */\r
- return OldPriority;\r
-}\r
-\r
-/*\r
- * @implemented\r
- */\r
-VOID\r
-NTAPI\r
-KeTerminateThread(IN KPRIORITY Increment)\r
-{\r
- PLIST_ENTRY *ListHead;\r
- PETHREAD Entry, SavedEntry;\r
- PETHREAD *ThreadAddr;\r
- KLOCK_QUEUE_HANDLE LockHandle;\r
- PKTHREAD Thread = KeGetCurrentThread();\r
- PKPROCESS Process = Thread->ApcState.Process;\r
- ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);\r
-\r
- /* Lock the process */\r
- KiAcquireProcessLock(Process, &LockHandle);\r
-\r
- /* Make sure we won't get Swapped */\r
- KiSetThreadSwapBusy(Thread);\r
-\r
- /* Save the Kernel and User Times */\r
- Process->KernelTime += Thread->KernelTime;\r
- Process->UserTime += Thread->UserTime;\r
-\r
- /* Get the current entry and our Port */\r
- Entry = (PETHREAD)PspReaperListHead.Flink;\r
- ThreadAddr = &((PETHREAD)Thread)->ReaperLink;\r
-\r
- /* Add it to the reaper's list */\r
- do\r
- {\r
- /* Get the list head */\r
- ListHead = &PspReaperListHead.Flink;\r
-\r
- /* Link ourselves */\r
- *ThreadAddr = Entry;\r
- SavedEntry = Entry;\r
-\r
- /* Now try to do the exchange */\r
- Entry = InterlockedCompareExchangePointer(ListHead, ThreadAddr, Entry);\r
-\r
- /* Break out if the change was succesful */\r
- } while (Entry != SavedEntry);\r
-\r
- /* Acquire the dispatcher lock */\r
- KiAcquireDispatcherLockAtDpcLevel();\r
-\r
- /* Check if the reaper wasn't active */\r
- if (!Entry)\r
- {\r
- /* Activate it as a work item, directly through its Queue */\r
- KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue,\r
- &PspReaperWorkItem.List,\r
- FALSE);\r
- }\r
-\r
- /* Check the thread has an associated queue */\r
- if (Thread->Queue)\r
- {\r
- /* Remove it from the list, and handle the queue */\r
- RemoveEntryList(&Thread->QueueListEntry);\r
- KiWakeQueue(Thread->Queue);\r
- }\r
-\r
- /* Signal the thread */\r
- Thread->DispatcherHeader.SignalState = TRUE;\r
- if (!IsListEmpty(&Thread->DispatcherHeader.WaitListHead))\r
- {\r
- /* Unwait the threads */\r
- KxUnwaitThread(&Thread->DispatcherHeader, Increment);\r
- }\r
-\r
- /* Remove the thread from the list */\r
- RemoveEntryList(&Thread->ThreadListEntry);\r
-\r
- /* Release the process lock */\r
- KiReleaseProcessLockFromDpcLevel(&LockHandle);\r
-\r
- /* Set us as terminated, decrease the Process's stack count */\r
- Thread->State = Terminated;\r
-\r
- /* Decrease stack count */\r
- ASSERT(Process->StackCount != 0);\r
- ASSERT(Process->State == ProcessInMemory);\r
- Process->StackCount--;\r
- if (!Process->StackCount)\r
- {\r
- /* FIXME: Swap stacks */\r
- }\r
-\r
- /* Rundown arch-specific parts */\r
- KiRundownThread(Thread);\r
-\r
- /* Swap to a new thread */\r
- KiReleaseDispatcherLockFromDpcLevel();\r
- KiSwapThread(Thread, KeGetCurrentPrcb());\r
-}\r
+/*
+ * 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;
+}
+
+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;
+}
+
+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;
+}
+
+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 */
+ KiReleaseProcessLock(&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);
+
+ /* 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 */
+ 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 = NextEntry->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;
+ Thread->Iopl = Process->Iopl;
+ 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 = (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)));
+#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);
+}
+
+NTSTATUS
+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, 1);
+
+ /* 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 */
+ KiReleaseProcessLock(&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;
+
+ /* 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->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[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,
+ 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 */
+ 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
+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);
+ }
+}
+
+VOID
+NTAPI
+KeUninitThread(IN PKTHREAD Thread)
+{
+ /* Delete the stack */
+ MmDeleteKernelStack(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);
+
+#ifdef NEW_SCHEDULER
+ /* 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;
+ }
+#else
+ /* We need to dispatch a new thread */
+ NextThread = NULL;
+ CurrentThread->WaitIrql = OldIrql;
+ KiDispatchThreadNoLock(Ready);
+ KeLowerIrql(OldIrql);
+ return;
+#endif
+
+ /* 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);
+
+#ifdef NEW_SCHEDULER
+ /* 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;
+ }
+#else
+ /* We need to dispatch a new thread */
+ NextThread = NULL;
+ CurrentThread->WaitIrql = OldIrql;
+ KiDispatchThreadNoLock(Ready);
+ KeLowerIrql(OldIrql);
+ return;
+#endif
+
+ /* 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;
+ BOOLEAN Released = FALSE;
+ 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;
+
+ /* 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, &Released);
+ }
+
+ /* Release thread lock */
+ KiReleaseThreadLock(Thread);
+
+ /* Check if lock was released */
+ if (!Released)
+ {
+ /* Release the dispatcher database */
+ KiReleaseDispatcherLock(OldIrql);
+ }
+ else
+ {
+ /* Lower IRQL only */
+ KeLowerIrql(OldIrql);
+ }
+
+ /* Return old increment */
+ 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;
+ BOOLEAN Released = FALSE;
+ 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, &Released);
+ }
+
+ /* Release thread lock */
+ KiReleaseThreadLock(Thread);
+
+ /* Check if lock was released */
+ if (!Released)
+ {
+ /* Release the dispatcher database */
+ KiReleaseDispatcherLock(OldIrql);
+ }
+ else
+ {
+ /* Lower IRQL only */
+ KeLowerIrql(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(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->DispatcherHeader.SignalState = TRUE;
+ if (!IsListEmpty(&Thread->DispatcherHeader.WaitListHead))
+ {
+ /* Unwait the threads */
+ KxUnwaitThread(&Thread->DispatcherHeader, 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());
+}