-/*\r
- * COPYRIGHT: See COPYING in the top level directory\r
- * PROJECT: ReactOS kernel\r
- * FILE: ntoskrnl/ps/thread.c\r
- * PURPOSE: Thread managment\r
- * PROGRAMMER: David Welch (welch@mcmail.com)\r
- * REVISION HISTORY: \r
- * 23/06/98: Created\r
- */\r
-\r
-/*\r
- * NOTE:\r
- * \r
- * All of the routines that manipulate the thread queue synchronize on\r
- * a single spinlock\r
- * \r
- */\r
-\r
-/* INCLUDES ****************************************************************/\r
-\r
-#include <windows.h>\r
-#include <ddk/ntddk.h>\r
-#include <internal/ke.h>\r
-#include <internal/ob.h>\r
-#include <internal/string.h>\r
-#include <internal/hal.h>\r
-#include <internal/ps.h>\r
-\r
-#define NDEBUG\r
-#include <internal/debug.h>\r
-\r
-/* TYPES *******************************************************************/\r
-\r
-/* GLOBALS ******************************************************************/\r
-\r
-POBJECT_TYPE PsThreadType = NULL;\r
-\r
-#define NR_THREAD_PRIORITY_LEVELS (31)\r
-#define THREAD_PRIORITY_MAX (15)\r
-\r
-static KSPIN_LOCK ThreadListLock = {0,};\r
-\r
-/*\r
- * PURPOSE: List of threads associated with each priority level\r
- */\r
-static LIST_ENTRY PriorityListHead[NR_THREAD_PRIORITY_LEVELS]={{NULL,NULL},};\r
-static BOOLEAN DoneInitYet = FALSE;\r
-ULONG PiNrThreads = 0;\r
-\r
-static PETHREAD CurrentThread = NULL;\r
-\r
-static ULONG NextThreadUniqueId = 0;\r
-\r
-/* FUNCTIONS ***************************************************************/\r
-\r
-PKTHREAD KeGetCurrentThread(VOID)\r
-{\r
- return(&(CurrentThread->Tcb));\r
-}\r
-\r
-PETHREAD PsGetCurrentThread(VOID)\r
-{\r
- return((PETHREAD)KeGetCurrentThread());\r
-}\r
-\r
-static VOID PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)\r
-{\r
- KIRQL oldlvl;\r
- \r
- DPRINT("PsInsertIntoThreadList(Priority %x, Thread %x)\n",Priority,\r
- Thread);\r
- \r
- KeAcquireSpinLock(&ThreadListLock,&oldlvl);\r
- InsertTailList(&PriorityListHead[THREAD_PRIORITY_MAX+Priority],\r
- &Thread->Tcb.Entry);\r
- KeReleaseSpinLock(&ThreadListLock,oldlvl);\r
-}\r
-\r
-VOID PsBeginThread(PKSTART_ROUTINE StartRoutine, PVOID StartContext)\r
-{\r
- NTSTATUS Ret;\r
- \r
- KeReleaseSpinLock(&ThreadListLock,PASSIVE_LEVEL);\r
- Ret = StartRoutine(StartContext);\r
- PsTerminateSystemThread(Ret);\r
- for(;;);\r
-}\r
-\r
-static PETHREAD PsScanThreadList(KPRIORITY Priority)\r
-{\r
- PLIST_ENTRY current_entry;\r
- PETHREAD current;\r
- PETHREAD oldest = NULL;\r
- ULONG oldest_time = 0;\r
- \r
-// DPRINT("PsScanThreadList(Priority %d)\n",Priority);\r
- \r
- current_entry = PriorityListHead[THREAD_PRIORITY_MAX+Priority].Flink;\r
- while (current_entry != &PriorityListHead[THREAD_PRIORITY_MAX+Priority])\r
- {\r
- current = CONTAINING_RECORD(current_entry,ETHREAD,Tcb.Entry);\r
- #if 0\r
- if (current->Tcb.ThreadState == THREAD_STATE_TERMINATED &&\r
- current != CurrentThread)\r
- {\r
- PsReleaseThread(CurrentThread);\r
- }\r
- #endif\r
- if (current->Tcb.ThreadState == THREAD_STATE_RUNNABLE)\r
- {\r
- if (oldest == NULL || oldest_time > current->Tcb.LastTick)\r
- {\r
- oldest = current;\r
- oldest_time = current->Tcb.LastTick;\r
- }\r
- }\r
- current_entry = current_entry->Flink;\r
- }\r
-// DPRINT("PsScanThreadList() = %x\n",oldest);\r
- return(oldest);\r
-}\r
-\r
-VOID PsDispatchThread(VOID)\r
-{\r
- KPRIORITY CurrentPriority;\r
- PETHREAD Candidate;\r
- KIRQL irql;\r
- LARGE_INTEGER TickCount;\r
- \r
- KeAcquireSpinLock(&ThreadListLock,&irql);\r
- \r
- if (!DoneInitYet)\r
- {\r
- return;\r
- }\r
- \r
- DPRINT("PsDispatchThread() Current %x\n",CurrentThread);\r
- \r
- if (CurrentThread->Tcb.ThreadState==THREAD_STATE_RUNNING) \r
- {\r
- CurrentThread->Tcb.ThreadState=THREAD_STATE_RUNNABLE;\r
- }\r
- \r
- for (CurrentPriority=THREAD_PRIORITY_TIME_CRITICAL; \r
- CurrentPriority>=THREAD_PRIORITY_IDLE;\r
- CurrentPriority--)\r
- {\r
- Candidate = PsScanThreadList(CurrentPriority);\r
- if (Candidate == CurrentThread)\r
- {\r
-// DbgPrint("Scheduling current thread\n");\r
- KeQueryTickCount(&TickCount);\r
- CurrentThread->Tcb.LastTick = GET_LARGE_INTEGER_LOW_PART(TickCount);\r
- CurrentThread->Tcb.ThreadState = THREAD_STATE_RUNNING;\r
- KeReleaseSpinLock(&ThreadListLock,irql);\r
- return;\r
- }\r
- if (Candidate != NULL)\r
- { \r
-// DbgPrint("Scheduling %x\n",Candidate);\r
- \r
- Candidate->Tcb.ThreadState = THREAD_STATE_RUNNING;\r
- \r
- KeQueryTickCount(&TickCount);\r
- CurrentThread->Tcb.LastTick = GET_LARGE_INTEGER_LOW_PART(TickCount);\r
- \r
- CurrentThread = Candidate;\r
- \r
- HalTaskSwitch(&CurrentThread->Tcb);\r
- KeReleaseSpinLock(&ThreadListLock,irql);\r
- return;\r
- }\r
- }\r
-}\r
-\r
-NTSTATUS PsInitializeThread(HANDLE ProcessHandle, \r
- PETHREAD* ThreadPtr,\r
- PHANDLE ThreadHandle,\r
- ACCESS_MASK DesiredAccess,\r
- POBJECT_ATTRIBUTES ThreadAttributes)\r
-{\r
- ULONG ThreadId;\r
- ULONG ProcessId;\r
- PETHREAD Thread;\r
- NTSTATUS Status;\r
- \r
- PiNrThreads++;\r
- \r
- Thread = ObGenericCreateObject(ThreadHandle,\r
- DesiredAccess,\r
- ThreadAttributes,\r
- PsThreadType);\r
- DPRINT("Thread = %x\n",Thread);\r
- Thread->Tcb.LastTick = 0;\r
- Thread->Tcb.ThreadState=THREAD_STATE_SUSPENDED;\r
- Thread->Tcb.BasePriority=THREAD_PRIORITY_NORMAL;\r
- Thread->Tcb.CurrentPriority=THREAD_PRIORITY_NORMAL;\r
- Thread->Tcb.ApcList=ExAllocatePool(NonPagedPool,sizeof(LIST_ENTRY));\r
- Thread->Tcb.SuspendCount = 1;\r
- if (ProcessHandle!=NULL)\r
- {\r
- Status = ObReferenceObjectByHandle(ProcessHandle,\r
- PROCESS_CREATE_THREAD,\r
- PsProcessType,\r
- UserMode,\r
- (PVOID*)&Thread->ThreadsProcess,\r
- NULL);\r
- if (Status != STATUS_SUCCESS)\r
- {\r
- return(Status);\r
- }\r
- }\r
- else\r
- {\r
- Thread->ThreadsProcess=SystemProcess;\r
- ObReferenceObjectByPointer(Thread->ThreadsProcess,\r
- PROCESS_CREATE_THREAD,\r
- PsProcessType,\r
- UserMode);\r
- }\r
- ObReferenceObjectByPointer(Thread->ThreadsProcess,\r
- PROCESS_CREATE_THREAD,\r
- PsProcessType,\r
- UserMode);\r
- InitializeListHead(Thread->Tcb.ApcList);\r
- InitializeListHead(&(Thread->IrpList));\r
- Thread->Cid.UniqueThread=InterlockedIncrement(&NextThreadUniqueId);\r
- PsInsertIntoThreadList(Thread->Tcb.CurrentPriority,Thread);\r
- \r
- *ThreadPtr = Thread;\r
- \r
- ObDereferenceObject(Thread->ThreadsProcess); \r
- return(STATUS_SUCCESS);\r
-}\r
-\r
-VOID PsResumeThread(PETHREAD Thread)\r
-{\r
- DPRINT("PsResumeThread(Thread %x)\n",Thread);\r
- \r
- Thread->Tcb.SuspendCount--;\r
- DPRINT("Thread->Tcb.SuspendCount %d\n",Thread->Tcb.SuspendCount);\r
- DPRINT("Thread->Tcb.ThreadState %d THREAD_STATE_RUNNING %d\n",\r
- Thread->Tcb.ThreadState,THREAD_STATE_RUNNING);\r
- if (Thread->Tcb.SuspendCount <= 0 && \r
- Thread->Tcb.ThreadState != THREAD_STATE_RUNNING)\r
- {\r
- DPRINT("Setting thread to runnable\n");\r
- Thread->Tcb.ThreadState = THREAD_STATE_RUNNABLE;\r
- }\r
- DPRINT("Finished PsResumeThread()\n");\r
-}\r
-\r
-VOID PsSuspendThread(PETHREAD Thread)\r
-{\r
- DPRINT("PsSuspendThread(Thread %x)\n",Thread);\r
- Thread->Tcb.SuspendCount++;\r
- if (Thread->Tcb.SuspendCount > 0)\r
- {\r
- Thread->Tcb.ThreadState = THREAD_STATE_SUSPENDED;\r
- if (Thread == CurrentThread)\r
- {\r
- PsDispatchThread();\r
- }\r
- }\r
-}\r
-\r
-void PsInitThreadManagment(void)\r
-/*\r
- * FUNCTION: Initialize thread managment\r
- */\r
-{\r
- PETHREAD FirstThread;\r
- ULONG i;\r
- ANSI_STRING AnsiString;\r
- HANDLE FirstThreadHandle;\r
- \r
- KeInitializeSpinLock(&ThreadListLock);\r
- for (i=0; i<NR_THREAD_PRIORITY_LEVELS; i++)\r
- {\r
- InitializeListHead(&PriorityListHead[i]);\r
- }\r
- \r
- PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));\r
- \r
- RtlInitAnsiString(&AnsiString,"Thread");\r
- RtlAnsiStringToUnicodeString(&PsThreadType->TypeName,&AnsiString,TRUE);\r
- \r
- PsThreadType->TotalObjects = 0;\r
- PsThreadType->TotalHandles = 0;\r
- PsThreadType->MaxObjects = 0;\r
- PsThreadType->MaxHandles = 0;\r
- PsThreadType->PagedPoolCharge = 0;\r
- PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);\r
- PsThreadType->Dump = NULL;\r
- PsThreadType->Open = NULL;\r
- PsThreadType->Close = NULL;\r
- PsThreadType->Delete = NULL;\r
- PsThreadType->Parse = NULL;\r
- PsThreadType->Security = NULL;\r
- PsThreadType->QueryName = NULL;\r
- PsThreadType->OkayToClose = NULL;\r
- \r
- PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,\r
- THREAD_ALL_ACCESS,NULL);\r
- HalInitFirstTask(FirstThread);\r
- FirstThread->Tcb.ThreadState = THREAD_STATE_RUNNING;\r
- FirstThread->Tcb.SuspendCount = 0;\r
-\r
- DPRINT("FirstThread %x\n",FirstThread);\r
- \r
- CurrentThread = FirstThread;\r
- \r
- DoneInitYet = TRUE;\r
-}\r
-\r
-NTSTATUS NtCreateThread(PHANDLE ThreadHandle,\r
- ACCESS_MASK DesiredAccess,\r
- POBJECT_ATTRIBUTES ObjectAttributes,\r
- HANDLE ProcessHandle,\r
- PCLIENT_ID Client,\r
- PCONTEXT ThreadContext,\r
- PINITIAL_TEB InitialTeb,\r
- BOOLEAN CreateSuspended)\r
-{\r
- return(ZwCreateThread(ThreadHandle,\r
- DesiredAccess,\r
- ObjectAttributes,\r
- ProcessHandle,\r
- Client,\r
- ThreadContext,\r
- InitialTeb,\r
- CreateSuspended));\r
-}\r
-\r
-NTSTATUS ZwCreateThread(PHANDLE ThreadHandle,\r
- ACCESS_MASK DesiredAccess,\r
- POBJECT_ATTRIBUTES ObjectAttributes,\r
- HANDLE ProcessHandle,\r
- PCLIENT_ID Client,\r
- PCONTEXT ThreadContext,\r
- PINITIAL_TEB InitialTeb,\r
- BOOLEAN CreateSuspended)\r
-{\r
- PETHREAD Thread;\r
- NTSTATUS Status;\r
- \r
- DbgPrint("ZwCreateThread(ThreadHandle %x, PCONTEXT %x)\n",\r
- ThreadHandle,ThreadContext);\r
- \r
- Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,\r
- DesiredAccess,ObjectAttributes);\r
- if (Status != STATUS_SUCCESS)\r
- {\r
- return(Status);\r
- }\r
- \r
- HalInitTaskWithContext(Thread,ThreadContext);\r
- Thread->StartAddress=NULL;\r
-\r
- if (Client!=NULL)\r
- {\r
- *Client=Thread->Cid;\r
- } \r
- \r
- if (!CreateSuspended)\r
- {\r
- DPRINT("Not creating suspended\n");\r
- PsResumeThread(Thread);\r
- }\r
- DPRINT("Finished PsCreateThread()\n");\r
- return(STATUS_SUCCESS);\r
-}\r
-\r
-NTSTATUS PsCreateSystemThread(PHANDLE ThreadHandle,\r
- ACCESS_MASK DesiredAccess,\r
- POBJECT_ATTRIBUTES ObjectAttributes,\r
- HANDLE ProcessHandle,\r
- PCLIENT_ID ClientId,\r
- PKSTART_ROUTINE StartRoutine,\r
- PVOID StartContext)\r
-/*\r
- * FUNCTION: Creates a thread which executes in kernel mode\r
- * ARGUMENTS:\r
- * ThreadHandle (OUT) = Caller supplied storage for the returned thread \r
- * handle\r
- * DesiredAccess = Requested access to the thread\r
- * ObjectAttributes = Object attributes (optional)\r
- * ProcessHandle = Handle of process thread will run in\r
- * NULL to use system process\r
- * ClientId (OUT) = Caller supplied storage for the returned client id\r
- * of the thread (optional)\r
- * StartRoutine = Entry point for the thread\r
- * StartContext = Argument supplied to the thread when it begins\r
- * execution\r
- * RETURNS: Success or failure status\r
- */\r
-{\r
- PETHREAD Thread;\r
- NTSTATUS Status;\r
- \r
- DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",\r
- ThreadHandle,ProcessHandle);\r
- \r
- Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,\r
- DesiredAccess,ObjectAttributes);\r
- if (Status != STATUS_SUCCESS)\r
- {\r
- return(Status);\r
- }\r
- \r
- Thread->StartAddress=StartRoutine;\r
- HalInitTask(Thread,StartRoutine,StartContext);\r
-\r
- if (ClientId!=NULL)\r
- {\r
- *ClientId=Thread->Cid;\r
- } \r
-\r
- PsResumeThread(Thread);\r
- \r
- return(STATUS_SUCCESS);\r
-}\r
-\r
-LONG KeSetBasePriorityThread(PKTHREAD Thread, LONG Increment)\r
-{\r
- UNIMPLEMENTED;\r
-}\r
-\r
-KPRIORITY KeSetPriorityThread(PKTHREAD Thread, KPRIORITY Priority)\r
-{\r
- KPRIORITY OldPriority;\r
- OldPriority = Thread->CurrentPriority;\r
- Thread->CurrentPriority = Priority;\r
-\r
- RemoveEntryList(&Thread->Entry);\r
- PsInsertIntoThreadList(Thread->CurrentPriority,\r
- CONTAINING_RECORD(Thread,ETHREAD,Tcb));\r
- \r
- return(OldPriority);\r
-}\r
-\r
-NTSTATUS STDCALL NtAlertResumeThread(IN HANDLE ThreadHandle,\r
- OUT PULONG SuspendCount)\r
-{\r
- return(ZwAlertResumeThread(ThreadHandle,SuspendCount));\r
-}\r
-\r
-NTSTATUS STDCALL ZwAlertResumeThread(IN HANDLE ThreadHandle,\r
- OUT PULONG SuspendCount)\r
-{\r
- UNIMPLEMENTED;\r
-}\r
-\r
-NTSTATUS STDCALL NtAlertThread(IN HANDLE ThreadHandle)\r
-{\r
- return(ZwAlertThread(ThreadHandle));\r
-}\r
-\r
-NTSTATUS STDCALL ZwAlertThread(IN HANDLE ThreadHandle)\r
-{\r
- UNIMPLEMENTED;\r
-}\r
-\r
-NTSTATUS STDCALL NtGetContextThread(IN HANDLE ThreadHandle, \r
- OUT PCONTEXT Context)\r
-{\r
- return(ZwGetContextThread(ThreadHandle,Context));\r
-}\r
-\r
-NTSTATUS STDCALL ZwGetContextThread(IN HANDLE ThreadHandle, \r
- OUT PCONTEXT Context)\r
-{\r
- UNIMPLEMENTED;\r
-}\r
-\r
-NTSTATUS STDCALL NtOpenThread(OUT PHANDLE ThreadHandle,\r
- IN ACCESS_MASK DesiredAccess,\r
- IN POBJECT_ATTRIBUTES ObjectAttributes,\r
- IN PCLIENT_ID ClientId)\r
-{\r
- return(ZwOpenThread(ThreadHandle,\r
- DesiredAccess,\r
- ObjectAttributes,\r
- ClientId));\r
-}\r
-\r
-NTSTATUS STDCALL ZwOpenThread(OUT PHANDLE ThreadHandle,\r
- IN ACCESS_MASK DesiredAccess,\r
- IN POBJECT_ATTRIBUTES ObjectAttributes,\r
- IN PCLIENT_ID ClientId)\r
-{\r
- UNIMPLEMENTED;\r
-}\r
-\r
-NTSTATUS STDCALL NtResumeThread(IN HANDLE ThreadHandle,\r
- IN PULONG SuspendCount)\r
-{\r
- return(ZwResumeThread(ThreadHandle,SuspendCount));\r
-}\r
-\r
-NTSTATUS STDCALL ZwResumeThread(IN HANDLE ThreadHandle,\r
- IN PULONG SuspendCount)\r
-/*\r
- * FUNCTION: Decrements a thread's resume count\r
- * ARGUMENTS: \r
- * ThreadHandle = Handle to the thread that should be resumed\r
- * ResumeCount = The resulting resume count.\r
- * REMARK:\r
- * A thread is resumed if its suspend count is 0. This procedure maps to\r
- * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )\r
- * RETURNS: Status\r
- */\r
-{\r
- PETHREAD Thread;\r
- NTSTATUS Status;\r
- \r
- Status = ObReferenceObjectByHandle(ThreadHandle,\r
- THREAD_SUSPEND_RESUME,\r
- PsThreadType,\r
- UserMode,\r
- (PVOID*)&Thread,\r
- NULL);\r
- if (Status != STATUS_SUCCESS)\r
- {\r
- return(Status);\r
- }\r
- \r
- (*SuspendCount) = InterlockedDecrement(&Thread->Tcb.SuspendCount);\r
- if (Thread->Tcb.SuspendCount <= 0)\r
- {\r
- Thread->Tcb.ThreadState = THREAD_STATE_RUNNABLE;\r
- }\r
- \r
- ObDereferenceObject(Thread);\r
- return(STATUS_SUCCESS);\r
-}\r
-\r
-NTSTATUS STDCALL NtSetContextThread(IN HANDLE ThreadHandle,\r
- IN PCONTEXT Context)\r
-{\r
- return(ZwSetContextThread(ThreadHandle,Context));\r
-}\r
-\r
-NTSTATUS STDCALL ZwSetContextThread(IN HANDLE ThreadHandle,\r
- IN PCONTEXT Context)\r
-{\r
- UNIMPLEMENTED;\r
-}\r
-\r
-NTSTATUS STDCALL NtSuspendThread(IN HANDLE ThreadHandle,\r
- IN PULONG PreviousSuspendCount)\r
-{\r
- return(ZwSuspendThread(ThreadHandle,PreviousSuspendCount));\r
-}\r
-\r
-NTSTATUS STDCALL ZwSuspendThread(IN HANDLE ThreadHandle,\r
- IN PULONG PreviousSuspendCount)\r
-/*\r
- * FUNCTION: Increments a thread's suspend count\r
- * ARGUMENTS: \r
- * ThreadHandle = Handle to the thread that should be resumed\r
- * PreviousSuspendCount = The resulting/previous suspend count.\r
- * REMARK:\r
- * A thread will be suspended if its suspend count is greater than 0. \r
- * This procedure maps to the win32 SuspendThread function. ( \r
- * documentation about the the suspend count can be found here aswell )\r
- * The suspend count is not increased if it is greater than \r
- * MAXIMUM_SUSPEND_COUNT.\r
- * RETURNS: Status\r
- */ \r
-{\r
- PETHREAD Thread;\r
- NTSTATUS Status;\r
- \r
- Status = ObReferenceObjectByHandle(ThreadHandle,\r
- THREAD_SUSPEND_RESUME,\r
- PsThreadType,\r
- UserMode,\r
- (PVOID*)&Thread,\r
- NULL);\r
- if (Status != STATUS_SUCCESS)\r
- {\r
- return(Status);\r
- }\r
- \r
- (*PreviousSuspendCount) = InterlockedIncrement(&Thread->Tcb.SuspendCount);\r
- if (Thread->Tcb.SuspendCount > 0)\r
- {\r
- Thread->Tcb.ThreadState = THREAD_STATE_SUSPENDED;\r
- if (Thread == PsGetCurrentThread())\r
- {\r
- PsDispatchThread();\r
- }\r
- }\r
- \r
- ObDereferenceObject(Thread);\r
- return(STATUS_SUCCESS);\r
-}\r
-\r
-NTSTATUS STDCALL NtContinue(IN PCONTEXT Context, IN CINT IrqLevel)\r
-{\r
- return(ZwContinue(Context,IrqLevel));\r
-}\r
-\r
-NTSTATUS STDCALL ZwContinue(IN PCONTEXT Context, IN CINT IrqLevel)\r
-{\r
- UNIMPLEMENTED;\r
-}\r
-\r
-NTSTATUS STDCALL NtYieldExecution(VOID)\r
-{\r
- return(ZwYieldExecution());\r
-}\r
-\r
-NTSTATUS STDCALL ZwYieldExecution(VOID)\r
-{\r
- PsDispatchThread();\r
- return(STATUS_SUCCESS);\r
-}\r
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
+ * FILE: ntoskrnl/ps/thread.c
+ * PURPOSE: Thread managment
+ * PROGRAMMER: David Welch (welch@mcmail.com)
+ * REVISION HISTORY:
+ * 23/06/98: Created
+ */
+
+/*
+ * NOTE:
+ *
+ * All of the routines that manipulate the thread queue synchronize on
+ * a single spinlock
+ *
+ */
+
+/* INCLUDES ****************************************************************/
+
+#include <windows.h>
+#include <ddk/ntddk.h>
+#include <internal/ke.h>
+#include <internal/ob.h>
+#include <string.h>
+#include <internal/string.h>
+#include <internal/hal.h>
+#include <internal/ps.h>
+#include <internal/ob.h>
+
+#define NDEBUG
+#include <internal/debug.h>
+
+/* TYPES *******************************************************************/
+
+/* GLOBALS ******************************************************************/
+
+POBJECT_TYPE PsThreadType = NULL;
+
+#define NR_THREAD_PRIORITY_LEVELS (31)
+#define THREAD_PRIORITY_MAX (15)
+
+static KSPIN_LOCK ThreadListLock = {0,};
+
+/*
+ * PURPOSE: List of threads associated with each priority level
+ */
+static LIST_ENTRY PriorityListHead[NR_THREAD_PRIORITY_LEVELS]={{NULL,NULL},};
+static BOOLEAN DoneInitYet = FALSE;
+ULONG PiNrThreads = 0;
+ULONG PiNrRunnableThreads = 0;
+
+static PETHREAD CurrentThread = NULL;
+
+static ULONG NextUniqueThreadId = 0;
+
+/* FUNCTIONS ***************************************************************/
+
+PKTHREAD KeGetCurrentThread(VOID)
+{
+ return(&(CurrentThread->Tcb));
+}
+
+PETHREAD PsGetCurrentThread(VOID)
+{
+ return(CurrentThread);
+}
+
+VOID PiTerminateProcessThreads(PEPROCESS Process, NTSTATUS ExitStatus)
+{
+ KIRQL oldlvl;
+ PLIST_ENTRY current_entry;
+ PETHREAD current;
+ ULONG i;
+
+ KeAcquireSpinLock(&ThreadListLock, &oldlvl);
+
+ for (i=0; i<NR_THREAD_PRIORITY_LEVELS; i++)
+ {
+ current_entry = PriorityListHead[i].Flink;
+ while (current_entry != &PriorityListHead[i])
+ {
+ current = CONTAINING_RECORD(current_entry,ETHREAD,Tcb.Entry);
+ if (current->ThreadsProcess == Process &&
+ current != PsGetCurrentThread())
+ {
+ PsTerminateOtherThread(current, ExitStatus);
+ }
+ current_entry = current_entry->Flink;
+ }
+ }
+
+ KeReleaseSpinLock(&ThreadListLock, oldlvl);
+}
+
+static VOID PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)
+{
+ KIRQL oldlvl;
+
+ DPRINT("PsInsertIntoThreadList(Priority %x, Thread %x)\n",Priority,
+ Thread);
+
+ KeAcquireSpinLock(&ThreadListLock,&oldlvl);
+ InsertTailList(&PriorityListHead[THREAD_PRIORITY_MAX+Priority],
+ &Thread->Tcb.Entry);
+ KeReleaseSpinLock(&ThreadListLock,oldlvl);
+}
+
+VOID PsBeginThread(PKSTART_ROUTINE StartRoutine, PVOID StartContext)
+{
+ NTSTATUS Ret;
+
+ KeReleaseSpinLock(&ThreadListLock,PASSIVE_LEVEL);
+ Ret = StartRoutine(StartContext);
+ PsTerminateSystemThread(Ret);
+ KeBugCheck(0);
+}
+
+static PETHREAD PsScanThreadList(KPRIORITY Priority)
+{
+ PLIST_ENTRY current_entry;
+ PETHREAD current;
+ PETHREAD oldest = NULL;
+ ULONG oldest_time = 0;
+
+// DPRINT("PsScanThreadList(Priority %d)\n",Priority);
+
+ current_entry = PriorityListHead[THREAD_PRIORITY_MAX+Priority].Flink;
+ while (current_entry != &PriorityListHead[THREAD_PRIORITY_MAX+Priority])
+ {
+ current = CONTAINING_RECORD(current_entry,ETHREAD,Tcb.Entry);
+
+ if (current->Tcb.ThreadState == THREAD_STATE_TERMINATED &&
+ current != CurrentThread)
+ {
+ PsReleaseThread(current);
+ }
+
+ if (current->Tcb.ThreadState == THREAD_STATE_RUNNABLE)
+ {
+ if (oldest == NULL || oldest_time > current->Tcb.LastTick)
+ {
+ oldest = current;
+ oldest_time = current->Tcb.LastTick;
+ }
+ }
+ current_entry = current_entry->Flink;
+ }
+// DPRINT("PsScanThreadList() = %x\n",oldest);
+ return(oldest);
+}
+
+VOID PsDispatchThread(VOID)
+{
+ KPRIORITY CurrentPriority;
+ PETHREAD Candidate;
+ KIRQL irql;
+ LARGE_INTEGER TickCount;
+
+ KeAcquireSpinLock(&ThreadListLock,&irql);
+
+ if (!DoneInitYet)
+ {
+ return;
+ }
+
+ DPRINT("PsDispatchThread() Current %x\n",CurrentThread);
+
+ if (CurrentThread->Tcb.ThreadState==THREAD_STATE_RUNNING)
+ {
+ CurrentThread->Tcb.ThreadState=THREAD_STATE_RUNNABLE;
+ }
+
+ for (CurrentPriority=THREAD_PRIORITY_TIME_CRITICAL;
+ CurrentPriority>=THREAD_PRIORITY_IDLE;
+ CurrentPriority--)
+ {
+ Candidate = PsScanThreadList(CurrentPriority);
+ if (Candidate == CurrentThread)
+ {
+ DPRINT("Scheduling current thread\n");
+ KeQueryTickCount(&TickCount);
+ CurrentThread->Tcb.LastTick = TickCount.LowPart;
+ CurrentThread->Tcb.ThreadState = THREAD_STATE_RUNNING;
+ KeReleaseSpinLock(&ThreadListLock,irql);
+ return;
+ }
+ if (Candidate != NULL)
+ {
+ DPRINT("Scheduling %x\n",Candidate);
+
+ Candidate->Tcb.ThreadState = THREAD_STATE_RUNNING;
+
+ KeQueryTickCount(&TickCount);
+ CurrentThread->Tcb.LastTick = TickCount.LowPart;
+
+ CurrentThread = Candidate;
+
+ HalTaskSwitch(&CurrentThread->Tcb);
+ KeReleaseSpinLock(&ThreadListLock,irql);
+ return;
+ }
+ }
+ DbgPrint("CRITICAL: No threads are runnable\n");
+ KeBugCheck(0);
+}
+
+NTSTATUS PsInitializeThread(HANDLE ProcessHandle,
+ PETHREAD* ThreadPtr,
+ PHANDLE ThreadHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ThreadAttributes)
+{
+ PETHREAD Thread;
+ NTSTATUS Status;
+
+ PiNrThreads++;
+
+ Thread = ObCreateObject(ThreadHandle,
+ DesiredAccess,
+ ThreadAttributes,
+ PsThreadType);
+ DPRINT("Thread = %x\n",Thread);
+ Thread->Tcb.LastTick = 0;
+ Thread->Tcb.ThreadState=THREAD_STATE_SUSPENDED;
+ Thread->Tcb.BasePriority=THREAD_PRIORITY_NORMAL;
+ Thread->Tcb.CurrentPriority=THREAD_PRIORITY_NORMAL;
+ Thread->Tcb.ApcList=ExAllocatePool(NonPagedPool,sizeof(LIST_ENTRY));
+ Thread->Tcb.SuspendCount = 1;
+ if (ProcessHandle != NULL)
+ {
+ Status = ObReferenceObjectByHandle(ProcessHandle,
+ PROCESS_CREATE_THREAD,
+ PsProcessType,
+ UserMode,
+ (PVOID*)&Thread->ThreadsProcess,
+ NULL);
+ if (Status != STATUS_SUCCESS)
+ {
+ DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
+ return(Status);
+ }
+ }
+ else
+ {
+ Thread->ThreadsProcess = SystemProcess;
+ ObReferenceObjectByPointer(Thread->ThreadsProcess,
+ PROCESS_CREATE_THREAD,
+ PsProcessType,
+ UserMode);
+ }
+ ObReferenceObjectByPointer(Thread->ThreadsProcess,
+ PROCESS_CREATE_THREAD,
+ PsProcessType,
+ UserMode);
+ InitializeListHead(Thread->Tcb.ApcList);
+ InitializeListHead(&(Thread->IrpList));
+ Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
+ &NextUniqueThreadId);
+ Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
+ DbgPrint("Thread->Cid.UniqueThread %d\nThread->Cid.UniqueProcess %d\n",
+ Thread->Cid.UniqueThread, Thread->Cid.UniqueThread);
+ ObReferenceObjectByPointer(Thread,
+ THREAD_ALL_ACCESS,
+ PsThreadType,
+ UserMode);
+ PsInsertIntoThreadList(Thread->Tcb.CurrentPriority,Thread);
+
+ *ThreadPtr = Thread;
+
+ ObDereferenceObject(Thread->ThreadsProcess);
+ return(STATUS_SUCCESS);
+}
+
+VOID PsResumeThread(PETHREAD Thread)
+{
+ DPRINT("PsResumeThread(Thread %x)\n",Thread);
+ Thread->Tcb.SuspendCount--;
+ if (Thread->Tcb.SuspendCount <= 0 &&
+ Thread->Tcb.ThreadState != THREAD_STATE_RUNNING)
+ {
+ DPRINT("Setting thread to runnable\n");
+ Thread->Tcb.ThreadState = THREAD_STATE_RUNNABLE;
+ }
+ DPRINT("Finished PsResumeThread()\n");
+}
+
+VOID PsSuspendThread(PETHREAD Thread)
+{
+ DPRINT("PsSuspendThread(Thread %x)\n",Thread);
+ Thread->Tcb.SuspendCount++;
+ if (Thread->Tcb.SuspendCount > 0)
+ {
+ Thread->Tcb.ThreadState = THREAD_STATE_SUSPENDED;
+ if (Thread == CurrentThread)
+ {
+ PsDispatchThread();
+ }
+ }
+}
+
+VOID PiDeleteThread(PVOID ObjectBody)
+{
+ DbgPrint("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
+}
+
+VOID PsInitThreadManagment(VOID)
+/*
+ * FUNCTION: Initialize thread managment
+ */
+{
+ PETHREAD FirstThread;
+ ULONG i;
+ ANSI_STRING AnsiString;
+ HANDLE FirstThreadHandle;
+
+ KeInitializeSpinLock(&ThreadListLock);
+ for (i=0; i<NR_THREAD_PRIORITY_LEVELS; i++)
+ {
+ InitializeListHead(&PriorityListHead[i]);
+ }
+
+ PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
+
+ RtlInitAnsiString(&AnsiString,"Thread");
+ RtlAnsiStringToUnicodeString(&PsThreadType->TypeName,&AnsiString,TRUE);
+
+ PsThreadType->TotalObjects = 0;
+ PsThreadType->TotalHandles = 0;
+ PsThreadType->MaxObjects = 0;
+ PsThreadType->MaxHandles = 0;
+ PsThreadType->PagedPoolCharge = 0;
+ PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
+ PsThreadType->Dump = NULL;
+ PsThreadType->Open = NULL;
+ PsThreadType->Close = NULL;
+ PsThreadType->Delete = PiDeleteThread;
+ PsThreadType->Parse = NULL;
+ PsThreadType->Security = NULL;
+ PsThreadType->QueryName = NULL;
+ PsThreadType->OkayToClose = NULL;
+
+ PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
+ THREAD_ALL_ACCESS,NULL);
+ HalInitFirstTask(FirstThread);
+ FirstThread->Tcb.ThreadState = THREAD_STATE_RUNNING;
+ FirstThread->Tcb.SuspendCount = 0;
+
+ DPRINT("FirstThread %x\n",FirstThread);
+
+ CurrentThread = FirstThread;
+
+ DoneInitYet = TRUE;
+}
+
+NTSTATUS NtCreateThread(PHANDLE ThreadHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes,
+ HANDLE ProcessHandle,
+ PCLIENT_ID Client,
+ PCONTEXT ThreadContext,
+ PINITIAL_TEB InitialTeb,
+ BOOLEAN CreateSuspended)
+{
+ return(ZwCreateThread(ThreadHandle,
+ DesiredAccess,
+ ObjectAttributes,
+ ProcessHandle,
+ Client,
+ ThreadContext,
+ InitialTeb,
+ CreateSuspended));
+}
+
+NTSTATUS ZwCreateThread(PHANDLE ThreadHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes,
+ HANDLE ProcessHandle,
+ PCLIENT_ID Client,
+ PCONTEXT ThreadContext,
+ PINITIAL_TEB InitialTeb,
+ BOOLEAN CreateSuspended)
+{
+ PETHREAD Thread;
+ NTSTATUS Status;
+
+ DPRINT("ZwCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
+ ThreadHandle,ThreadContext);
+
+ Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
+ DesiredAccess,ObjectAttributes);
+ if (!NT_SUCCESS(Status))
+ {
+ return(Status);
+ }
+
+ Status = HalInitTaskWithContext(Thread,ThreadContext);
+ if (!NT_SUCCESS(Status))
+ {
+ return(Status);
+ }
+ Thread->StartAddress=NULL;
+
+ if (Client!=NULL)
+ {
+ *Client=Thread->Cid;
+ }
+
+ if (!CreateSuspended)
+ {
+ DPRINT("Not creating suspended\n");
+ PsResumeThread(Thread);
+ }
+ DPRINT("Finished PsCreateThread()\n");
+ return(STATUS_SUCCESS);
+}
+
+NTSTATUS PsCreateSystemThread(PHANDLE ThreadHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes,
+ HANDLE ProcessHandle,
+ PCLIENT_ID ClientId,
+ PKSTART_ROUTINE StartRoutine,
+ PVOID StartContext)
+/*
+ * FUNCTION: Creates a thread which executes in kernel mode
+ * ARGUMENTS:
+ * ThreadHandle (OUT) = Caller supplied storage for the returned thread
+ * handle
+ * DesiredAccess = Requested access to the thread
+ * ObjectAttributes = Object attributes (optional)
+ * ProcessHandle = Handle of process thread will run in
+ * NULL to use system process
+ * ClientId (OUT) = Caller supplied storage for the returned client id
+ * of the thread (optional)
+ * StartRoutine = Entry point for the thread
+ * StartContext = Argument supplied to the thread when it begins
+ * execution
+ * RETURNS: Success or failure status
+ */
+{
+ PETHREAD Thread;
+ NTSTATUS Status;
+
+ DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
+ ThreadHandle,ProcessHandle);
+
+ Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
+ DesiredAccess,ObjectAttributes);
+ if (!NT_SUCCESS(Status))
+ {
+ return(Status);
+ }
+
+ Thread->StartAddress=StartRoutine;
+ Status = HalInitTask(Thread,StartRoutine,StartContext);
+ if (!NT_SUCCESS(Status))
+ {
+ return(Status);
+ }
+
+ if (ClientId!=NULL)
+ {
+ *ClientId=Thread->Cid;
+ }
+
+ PsResumeThread(Thread);
+
+ return(STATUS_SUCCESS);
+}
+
+LONG KeSetBasePriorityThread(PKTHREAD Thread, LONG Increment)
+{
+ UNIMPLEMENTED;
+}
+
+KPRIORITY KeSetPriorityThread(PKTHREAD Thread, KPRIORITY Priority)
+{
+ KPRIORITY OldPriority;
+ OldPriority = Thread->CurrentPriority;
+ Thread->CurrentPriority = Priority;
+
+ RemoveEntryList(&Thread->Entry);
+ PsInsertIntoThreadList(Thread->CurrentPriority,
+ CONTAINING_RECORD(Thread,ETHREAD,Tcb));
+
+ return(OldPriority);
+}
+
+NTSTATUS STDCALL NtAlertResumeThread(IN HANDLE ThreadHandle,
+ OUT PULONG SuspendCount)
+{
+ return(ZwAlertResumeThread(ThreadHandle,SuspendCount));
+}
+
+NTSTATUS STDCALL ZwAlertResumeThread(IN HANDLE ThreadHandle,
+ OUT PULONG SuspendCount)
+{
+ UNIMPLEMENTED;
+}
+
+NTSTATUS STDCALL NtAlertThread(IN HANDLE ThreadHandle)
+{
+ return(ZwAlertThread(ThreadHandle));
+}
+
+NTSTATUS STDCALL ZwAlertThread(IN HANDLE ThreadHandle)
+{
+ UNIMPLEMENTED;
+}
+
+NTSTATUS STDCALL NtGetContextThread(IN HANDLE ThreadHandle,
+ OUT PCONTEXT Context)
+{
+ return(ZwGetContextThread(ThreadHandle,Context));
+}
+
+NTSTATUS STDCALL ZwGetContextThread(IN HANDLE ThreadHandle,
+ OUT PCONTEXT Context)
+{
+ UNIMPLEMENTED;
+}
+
+NTSTATUS STDCALL NtOpenThread(OUT PHANDLE ThreadHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN PCLIENT_ID ClientId)
+{
+ return(ZwOpenThread(ThreadHandle,
+ DesiredAccess,
+ ObjectAttributes,
+ ClientId));
+}
+
+NTSTATUS STDCALL ZwOpenThread(OUT PHANDLE ThreadHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN PCLIENT_ID ClientId)
+{
+ UNIMPLEMENTED;
+}
+
+NTSTATUS STDCALL NtResumeThread(IN HANDLE ThreadHandle,
+ IN PULONG SuspendCount)
+{
+ return(ZwResumeThread(ThreadHandle,SuspendCount));
+}
+
+NTSTATUS STDCALL ZwResumeThread(IN HANDLE ThreadHandle,
+ IN PULONG SuspendCount)
+/*
+ * FUNCTION: Decrements a thread's resume count
+ * ARGUMENTS:
+ * ThreadHandle = Handle to the thread that should be resumed
+ * ResumeCount = The resulting resume count.
+ * REMARK:
+ * A thread is resumed if its suspend count is 0. This procedure maps to
+ * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )
+ * RETURNS: Status
+ */
+{
+ PETHREAD Thread;
+ NTSTATUS Status;
+
+ Status = ObReferenceObjectByHandle(ThreadHandle,
+ THREAD_SUSPEND_RESUME,
+ PsThreadType,
+ UserMode,
+ (PVOID*)&Thread,
+ NULL);
+ if (Status != STATUS_SUCCESS)
+ {
+ return(Status);
+ }
+
+ (*SuspendCount) = InterlockedDecrement(&Thread->Tcb.SuspendCount);
+ if (Thread->Tcb.SuspendCount <= 0)
+ {
+ Thread->Tcb.ThreadState = THREAD_STATE_RUNNABLE;
+ }
+
+ ObDereferenceObject(Thread);
+ return(STATUS_SUCCESS);
+}
+
+NTSTATUS STDCALL NtSetContextThread(IN HANDLE ThreadHandle,
+ IN PCONTEXT Context)
+{
+ return(ZwSetContextThread(ThreadHandle,Context));
+}
+
+NTSTATUS STDCALL ZwSetContextThread(IN HANDLE ThreadHandle,
+ IN PCONTEXT Context)
+{
+ UNIMPLEMENTED;
+}
+
+NTSTATUS STDCALL NtSuspendThread(IN HANDLE ThreadHandle,
+ IN PULONG PreviousSuspendCount)
+{
+ return(ZwSuspendThread(ThreadHandle,PreviousSuspendCount));
+}
+
+NTSTATUS STDCALL ZwSuspendThread(IN HANDLE ThreadHandle,
+ IN PULONG PreviousSuspendCount)
+/*
+ * FUNCTION: Increments a thread's suspend count
+ * ARGUMENTS:
+ * ThreadHandle = Handle to the thread that should be resumed
+ * PreviousSuspendCount = The resulting/previous suspend count.
+ * REMARK:
+ * A thread will be suspended if its suspend count is greater than 0.
+ * This procedure maps to the win32 SuspendThread function. (
+ * documentation about the the suspend count can be found here aswell )
+ * The suspend count is not increased if it is greater than
+ * MAXIMUM_SUSPEND_COUNT.
+ * RETURNS: Status
+ */
+{
+ PETHREAD Thread;
+ NTSTATUS Status;
+
+ Status = ObReferenceObjectByHandle(ThreadHandle,
+ THREAD_SUSPEND_RESUME,
+ PsThreadType,
+ UserMode,
+ (PVOID*)&Thread,
+ NULL);
+ if (Status != STATUS_SUCCESS)
+ {
+ return(Status);
+ }
+
+ (*PreviousSuspendCount) = InterlockedIncrement(&Thread->Tcb.SuspendCount);
+ if (Thread->Tcb.SuspendCount > 0)
+ {
+ Thread->Tcb.ThreadState = THREAD_STATE_SUSPENDED;
+ if (Thread == PsGetCurrentThread())
+ {
+ PsDispatchThread();
+ }
+ }
+
+ ObDereferenceObject(Thread);
+ return(STATUS_SUCCESS);
+}
+
+NTSTATUS STDCALL NtContinue(IN PCONTEXT Context, IN CINT IrqLevel)
+{
+ return(ZwContinue(Context,IrqLevel));
+}
+
+NTSTATUS STDCALL ZwContinue(IN PCONTEXT Context, IN CINT IrqLevel)
+{
+ UNIMPLEMENTED;
+}
+
+NTSTATUS STDCALL NtYieldExecution(VOID)
+{
+ return(ZwYieldExecution());
+}
+
+NTSTATUS STDCALL ZwYieldExecution(VOID)
+{
+ PsDispatchThread();
+ return(STATUS_SUCCESS);
+}