/*
- * ReactOS kernel
- * Copyright (C) 2000 ReactOS Team
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/* $Id: kthread.c,v 1.31 2002/08/09 17:23:57 dwelch Exp $
- *
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
* FILE: ntoskrnl/ke/kthread.c
* PURPOSE: Microkernel thread support
- * PROGRAMMER: David Welch (welch@cwcom.net)
- * UPDATE HISTORY:
- * Created 22/05/98
+ *
+ * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
+ * David Welch (welch@cwcom.net)
*/
/* INCLUDES *****************************************************************/
-#include <ddk/ntddk.h>
-#include <internal/ke.h>
-#include <internal/ps.h>
-#include <internal/id.h>
-#include <internal/pool.h>
-
+#include <ntoskrnl.h>
#define NDEBUG
#include <internal/debug.h>
-/* EXTERN ********************************************************************/
+#define THREAD_ALERT_INCREMENT 2
+
+extern EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
-//extern VOID
-//PiTimeoutThread(struct _KDPC Dpc, PVOID Context, PVOID Arg1, PVOID Arg2);
+/*
+ * 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
-KeFreeStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
- PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty)
+KiRequestReschedule(CCHAR Processor)
{
- assert(SwapEntry == 0);
- if (PhysAddr.QuadPart != 0)
- {
- MmReleasePageMemoryConsumer(MC_NPPOOL, PhysAddr);
+ 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->QueueListEntry);
+ PriorityListMask |= (1 << Priority);
}
-NTSTATUS
-KeReleaseThread(PETHREAD Thread)
+STATIC
+VOID
+KiRemoveFromThreadList(PKTHREAD Thread)
+{
+ ASSERT(Ready == Thread->State);
+ RemoveEntryList(&Thread->QueueListEntry);
+ 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, QueueListEntry);
+
+ 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 {
+
+ ULONG 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->ApcState.Process->ThreadQuantum;
+ }
+
+ 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)) {
+
+ ULONG 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
/*
- * FUNCTION: Releases the resource allocated for a thread by
- * KeInitializeThread
- * NOTE: The thread had better not be running when this is called
+ * @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;
+}
+
+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)
{
- extern unsigned int init_stack;
+ ULONG PreviousCount;
+ KIRQL OldIrql;
+
+ DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread, Thread->SuspendCount, Thread->FreezeCount);
- if (Thread->Tcb.StackLimit != (ULONG)&init_stack)
- {
- MmLockAddressSpace(MmGetKernelAddressSpace());
- MmFreeMemoryArea(MmGetKernelAddressSpace(),
- (PVOID)Thread->Tcb.StackLimit,
- MM_STACK_SIZE,
- KeFreeStackPage,
- NULL);
- MmUnlockAddressSpace(MmGetKernelAddressSpace());
+ /* 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);
+ }
}
- Thread->Tcb.StackLimit = 0;
- Thread->Tcb.InitialStack = NULL;
- Thread->Tcb.StackBase = NULL;
- Thread->Tcb.KernelStack = NULL;
- return(STATUS_SUCCESS);
+
+ /* Release Lock and return the Old State */
+ KeReleaseDispatcherDatabaseLock(OldIrql);
+ return PreviousCount;
}
-VOID
-KeInitializeThread(PKPROCESS Process, PKTHREAD Thread, BOOLEAN First)
+BOOLEAN
+STDCALL
+KiInsertQueueApc(PKAPC Apc,
+ KPRIORITY PriorityBoost);
+
/*
- * FUNCTION: Initialize the microkernel state of the thread
+ * Used by the debugging code to freeze all the process's threads
+ * while the debugger is examining their state.
*/
+VOID
+STDCALL
+KeFreezeAllThreads(PKPROCESS Process)
{
- PVOID KernelStack;
- NTSTATUS Status;
- extern unsigned int init_stack_top;
- extern unsigned int init_stack;
- PMEMORY_AREA StackArea;
- ULONG i;
-
- KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
- InternalThreadType,
- sizeof(ETHREAD),
- FALSE);
- InitializeListHead(&Thread->MutantListHead);
- if (!First)
+ 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)
{
- KernelStack = NULL;
-
- MmLockAddressSpace(MmGetKernelAddressSpace());
- Status = MmCreateMemoryArea(NULL,
- MmGetKernelAddressSpace(),
- MEMORY_AREA_KERNEL_STACK,
- &KernelStack,
- MM_STACK_SIZE,
- 0,
- &StackArea,
- FALSE);
- MmUnlockAddressSpace(MmGetKernelAddressSpace());
+ /* 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];
- if (!NT_SUCCESS(Status))
- {
- DPRINT1("Failed to create thread stack\n");
- KeBugCheck(0);
- }
- for (i = 0; i < (MM_STACK_SIZE / PAGESIZE); i++)
- {
- PHYSICAL_ADDRESS Page;
- Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page);
- if (!NT_SUCCESS(Status))
- {
- KeBugCheck(0);
- }
- Status = MmCreateVirtualMapping(NULL,
- KernelStack + (i * PAGESIZE),
- PAGE_EXECUTE_READWRITE,
- Page,
- TRUE);
- }
- Thread->InitialStack = KernelStack + MM_STACK_SIZE;
- Thread->StackBase = KernelStack + MM_STACK_SIZE;
- Thread->StackLimit = (ULONG)KernelStack;
- Thread->KernelStack = KernelStack + MM_STACK_SIZE;
- }
- else
- {
- Thread->InitialStack = (PVOID)&init_stack_top;
- Thread->StackBase = (PVOID)&init_stack_top;
- Thread->StackLimit = (ULONG)&init_stack;
- Thread->KernelStack = (PVOID)&init_stack_top;
- }
-
- /*
- * The Native API function will initialize the TEB field later
- */
- Thread->Teb = NULL;
- Thread->TlsArray = NULL;
- Thread->DebugActive = 0;
- Thread->State = THREAD_STATE_INITIALIZED;
- Thread->Alerted[0] = 0;
- Thread->Alerted[1] = 0;
- Thread->Iopl = 0;
- /*
- * FIXME: Think how this might work
- */
- Thread->NpxState = 0;
-
- Thread->Saturation = 0;
- Thread->Priority = 0;
- InitializeListHead(&Thread->ApcState.ApcListHead[0]);
- InitializeListHead(&Thread->ApcState.ApcListHead[1]);
- Thread->ApcState.Process = Process;
- Thread->ApcState.KernelApcInProgress = 0;
- Thread->ApcState.KernelApcPending = 0;
- Thread->ApcState.UserApcPending = 0;
- Thread->ContextSwitches = 0;
- Thread->WaitStatus = STATUS_SUCCESS;
- Thread->WaitIrql = 0;
- Thread->WaitMode = 0;
- Thread->WaitNext = 0;
- Thread->WaitBlockList = NULL;
- Thread->WaitListEntry.Flink = NULL;
- Thread->WaitListEntry.Blink = NULL;
- Thread->WaitTime = 0;
- Thread->BasePriority = 0;
- Thread->DecrementCount = 0;
- Thread->PriorityDecrement = 0;
- Thread->Quantum = 0;
- memset(Thread->WaitBlock, 0, sizeof(KWAIT_BLOCK)*4);
- Thread->LegoData = 0;
- /*
- * FIXME: Why this?
- */
- Thread->KernelApcDisable = 1;
- Thread->UserAffinity = Process->Affinity;
- Thread->SystemAffinityActive = 0;
- Thread->Queue = NULL;
- KeInitializeSpinLock(&Thread->ApcQueueLock);
- memset(&Thread->Timer, 0, sizeof(KTIMER));
- Thread->QueueListEntry.Flink = NULL;
- Thread->QueueListEntry.Blink = NULL;
- Thread->Affinity = Process->Affinity;
- Thread->Preempted = 0;
- Thread->ProcessReadyQueue = 0;
- Thread->KernelStackResident = 1;
- Thread->NextProcessor = 0;
- Thread->CallbackStack = NULL;
- Thread->Win32Thread = 0;
- Thread->TrapFrame = NULL;
- Thread->ApcStatePointer[0] = NULL;
- Thread->ApcStatePointer[1] = NULL;
- Thread->EnableStackSwap = 0;
- Thread->LargeStack = 0;
- Thread->ResourceIndex = 0;
- Thread->PreviousMode = KernelMode;
- Thread->KernelTime = 0;
- Thread->UserTime = 0;
- memset(&Thread->SavedApcState, 0, sizeof(KAPC_STATE));
- Thread->Alertable = 1;
- Thread->ApcStateIndex = 0;
- Thread->ApcQueueable = 0;
- Thread->AutoAlignment = 0;
- KeInitializeApc(&Thread->SuspendApc,
- Thread,
- 0,
- PiSuspendThreadKernelRoutine,
- PiSuspendThreadRundownRoutine,
- PiSuspendThreadNormalRoutine,
- KernelMode,
- NULL);
- KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 128);
- Thread->ThreadListEntry.Flink = NULL;
- Thread->ThreadListEntry.Blink = NULL;
- Thread->FreezeCount = 0;
- Thread->SuspendCount = 0;
-
- /*
- * Initialize ReactOS specific members
- */
- Thread->ProcessThreadListEntry.Flink = NULL;
- Thread->ProcessThreadListEntry.Blink = NULL;
-
- /*
- * Do x86 specific part
- */
+ /* 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->ThreadQuantum;
+ 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;
+ ULONG 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()->ProcessorNumber;
+ 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;
+}