mirror of
https://github.com/reactos/reactos.git
synced 2025-04-26 08:30:21 +00:00

- Detect if Kernel APCs were pending during thread shutdown. - Call Lego Routine, if registered, during thread shutdown. svn path=/trunk/; revision=16705
1406 lines
36 KiB
C
1406 lines
36 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/ke/kthread.c
|
|
* PURPOSE: Microkernel thread support
|
|
*
|
|
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
|
|
* David Welch (welch@cwcom.net)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <internal/debug.h>
|
|
|
|
#ifndef MUTANT_INCREMENT
|
|
#define MUTANT_INCREMENT 1
|
|
#endif
|
|
|
|
#define THREAD_ALERT_INCREMENT 2
|
|
|
|
extern EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
|
|
|
|
/*
|
|
* PURPOSE: List of threads associated with each priority level
|
|
*/
|
|
LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY];
|
|
static ULONG PriorityListMask = 0;
|
|
ULONG IdleProcessorMask = 0;
|
|
extern BOOLEAN DoneInitYet;
|
|
extern PETHREAD PspReaperList;
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
STATIC
|
|
VOID
|
|
KiRequestReschedule(CCHAR Processor)
|
|
{
|
|
PKPCR Pcr;
|
|
|
|
Pcr = (PKPCR)(KPCR_BASE + Processor * PAGE_SIZE);
|
|
Pcr->Prcb->QuantumEnd = TRUE;
|
|
KiIpiSendRequest(1 << Processor, IPI_REQUEST_DPC);
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
KiInsertIntoThreadList(KPRIORITY Priority,
|
|
PKTHREAD Thread)
|
|
{
|
|
ASSERT(Ready == Thread->State);
|
|
ASSERT(Thread->Priority == Priority);
|
|
|
|
if (Priority >= MAXIMUM_PRIORITY || Priority < LOW_PRIORITY) {
|
|
|
|
DPRINT1("Invalid thread priority (%d)\n", Priority);
|
|
KEBUGCHECK(0);
|
|
}
|
|
|
|
InsertTailList(&PriorityListHead[Priority], &Thread->WaitListEntry);
|
|
PriorityListMask |= (1 << Priority);
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
KiRemoveFromThreadList(PKTHREAD Thread)
|
|
{
|
|
ASSERT(Ready == Thread->State);
|
|
RemoveEntryList(&Thread->WaitListEntry);
|
|
if (IsListEmpty(&PriorityListHead[(ULONG)Thread->Priority])) {
|
|
|
|
PriorityListMask &= ~(1 << Thread->Priority);
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
PKTHREAD
|
|
KiScanThreadList(KPRIORITY Priority,
|
|
KAFFINITY Affinity)
|
|
{
|
|
PLIST_ENTRY current_entry;
|
|
PKTHREAD current;
|
|
ULONG Mask;
|
|
|
|
Mask = (1 << Priority);
|
|
|
|
if (PriorityListMask & Mask) {
|
|
|
|
current_entry = PriorityListHead[Priority].Flink;
|
|
|
|
while (current_entry != &PriorityListHead[Priority]) {
|
|
|
|
current = CONTAINING_RECORD(current_entry, KTHREAD, WaitListEntry);
|
|
|
|
if (current->State != Ready) {
|
|
|
|
DPRINT1("%d/%d\n", ¤t, current->State);
|
|
}
|
|
|
|
ASSERT(current->State == Ready);
|
|
|
|
if (current->Affinity & Affinity) {
|
|
|
|
KiRemoveFromThreadList(current);
|
|
return(current);
|
|
}
|
|
|
|
current_entry = current_entry->Flink;
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
VOID
|
|
STDCALL
|
|
KiDispatchThreadNoLock(ULONG NewThreadStatus)
|
|
{
|
|
KPRIORITY CurrentPriority;
|
|
PKTHREAD Candidate;
|
|
ULONG Affinity;
|
|
PKTHREAD CurrentThread = KeGetCurrentThread();
|
|
|
|
DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
|
|
CurrentThread, NewThreadStatus, CurrentThread->State);
|
|
|
|
CurrentThread->State = (UCHAR)NewThreadStatus;
|
|
|
|
if (NewThreadStatus == Ready) {
|
|
|
|
KiInsertIntoThreadList(CurrentThread->Priority,
|
|
CurrentThread);
|
|
}
|
|
|
|
Affinity = 1 << KeGetCurrentProcessorNumber();
|
|
|
|
for (CurrentPriority = HIGH_PRIORITY; CurrentPriority >= LOW_PRIORITY; CurrentPriority--) {
|
|
|
|
Candidate = KiScanThreadList(CurrentPriority, Affinity);
|
|
|
|
if (Candidate == CurrentThread) {
|
|
|
|
Candidate->State = Running;
|
|
KeReleaseDispatcherDatabaseLockFromDpcLevel();
|
|
return;
|
|
}
|
|
|
|
if (Candidate != NULL) {
|
|
|
|
PKTHREAD OldThread;
|
|
PKTHREAD IdleThread;
|
|
|
|
DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
|
|
|
|
Candidate->State = Running;
|
|
|
|
OldThread = CurrentThread;
|
|
CurrentThread = Candidate;
|
|
IdleThread = KeGetCurrentPrcb()->IdleThread;
|
|
|
|
if (OldThread == IdleThread) {
|
|
|
|
IdleProcessorMask &= ~Affinity;
|
|
|
|
} else if (CurrentThread == IdleThread) {
|
|
|
|
IdleProcessorMask |= Affinity;
|
|
}
|
|
|
|
MmUpdatePageDir(PsGetCurrentProcess(),((PETHREAD)CurrentThread)->ThreadsProcess, sizeof(EPROCESS));
|
|
|
|
/* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */
|
|
DPRINT("You are : %x, swapping to: %x\n", OldThread, CurrentThread);
|
|
KiArchContextSwitch(CurrentThread);
|
|
DPRINT("You are : %x, swapped from: %x\n", OldThread, CurrentThread);
|
|
return;
|
|
}
|
|
}
|
|
|
|
DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber());
|
|
KEBUGCHECK(0);
|
|
}
|
|
|
|
VOID
|
|
STDCALL
|
|
KiBlockThread(PNTSTATUS Status,
|
|
UCHAR Alertable,
|
|
ULONG WaitMode,
|
|
UCHAR WaitReason)
|
|
{
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
PKWAIT_BLOCK WaitBlock;
|
|
|
|
if (Thread->ApcState.KernelApcPending) {
|
|
|
|
DPRINT("Dispatching Thread as ready (APC!)\n");
|
|
|
|
/* Remove Waits */
|
|
WaitBlock = Thread->WaitBlockList;
|
|
do {
|
|
RemoveEntryList (&WaitBlock->WaitListEntry);
|
|
WaitBlock = WaitBlock->NextWaitBlock;
|
|
} while (WaitBlock != Thread->WaitBlockList);
|
|
Thread->WaitBlockList = NULL;
|
|
|
|
/* Dispatch it and return status */
|
|
KiDispatchThreadNoLock (Ready);
|
|
if (Status != NULL) *Status = STATUS_KERNEL_APC;
|
|
|
|
} else {
|
|
|
|
/* Set the Thread Data as Requested */
|
|
DPRINT("Dispatching Thread as blocked: %d\n", Thread->WaitStatus);
|
|
Thread->Alertable = Alertable;
|
|
Thread->WaitMode = (UCHAR)WaitMode;
|
|
Thread->WaitReason = WaitReason;
|
|
|
|
/* Dispatch it and return status */
|
|
KiDispatchThreadNoLock(Waiting);
|
|
DPRINT("Dispatching Thread as blocked: %d\n", Thread->WaitStatus);
|
|
if (Status != NULL) *Status = Thread->WaitStatus;
|
|
}
|
|
|
|
DPRINT("Releasing Dispatcher Lock\n");
|
|
KfLowerIrql(Thread->WaitIrql);
|
|
}
|
|
|
|
VOID
|
|
STDCALL
|
|
KiDispatchThread(ULONG NewThreadStatus)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
if (!DoneInitYet || KeGetCurrentPrcb()->IdleThread == NULL) {
|
|
return;
|
|
}
|
|
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
KiDispatchThreadNoLock(NewThreadStatus);
|
|
KeLowerIrql(OldIrql);
|
|
}
|
|
|
|
VOID
|
|
STDCALL
|
|
KiUnblockThread(PKTHREAD Thread,
|
|
PNTSTATUS WaitStatus,
|
|
KPRIORITY Increment)
|
|
{
|
|
if (Terminated == Thread->State) {
|
|
|
|
DPRINT("Can't unblock thread 0x%x because it's terminating\n",
|
|
Thread);
|
|
|
|
} else if (Ready == Thread->State ||
|
|
Running == Thread->State) {
|
|
|
|
DPRINT("Can't unblock thread 0x%x because it's %s\n",
|
|
Thread, (Thread->State == Ready ? "ready" : "running"));
|
|
|
|
} else {
|
|
|
|
LONG Processor;
|
|
KAFFINITY Affinity;
|
|
|
|
/* FIXME: This propably isn't the right way to do it... */
|
|
/* No it's not... i'll fix it later-- Alex */
|
|
if (Thread->Priority < LOW_REALTIME_PRIORITY &&
|
|
Thread->BasePriority < LOW_REALTIME_PRIORITY - 2) {
|
|
|
|
if (!Thread->PriorityDecrement && !Thread->DisableBoost) {
|
|
|
|
Thread->Priority = Thread->BasePriority + Increment;
|
|
Thread->PriorityDecrement = Increment;
|
|
}
|
|
|
|
} else {
|
|
|
|
Thread->Quantum = Thread->QuantumReset;
|
|
}
|
|
|
|
if (WaitStatus != NULL) {
|
|
|
|
Thread->WaitStatus = *WaitStatus;
|
|
}
|
|
|
|
Thread->State = Ready;
|
|
KiInsertIntoThreadList(Thread->Priority, Thread);
|
|
Processor = KeGetCurrentProcessorNumber();
|
|
Affinity = Thread->Affinity;
|
|
|
|
if (!(IdleProcessorMask & (1 << Processor) & Affinity) &&
|
|
(IdleProcessorMask & ~(1 << Processor) & Affinity)) {
|
|
|
|
LONG i;
|
|
|
|
for (i = 0; i < KeNumberProcessors - 1; i++) {
|
|
|
|
Processor++;
|
|
|
|
if (Processor >= KeNumberProcessors) {
|
|
|
|
Processor = 0;
|
|
}
|
|
|
|
if (IdleProcessorMask & (1 << Processor) & Affinity) {
|
|
#if 0
|
|
/* FIXME:
|
|
* Reschedule the threads on an other processor
|
|
*/
|
|
KeReleaseDispatcherDatabaseLockFromDpcLevel();
|
|
KiRequestReschedule(Processor);
|
|
KeAcquireDispatcherDatabaseLockAtDpcLevel();
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
STDCALL
|
|
KiSuspendThreadKernelRoutine(PKAPC Apc,
|
|
PKNORMAL_ROUTINE* NormalRoutine,
|
|
PVOID* NormalContext,
|
|
PVOID* SystemArgument1,
|
|
PVOID* SystemArguemnt2)
|
|
{
|
|
}
|
|
|
|
VOID
|
|
STDCALL
|
|
KiSuspendThreadNormalRoutine(PVOID NormalContext,
|
|
PVOID SystemArgument1,
|
|
PVOID SystemArgument2)
|
|
{
|
|
PKTHREAD CurrentThread = KeGetCurrentThread();
|
|
|
|
/* Non-alertable kernel-mode suspended wait */
|
|
DPRINT("Waiting...\n");
|
|
KeWaitForSingleObject(&CurrentThread->SuspendSemaphore,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
DPRINT("Done Waiting\n");
|
|
}
|
|
|
|
#ifdef KeGetCurrentThread
|
|
#undef KeGetCurrentThread
|
|
#endif
|
|
/*
|
|
* @implemented
|
|
*/
|
|
PKTHREAD
|
|
STDCALL
|
|
KeGetCurrentThread(VOID)
|
|
{
|
|
#ifdef CONFIG_SMP
|
|
ULONG Flags;
|
|
PKTHREAD Thread;
|
|
Ke386SaveFlags(Flags);
|
|
Ke386DisableInterrupts();
|
|
Thread = KeGetCurrentPrcb()->CurrentThread;
|
|
Ke386RestoreFlags(Flags);
|
|
return Thread;
|
|
#else
|
|
return(KeGetCurrentPrcb()->CurrentThread);
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
STDCALL
|
|
KeSetPreviousMode(ULONG Mode)
|
|
{
|
|
PsGetCurrentThread()->Tcb.PreviousMode = (UCHAR)Mode;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
KPROCESSOR_MODE
|
|
STDCALL
|
|
KeGetPreviousMode(VOID)
|
|
{
|
|
return (ULONG)PsGetCurrentThread()->Tcb.PreviousMode;
|
|
}
|
|
|
|
BOOLEAN
|
|
STDCALL
|
|
KeDisableThreadApcQueueing(IN PKTHREAD Thread)
|
|
{
|
|
KIRQL OldIrql;
|
|
BOOLEAN PreviousState;
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Save old state */
|
|
PreviousState = Thread->ApcQueueable;
|
|
|
|
/* Disable it now */
|
|
Thread->ApcQueueable = FALSE;
|
|
|
|
/* Release the Lock */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
|
|
/* Return old state */
|
|
return PreviousState;
|
|
}
|
|
|
|
VOID
|
|
STDCALL
|
|
KeRundownThread(VOID)
|
|
{
|
|
KIRQL OldIrql;
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
PLIST_ENTRY CurrentEntry;
|
|
PKMUTANT Mutant;
|
|
|
|
DPRINT("KeRundownThread: %x\n", Thread);
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
while (!IsListEmpty(&Thread->MutantListHead)) {
|
|
|
|
/* Get the Mutant */
|
|
CurrentEntry = RemoveHeadList(&Thread->MutantListHead);
|
|
Mutant = CONTAINING_RECORD(CurrentEntry, KMUTANT, MutantListEntry);
|
|
ASSERT(Mutant->ApcDisable == 0);
|
|
|
|
/* Uncondtionally abandon it */
|
|
DPRINT("Abandonning the Mutant\n");
|
|
Mutant->Header.SignalState = 1;
|
|
Mutant->Abandoned = TRUE;
|
|
Mutant->OwnerThread = NULL;
|
|
RemoveEntryList(&Mutant->MutantListEntry);
|
|
|
|
/* Check if the Wait List isn't empty */
|
|
DPRINT("Checking whether to wake the Mutant\n");
|
|
if (!IsListEmpty(&Mutant->Header.WaitListHead)) {
|
|
|
|
/* Wake the Mutant */
|
|
DPRINT("Waking the Mutant\n");
|
|
KiWaitTest(&Mutant->Header, MUTANT_INCREMENT);
|
|
}
|
|
}
|
|
|
|
/* Release the Lock */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
}
|
|
|
|
ULONG
|
|
STDCALL
|
|
KeResumeThread(PKTHREAD Thread)
|
|
{
|
|
ULONG PreviousCount;
|
|
KIRQL OldIrql;
|
|
|
|
DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread,
|
|
Thread->SuspendCount, Thread->FreezeCount);
|
|
|
|
/* Lock the Dispatcher */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Save the Old Count */
|
|
PreviousCount = Thread->SuspendCount;
|
|
|
|
/* Check if it existed */
|
|
if (PreviousCount) {
|
|
|
|
Thread->SuspendCount--;
|
|
|
|
/* Decrease the current Suspend Count and Check Freeze Count */
|
|
if ((!Thread->SuspendCount) && (!Thread->FreezeCount)) {
|
|
|
|
/* Signal the Suspend Semaphore */
|
|
Thread->SuspendSemaphore.Header.SignalState++;
|
|
KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
|
|
/* Release Lock and return the Old State */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
return PreviousCount;
|
|
}
|
|
|
|
BOOLEAN
|
|
STDCALL
|
|
KiInsertQueueApc(PKAPC Apc,
|
|
KPRIORITY PriorityBoost);
|
|
|
|
/*
|
|
* Used by the debugging code to freeze all the process's threads
|
|
* while the debugger is examining their state.
|
|
*/
|
|
VOID
|
|
STDCALL
|
|
KeFreezeAllThreads(PKPROCESS Process)
|
|
{
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY CurrentEntry;
|
|
PKTHREAD Current;
|
|
PKTHREAD CurrentThread = KeGetCurrentThread();
|
|
|
|
/* Acquire Lock */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Loop the Process's Threads */
|
|
CurrentEntry = Process->ThreadListHead.Flink;
|
|
while (CurrentEntry != &Process->ThreadListHead)
|
|
{
|
|
/* Get the Thread */
|
|
Current = CONTAINING_RECORD(CurrentEntry, KTHREAD, ThreadListEntry);
|
|
|
|
/* Make sure it's not ours */
|
|
if (Current == CurrentThread) continue;
|
|
|
|
/* Make sure it wasn't already frozen, and that it's not suspended */
|
|
if (!(++Current->FreezeCount) && !(Current->SuspendCount))
|
|
{
|
|
/* Insert the APC */
|
|
if (!KiInsertQueueApc(&Current->SuspendApc, IO_NO_INCREMENT))
|
|
{
|
|
/* Unsignal the Semaphore, the APC already got inserted */
|
|
Current->SuspendSemaphore.Header.SignalState--;
|
|
}
|
|
}
|
|
|
|
CurrentEntry = CurrentEntry->Flink;
|
|
}
|
|
|
|
/* Release the lock */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
}
|
|
|
|
NTSTATUS
|
|
STDCALL
|
|
KeSuspendThread(PKTHREAD Thread)
|
|
{
|
|
ULONG PreviousCount;
|
|
KIRQL OldIrql;
|
|
|
|
DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread, Thread->SuspendCount, Thread->FreezeCount);
|
|
|
|
/* Lock the Dispatcher */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Save the Old Count */
|
|
PreviousCount = Thread->SuspendCount;
|
|
|
|
/* Increment it */
|
|
Thread->SuspendCount++;
|
|
|
|
/* Check if we should suspend it */
|
|
if (!PreviousCount && !Thread->FreezeCount) {
|
|
|
|
/* Insert the APC */
|
|
if (!KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT)) {
|
|
|
|
/* Unsignal the Semaphore, the APC already got inserted */
|
|
Thread->SuspendSemaphore.Header.SignalState--;
|
|
}
|
|
}
|
|
|
|
/* Release Lock and return the Old State */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
return PreviousCount;
|
|
}
|
|
|
|
ULONG
|
|
STDCALL
|
|
KeForceResumeThread(IN PKTHREAD Thread)
|
|
{
|
|
KIRQL OldIrql;
|
|
ULONG PreviousCount;
|
|
|
|
/* Lock the Dispatcher Database and the APC Queue */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Save the old Suspend Count */
|
|
PreviousCount = Thread->SuspendCount + Thread->FreezeCount;
|
|
|
|
/* If the thread is suspended, wake it up!!! */
|
|
if (PreviousCount) {
|
|
|
|
/* Unwait it completely */
|
|
Thread->SuspendCount = 0;
|
|
Thread->FreezeCount = 0;
|
|
|
|
/* Signal and satisfy */
|
|
Thread->SuspendSemaphore.Header.SignalState++;
|
|
KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
|
|
}
|
|
|
|
/* Release Lock and return the Old State */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
return PreviousCount;
|
|
}
|
|
|
|
ULONG
|
|
STDCALL
|
|
KeAlertResumeThread(IN PKTHREAD Thread)
|
|
{
|
|
ULONG PreviousCount;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the Dispatcher Database and the APC Queue */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
KiAcquireSpinLock(&Thread->ApcQueueLock);
|
|
|
|
/* Return if Thread is already alerted. */
|
|
if (Thread->Alerted[KernelMode] == FALSE) {
|
|
|
|
/* If it's Blocked, unblock if it we should */
|
|
if (Thread->State == Waiting && Thread->Alertable) {
|
|
|
|
DPRINT("Aborting Wait\n");
|
|
KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
|
|
|
|
} else {
|
|
|
|
/* If not, simply Alert it */
|
|
Thread->Alerted[KernelMode] = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Save the old Suspend Count */
|
|
PreviousCount = Thread->SuspendCount;
|
|
|
|
/* If the thread is suspended, decrease one of the suspend counts */
|
|
if (PreviousCount) {
|
|
|
|
/* Decrease count. If we are now zero, unwait it completely */
|
|
if (--Thread->SuspendCount) {
|
|
|
|
/* Signal and satisfy */
|
|
Thread->SuspendSemaphore.Header.SignalState++;
|
|
KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
|
|
/* Release Locks and return the Old State */
|
|
KiReleaseSpinLock(&Thread->ApcQueueLock);
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
return PreviousCount;
|
|
}
|
|
|
|
BOOLEAN
|
|
STDCALL
|
|
KeAlertThread(PKTHREAD Thread,
|
|
KPROCESSOR_MODE AlertMode)
|
|
{
|
|
KIRQL OldIrql;
|
|
BOOLEAN PreviousState;
|
|
|
|
/* Acquire the Dispatcher Database Lock */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Save the Previous State */
|
|
PreviousState = Thread->Alerted[AlertMode];
|
|
|
|
/* Return if Thread is already alerted. */
|
|
if (PreviousState == FALSE) {
|
|
|
|
/* If it's Blocked, unblock if it we should */
|
|
if (Thread->State == Waiting &&
|
|
(AlertMode == KernelMode || Thread->WaitMode == AlertMode) &&
|
|
Thread->Alertable) {
|
|
|
|
DPRINT("Aborting Wait\n");
|
|
KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
|
|
|
|
} else {
|
|
|
|
/* If not, simply Alert it */
|
|
Thread->Alerted[AlertMode] = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Release the Dispatcher Lock */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
|
|
/* Return the old state */
|
|
return PreviousState;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID
|
|
STDCALL
|
|
KeCapturePersistentThreadState(IN PVOID CurrentThread,
|
|
IN ULONG Setting1,
|
|
IN ULONG Setting2,
|
|
IN ULONG Setting3,
|
|
IN ULONG Setting4,
|
|
IN ULONG Setting5,
|
|
IN PVOID ThreadState)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Initialize the microkernel state of the thread
|
|
*/
|
|
VOID
|
|
STDCALL
|
|
KeInitializeThread(PKPROCESS Process,
|
|
PKTHREAD Thread,
|
|
PKSYSTEM_ROUTINE SystemRoutine,
|
|
PKSTART_ROUTINE StartRoutine,
|
|
PVOID StartContext,
|
|
PCONTEXT Context,
|
|
PVOID Teb,
|
|
PVOID KernelStack)
|
|
{
|
|
/* Initalize the Dispatcher Header */
|
|
DPRINT("Initializing Dispatcher Header for New Thread: %x in Process: %x\n", Thread, Process);
|
|
KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
|
|
ThreadObject,
|
|
sizeof(KTHREAD),
|
|
FALSE);
|
|
|
|
DPRINT("Thread Header Created. SystemRoutine: %x, StartRoutine: %x with Context: %x\n",
|
|
SystemRoutine, StartRoutine, StartContext);
|
|
DPRINT("UserMode Information. Context: %x, Teb: %x\n", Context, Teb);
|
|
|
|
/* Initialize the Mutant List */
|
|
InitializeListHead(&Thread->MutantListHead);
|
|
|
|
/* 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;
|
|
KeInitializeSpinLock(&Thread->ApcQueueLock);
|
|
|
|
/* Initialize the Suspend APC */
|
|
KeInitializeApc(&Thread->SuspendApc,
|
|
Thread,
|
|
OriginalApcEnvironment,
|
|
KiSuspendThreadKernelRoutine,
|
|
NULL,
|
|
KiSuspendThreadNormalRoutine,
|
|
KernelMode,
|
|
NULL);
|
|
|
|
/* Initialize the Suspend Semaphore */
|
|
KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 128);
|
|
|
|
/* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
|
|
#if 0
|
|
Thread->WaitBlock[3].Object = (PVOID)&Thread->Timer;
|
|
Thread->WaitBlock[3].Thread = Thread;
|
|
Thread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
|
|
Thread->WaitBlock[3].WaitType = WaitAny;
|
|
Thread->WaitBlock[3].NextWaitBlock = NULL;
|
|
InsertTailList(&Thread->Timer.Header.WaitListHead,
|
|
&Thread->WaitBlock[3].WaitListEntry);
|
|
#endif
|
|
KeInitializeTimer(&Thread->Timer);
|
|
|
|
/* Set the TEB */
|
|
Thread->Teb = Teb;
|
|
|
|
/* Set the Thread Stacks */
|
|
Thread->InitialStack = (PCHAR)KernelStack + MM_STACK_SIZE;
|
|
Thread->StackBase = (PCHAR)KernelStack + MM_STACK_SIZE;
|
|
Thread->StackLimit = (ULONG_PTR)KernelStack;
|
|
Thread->KernelStackResident = TRUE;
|
|
|
|
/*
|
|
* Establish the pde's for the new stack and the thread structure within the
|
|
* address space of the new process. They are accessed while taskswitching or
|
|
* while handling page faults. At this point it isn't possible to call the
|
|
* page fault handler for the missing pde's.
|
|
*/
|
|
MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE);
|
|
MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
|
|
|
|
/* Initalize the Thread Context */
|
|
DPRINT("Initializing the Context for the thread: %x\n", Thread);
|
|
KiArchInitThreadWithContext(Thread,
|
|
SystemRoutine,
|
|
StartRoutine,
|
|
StartContext,
|
|
Context);
|
|
|
|
/* Setup scheduler Fields based on Parent */
|
|
DPRINT("Thread context created, setting Scheduler Data\n");
|
|
Thread->BasePriority = Process->BasePriority;
|
|
Thread->Quantum = Process->QuantumReset;
|
|
Thread->QuantumReset = Process->QuantumReset;
|
|
Thread->Affinity = Process->Affinity;
|
|
Thread->Priority = Process->BasePriority;
|
|
Thread->UserAffinity = Process->Affinity;
|
|
Thread->DisableBoost = Process->DisableBoost;
|
|
Thread->AutoAlignment = Process->AutoAlignment;
|
|
Thread->Iopl = Process->Iopl;
|
|
|
|
/* Set the Thread to initalized */
|
|
Thread->State = Initialized;
|
|
|
|
/*
|
|
* Insert the Thread into the Process's Thread List
|
|
* Note, this is the KTHREAD Thread List. It is removed in
|
|
* ke/kthread.c!KeTerminateThread.
|
|
*/
|
|
InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
|
|
DPRINT("Thread initalized\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
KPRIORITY
|
|
STDCALL
|
|
KeQueryPriorityThread (IN PKTHREAD Thread)
|
|
{
|
|
return Thread->Priority;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
ULONG
|
|
STDCALL
|
|
KeQueryRuntimeThread(IN PKTHREAD Thread,
|
|
OUT PULONG UserTime)
|
|
{
|
|
/* Return the User Time */
|
|
*UserTime = Thread->UserTime;
|
|
|
|
/* Return the Kernel Time */
|
|
return Thread->KernelTime;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
STDCALL
|
|
KeSetKernelStackSwapEnable(IN BOOLEAN Enable)
|
|
{
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
BOOLEAN PreviousState;
|
|
KIRQL OldIrql;
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Save Old State */
|
|
PreviousState = Thread->EnableStackSwap;
|
|
|
|
/* Set New State */
|
|
Thread->EnableStackSwap = Enable;
|
|
|
|
/* No, Release Lock */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
|
|
/* Return Old State */
|
|
return PreviousState;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
STDCALL
|
|
KeRevertToUserAffinityThread(VOID)
|
|
{
|
|
PKTHREAD CurrentThread = KeGetCurrentThread();
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT(CurrentThread->SystemAffinityActive != FALSE);
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Return to User Affinity */
|
|
CurrentThread->Affinity = CurrentThread->UserAffinity;
|
|
|
|
/* Disable System Affinity */
|
|
CurrentThread->SystemAffinityActive = FALSE;
|
|
|
|
/* Check if we need to Dispatch a New thread */
|
|
if (CurrentThread->Affinity & (1 << KeGetCurrentProcessorNumber())) {
|
|
|
|
/* No, just release */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
|
|
} else {
|
|
|
|
/* We need to dispatch a new thread */
|
|
CurrentThread->WaitIrql = OldIrql;
|
|
KiDispatchThreadNoLock(Ready);
|
|
KeLowerIrql(OldIrql);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
CCHAR
|
|
STDCALL
|
|
KeSetIdealProcessorThread(IN PKTHREAD Thread,
|
|
IN CCHAR Processor)
|
|
{
|
|
CCHAR PreviousIdealProcessor;
|
|
KIRQL OldIrql;
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Save Old Ideal Processor */
|
|
PreviousIdealProcessor = Thread->IdealProcessor;
|
|
|
|
/* Set New Ideal Processor */
|
|
Thread->IdealProcessor = Processor;
|
|
|
|
/* Release Lock */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
|
|
/* Return Old Ideal Processor */
|
|
return PreviousIdealProcessor;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
STDCALL
|
|
KeSetSystemAffinityThread(IN KAFFINITY Affinity)
|
|
{
|
|
PKTHREAD CurrentThread = KeGetCurrentThread();
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
|
|
|
|
/* Lock the Dispatcher Database */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Set the System Affinity Specified */
|
|
CurrentThread->Affinity = Affinity;
|
|
|
|
/* Enable System Affinity */
|
|
CurrentThread->SystemAffinityActive = TRUE;
|
|
|
|
/* Check if we need to Dispatch a New thread */
|
|
if (Affinity & (1 << KeGetCurrentProcessorNumber())) {
|
|
|
|
/* No, just release */
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
|
|
} else {
|
|
|
|
/* We need to dispatch a new thread */
|
|
CurrentThread->WaitIrql = OldIrql;
|
|
KiDispatchThreadNoLock(Ready);
|
|
KeLowerIrql(OldIrql);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LONG STDCALL
|
|
KeSetBasePriorityThread (PKTHREAD Thread,
|
|
LONG Increment)
|
|
/*
|
|
* Sets thread's base priority relative to the process' base priority
|
|
* Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
|
|
*/
|
|
{
|
|
KPRIORITY Priority;
|
|
if (Increment < -2)
|
|
{
|
|
Increment = -2;
|
|
}
|
|
else if (Increment > 2)
|
|
{
|
|
Increment = 2;
|
|
}
|
|
Priority = ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
|
|
if (Priority < LOW_PRIORITY)
|
|
{
|
|
Priority = LOW_PRIORITY;
|
|
}
|
|
else if (Priority >= MAXIMUM_PRIORITY)
|
|
{
|
|
Thread->BasePriority = HIGH_PRIORITY;
|
|
}
|
|
KeSetPriorityThread(Thread, Priority);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
KPRIORITY
|
|
STDCALL
|
|
KeSetPriorityThread(PKTHREAD Thread,
|
|
KPRIORITY Priority)
|
|
{
|
|
KPRIORITY OldPriority;
|
|
KIRQL OldIrql;
|
|
PKTHREAD CurrentThread;
|
|
ULONG Mask;
|
|
int i;
|
|
PKPCR Pcr;
|
|
|
|
if (Priority < LOW_PRIORITY || Priority >= MAXIMUM_PRIORITY) {
|
|
|
|
KEBUGCHECK(0);
|
|
}
|
|
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
OldPriority = Thread->Priority;
|
|
|
|
if (OldPriority != Priority) {
|
|
|
|
CurrentThread = KeGetCurrentThread();
|
|
|
|
if (Thread->State == Ready) {
|
|
|
|
KiRemoveFromThreadList(Thread);
|
|
Thread->BasePriority = Thread->Priority = (CHAR)Priority;
|
|
KiInsertIntoThreadList(Priority, Thread);
|
|
|
|
if (CurrentThread->Priority < Priority) {
|
|
|
|
KiDispatchThreadNoLock(Ready);
|
|
KeLowerIrql(OldIrql);
|
|
return (OldPriority);
|
|
}
|
|
|
|
} else if (Thread->State == Running) {
|
|
|
|
Thread->BasePriority = Thread->Priority = (CHAR)Priority;
|
|
|
|
if (Priority < OldPriority) {
|
|
|
|
/* Check for threads with a higher priority */
|
|
Mask = ~((1 << (Priority + 1)) - 1);
|
|
if (PriorityListMask & Mask) {
|
|
|
|
if (Thread == CurrentThread) {
|
|
|
|
KiDispatchThreadNoLock(Ready);
|
|
KeLowerIrql(OldIrql);
|
|
return (OldPriority);
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < KeNumberProcessors; i++) {
|
|
|
|
Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE);
|
|
|
|
if (Pcr->Prcb->CurrentThread == Thread) {
|
|
|
|
KeReleaseDispatcherDatabaseLockFromDpcLevel();
|
|
KiRequestReschedule(i);
|
|
KeLowerIrql(OldIrql);
|
|
return (OldPriority);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
Thread->BasePriority = Thread->Priority = (CHAR)Priority;
|
|
}
|
|
}
|
|
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
return(OldPriority);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*
|
|
* Sets thread's affinity
|
|
*/
|
|
NTSTATUS
|
|
STDCALL
|
|
KeSetAffinityThread(PKTHREAD Thread,
|
|
KAFFINITY Affinity)
|
|
{
|
|
KIRQL OldIrql;
|
|
LONG i;
|
|
PKPCR Pcr;
|
|
KAFFINITY ProcessorMask;
|
|
|
|
DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread, Affinity);
|
|
|
|
ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
|
|
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
Thread->UserAffinity = Affinity;
|
|
|
|
if (Thread->SystemAffinityActive == FALSE) {
|
|
|
|
Thread->Affinity = Affinity;
|
|
|
|
if (Thread->State == Running) {
|
|
|
|
ProcessorMask = 1 << KeGetCurrentKPCR()->Number;
|
|
if (Thread == KeGetCurrentThread()) {
|
|
|
|
if (!(Affinity & ProcessorMask)) {
|
|
|
|
KiDispatchThreadNoLock(Ready);
|
|
KeLowerIrql(OldIrql);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < KeNumberProcessors; i++) {
|
|
|
|
Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE);
|
|
if (Pcr->Prcb->CurrentThread == Thread) {
|
|
|
|
if (!(Affinity & ProcessorMask)) {
|
|
|
|
KeReleaseDispatcherDatabaseLockFromDpcLevel();
|
|
KiRequestReschedule(i);
|
|
KeLowerIrql(OldIrql);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT (i < KeNumberProcessors);
|
|
}
|
|
}
|
|
}
|
|
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
/* The Increment Argument seems to be ignored by NT and always 0 when called */
|
|
VOID
|
|
STDCALL
|
|
KeTerminateThread(IN KPRIORITY Increment)
|
|
{
|
|
KIRQL OldIrql;
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
|
|
/* Lock the Dispatcher Database and the APC Queue */
|
|
DPRINT("Terminating\n");
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
|
|
/* Remove the thread from the list */
|
|
RemoveEntryList(&Thread->ThreadListEntry);
|
|
|
|
/* Insert into the Reaper List */
|
|
DPRINT("List: %p\n", PspReaperList);
|
|
((PETHREAD)Thread)->ReaperLink = PspReaperList;
|
|
PspReaperList = (PETHREAD)Thread;
|
|
DPRINT("List: %p\n", PspReaperList);
|
|
|
|
/* Check if it's active */
|
|
if (PspReaping == FALSE) {
|
|
|
|
/* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */
|
|
PspReaping = TRUE;
|
|
DPRINT("Terminating\n");
|
|
KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue,
|
|
&PspReaperWorkItem.List,
|
|
FALSE);
|
|
}
|
|
|
|
/* Handle Kernel Queues */
|
|
if (Thread->Queue) {
|
|
|
|
DPRINT("Waking Queue\n");
|
|
RemoveEntryList(&Thread->QueueListEntry);
|
|
KiWakeQueue(Thread->Queue);
|
|
}
|
|
|
|
/* Signal the thread */
|
|
Thread->DispatcherHeader.SignalState = TRUE;
|
|
if (IsListEmpty(&Thread->DispatcherHeader.WaitListHead) != TRUE) {
|
|
|
|
/* Satisfy waits */
|
|
KiWaitTest((PVOID)Thread, Increment);
|
|
}
|
|
|
|
/* Find a new Thread */
|
|
KiDispatchThreadNoLock(Terminated);
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Tests whether there are any pending APCs for the current thread
|
|
* and if so the APCs will be delivered on exit from kernel mode
|
|
*/
|
|
BOOLEAN
|
|
STDCALL
|
|
KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
|
|
{
|
|
KIRQL OldIrql;
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
BOOLEAN OldState;
|
|
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Lock the Dispatcher Database and the APC Queue */
|
|
OldIrql = KeAcquireDispatcherDatabaseLock();
|
|
KiAcquireSpinLock(&Thread->ApcQueueLock);
|
|
|
|
/* Save the old State */
|
|
OldState = Thread->Alerted[AlertMode];
|
|
|
|
/* If the Thread is Alerted, Clear it */
|
|
if (OldState) {
|
|
|
|
Thread->Alerted[AlertMode] = FALSE;
|
|
|
|
} else if ((AlertMode == UserMode) && (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) {
|
|
|
|
/* If the mode is User and the Queue isn't empty, set Pending */
|
|
Thread->ApcState.UserApcPending = TRUE;
|
|
}
|
|
|
|
/* Release Locks and return the Old State */
|
|
KiReleaseSpinLock(&Thread->ApcQueueLock);
|
|
KeReleaseDispatcherDatabaseLock(OldIrql);
|
|
return OldState;
|
|
}
|
|
|
|
VOID
|
|
KiServiceCheck (VOID)
|
|
{
|
|
PKTHREAD Thread = KeGetCurrentThread();
|
|
|
|
/* Check if we need to inialize Win32 for this Thread */
|
|
if (Thread->ServiceTable != KeServiceDescriptorTableShadow) {
|
|
|
|
/* We do. Initialize it and save the new table */
|
|
PsInitWin32Thread((PETHREAD)Thread);
|
|
Thread->ServiceTable = KeServiceDescriptorTableShadow;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
* NOT EXPORTED
|
|
*/
|
|
NTSTATUS
|
|
STDCALL
|
|
NtAlertResumeThread(IN HANDLE ThreadHandle,
|
|
OUT PULONG SuspendCount)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PETHREAD Thread;
|
|
NTSTATUS Status;
|
|
ULONG PreviousState;
|
|
|
|
/* Check if parameters are valid */
|
|
if(PreviousMode != KernelMode) {
|
|
|
|
_SEH_TRY {
|
|
|
|
ProbeForWrite(SuspendCount,
|
|
sizeof(HANDLE),
|
|
sizeof(ULONG));
|
|
|
|
} _SEH_HANDLE {
|
|
|
|
Status = _SEH_GetExceptionCode();
|
|
|
|
} _SEH_END;
|
|
}
|
|
|
|
/* Reference the Object */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SUSPEND_RESUME,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
|
|
/* Check for Success */
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
/* Call the Kernel Function */
|
|
PreviousState = KeAlertResumeThread(&Thread->Tcb);
|
|
|
|
/* Dereference Object */
|
|
ObDereferenceObject(Thread);
|
|
|
|
if (SuspendCount) {
|
|
|
|
_SEH_TRY {
|
|
|
|
*SuspendCount = PreviousState;
|
|
|
|
} _SEH_HANDLE {
|
|
|
|
Status = _SEH_GetExceptionCode();
|
|
|
|
} _SEH_END;
|
|
}
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*
|
|
* EXPORTED
|
|
*/
|
|
NTSTATUS
|
|
STDCALL
|
|
NtAlertThread (IN HANDLE ThreadHandle)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PETHREAD Thread;
|
|
NTSTATUS Status;
|
|
|
|
/* Reference the Object */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SUSPEND_RESUME,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
|
|
/* Check for Success */
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
/*
|
|
* Do an alert depending on the processor mode. If some kmode code wants to
|
|
* enforce a umode alert it should call KeAlertThread() directly. If kmode
|
|
* code wants to do a kmode alert it's sufficient to call it with Zw or just
|
|
* use KeAlertThread() directly
|
|
*/
|
|
KeAlertThread(&Thread->Tcb, PreviousMode);
|
|
|
|
/* Dereference Object */
|
|
ObDereferenceObject(Thread);
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
STDCALL
|
|
NtDelayExecution(IN BOOLEAN Alertable,
|
|
IN PLARGE_INTEGER DelayInterval)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
LARGE_INTEGER SafeInterval;
|
|
NTSTATUS Status;
|
|
|
|
/* Check if parameters are valid */
|
|
if(PreviousMode != KernelMode) {
|
|
|
|
_SEH_TRY {
|
|
|
|
ProbeForRead(DelayInterval,
|
|
sizeof(LARGE_INTEGER),
|
|
sizeof(ULONG));
|
|
|
|
/* make a copy on the kernel stack and let DelayInterval point to it so
|
|
we don't need to wrap KeDelayExecutionThread in SEH! */
|
|
SafeInterval = *DelayInterval;
|
|
|
|
} _SEH_HANDLE {
|
|
|
|
Status = _SEH_GetExceptionCode();
|
|
} _SEH_END;
|
|
}
|
|
|
|
/* Call the Kernel Function */
|
|
Status = KeDelayExecutionThread(PreviousMode,
|
|
Alertable,
|
|
&SafeInterval);
|
|
|
|
/* Return Status */
|
|
return Status;
|
|
}
|