/* * 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 #define NDEBUG #include 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()); }