2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/thread.c
5 * PURPOSE: Thread managment
6 * PROGRAMMER: David Welch (welch@mcmail.com)
14 * All of the routines that manipulate the thread queue synchronize on
19 /* INCLUDES ****************************************************************/
22 #include <ddk/ntddk.h>
23 #include <internal/ke.h>
24 #include <internal/ob.h>
25 #include <internal/string.h>
26 #include <internal/hal.h>
27 #include <internal/ps.h>
30 #include <internal/debug.h>
32 /* TYPES *******************************************************************/
34 /* GLOBALS ******************************************************************/
36 POBJECT_TYPE PsThreadType
= NULL
;
38 #define NR_THREAD_PRIORITY_LEVELS (31)
39 #define THREAD_PRIORITY_MAX (15)
41 static KSPIN_LOCK ThreadListLock
= {0,};
44 * PURPOSE: List of threads associated with each priority level
46 static LIST_ENTRY PriorityListHead
[NR_THREAD_PRIORITY_LEVELS
]={{NULL
,NULL
},};
47 static BOOLEAN DoneInitYet
= FALSE
;
49 static PETHREAD CurrentThread
= NULL
;
51 static ULONG NextThreadUniqueId
= 0;
53 /* FUNCTIONS ***************************************************************/
55 PKTHREAD
KeGetCurrentThread(VOID
)
57 return(&(CurrentThread
->Tcb
));
60 PETHREAD
PsGetCurrentThread(VOID
)
62 return((PETHREAD
)KeGetCurrentThread());
65 static VOID
PsInsertIntoThreadList(KPRIORITY Priority
, PETHREAD Thread
)
69 DPRINT("PsInsertIntoThreadList(Priority %d, Thread %x)\n",Priority
,Thread
);
71 KeAcquireSpinLock(&ThreadListLock
,&oldlvl
);
72 InsertTailList(&PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
],
74 KeReleaseSpinLock(&ThreadListLock
,oldlvl
);
77 static PETHREAD
PsScanThreadList(KPRIORITY Priority
)
79 PLIST_ENTRY current_entry
;
81 PETHREAD oldest
= NULL
;
82 ULONG oldest_time
= 0;
84 DPRINT("PsScanThreadList(Priority %d)\n",Priority
);
86 current_entry
= PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
].Flink
;
87 while (current_entry
!= &PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
])
89 current
= CONTAINING_RECORD(current_entry
,ETHREAD
,Tcb
.Entry
);
90 if (current
->Tcb
.ThreadState
== THREAD_STATE_RUNNABLE
)
92 if (oldest
== NULL
|| oldest_time
> current
->Tcb
.LastTick
)
95 oldest_time
= current
->Tcb
.LastTick
;
98 current_entry
= current_entry
->Flink
;
100 DPRINT("PsScanThreadList() = %x\n",oldest
);
104 VOID
PsDispatchThread(VOID
)
106 KPRIORITY CurrentPriority
;
109 LARGE_INTEGER TickCount
;
111 KeAcquireSpinLock(&ThreadListLock
,&irql
);
118 DPRINT("PsDispatchThread() Current %x\n",CurrentThread
);
120 if (CurrentThread
->Tcb
.ThreadState
==THREAD_STATE_RUNNING
)
122 CurrentThread
->Tcb
.ThreadState
=THREAD_STATE_RUNNABLE
;
125 for (CurrentPriority
=THREAD_PRIORITY_TIME_CRITICAL
;
126 CurrentPriority
>=THREAD_PRIORITY_IDLE
;
129 Candidate
= PsScanThreadList(CurrentPriority
);
130 if (Candidate
== CurrentThread
)
132 DPRINT("Scheduling current thread\n");
133 KeQueryTickCount(&TickCount
);
134 CurrentThread
->Tcb
.LastTick
= GET_LARGE_INTEGER_LOW_PART(TickCount
);
135 CurrentThread
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
136 KeReleaseSpinLock(&ThreadListLock
,irql
);
137 KeLowerIrql(PASSIVE_LEVEL
);
140 if (Candidate
!= NULL
)
142 DPRINT("Scheduling %x\n",Candidate
);
144 Candidate
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
146 KeQueryTickCount(&TickCount
);
147 CurrentThread
->Tcb
.LastTick
= GET_LARGE_INTEGER_LOW_PART(TickCount
);
149 CurrentThread
= Candidate
;
151 KeReleaseSpinLock(&ThreadListLock
,irql
);
152 KeLowerIrql(PASSIVE_LEVEL
);
153 HalTaskSwitch(&CurrentThread
->Tcb
);
159 NTSTATUS
PsInitializeThread(HANDLE ProcessHandle
,
161 PHANDLE ThreadHandle
,
162 ACCESS_MASK DesiredAccess
,
163 POBJECT_ATTRIBUTES ThreadAttributes
)
170 Thread
= ObGenericCreateObject(ThreadHandle
,
174 DPRINT("Thread = %x\n",Thread
);
175 Thread
->Tcb
.LastTick
= 0;
176 Thread
->Tcb
.ThreadState
=THREAD_STATE_SUSPENDED
;
177 Thread
->Tcb
.BasePriority
=THREAD_PRIORITY_NORMAL
;
178 Thread
->Tcb
.CurrentPriority
=THREAD_PRIORITY_NORMAL
;
179 Thread
->Tcb
.ApcList
=ExAllocatePool(NonPagedPool
,sizeof(LIST_ENTRY
));
180 Thread
->Tcb
.SuspendCount
= 1;
181 if (ProcessHandle
!=NULL
)
183 Status
= ObReferenceObjectByHandle(ProcessHandle
,
184 PROCESS_CREATE_THREAD
,
187 (PVOID
*)&Thread
->ThreadsProcess
,
189 if (Status
!= STATUS_SUCCESS
)
196 Thread
->ThreadsProcess
=SystemProcess
;
198 InitializeListHead(Thread
->Tcb
.ApcList
);
199 InitializeListHead(&(Thread
->IrpList
));
200 Thread
->Cid
.UniqueThread
=NextThreadUniqueId
++;
201 // thread->Cid.ThreadId=InterlockedIncrement(&NextThreadUniqueId);
202 PsInsertIntoThreadList(Thread
->Tcb
.CurrentPriority
,Thread
);
206 return(STATUS_SUCCESS
);
209 VOID
PsResumeThread(PETHREAD Thread
)
211 DPRINT("PsResumeThread(Thread %x)\n",Thread
);
213 Thread
->Tcb
.SuspendCount
--;
214 DPRINT("Thread->Tcb.SuspendCount %d\n",Thread
->Tcb
.SuspendCount
);
215 DPRINT("Thread->Tcb.ThreadState %d THREAD_STATE_RUNNING %d\n",
216 Thread
->Tcb
.ThreadState
,THREAD_STATE_RUNNING
);
217 if (Thread
->Tcb
.SuspendCount
<= 0 &&
218 Thread
->Tcb
.ThreadState
!= THREAD_STATE_RUNNING
)
220 DPRINT("Setting thread to runnable\n");
221 Thread
->Tcb
.ThreadState
= THREAD_STATE_RUNNABLE
;
223 DPRINT("Finished PsResumeThread()\n");
226 VOID
PsSuspendThread(PETHREAD Thread
)
228 DPRINT("PsSuspendThread(Thread %x)\n",Thread
);
229 Thread
->Tcb
.SuspendCount
++;
230 if (Thread
->Tcb
.SuspendCount
> 0)
232 Thread
->Tcb
.ThreadState
= THREAD_STATE_SUSPENDED
;
233 if (Thread
== CurrentThread
)
240 void PsInitThreadManagment(void)
242 * FUNCTION: Initialize thread managment
245 PETHREAD FirstThread
;
247 ANSI_STRING AnsiString
;
248 HANDLE FirstThreadHandle
;
250 KeInitializeSpinLock(&ThreadListLock
);
251 for (i
=0; i
<NR_THREAD_PRIORITY_LEVELS
; i
++)
253 InitializeListHead(&PriorityListHead
[i
]);
256 PsThreadType
= ExAllocatePool(NonPagedPool
,sizeof(OBJECT_TYPE
));
258 RtlInitAnsiString(&AnsiString
,"Thread");
259 RtlAnsiStringToUnicodeString(&PsThreadType
->TypeName
,&AnsiString
,TRUE
);
261 PsThreadType
->TotalObjects
= 0;
262 PsThreadType
->TotalHandles
= 0;
263 PsThreadType
->MaxObjects
= 0;
264 PsThreadType
->MaxHandles
= 0;
265 PsThreadType
->PagedPoolCharge
= 0;
266 PsThreadType
->NonpagedPoolCharge
= sizeof(ETHREAD
);
267 PsThreadType
->Dump
= NULL
;
268 PsThreadType
->Open
= NULL
;
269 PsThreadType
->Close
= NULL
;
270 PsThreadType
->Delete
= NULL
;
271 PsThreadType
->Parse
= NULL
;
272 PsThreadType
->Security
= NULL
;
273 PsThreadType
->QueryName
= NULL
;
274 PsThreadType
->OkayToClose
= NULL
;
276 PsInitializeThread(NULL
,&FirstThread
,&FirstThreadHandle
,
277 THREAD_ALL_ACCESS
,NULL
);
278 HalInitFirstTask(FirstThread
);
279 FirstThread
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
280 FirstThread
->Tcb
.SuspendCount
= 0;
282 DPRINT("FirstThread %x\n",FirstThread
);
284 CurrentThread
= FirstThread
;
289 NTSTATUS
NtCreateThread(PHANDLE ThreadHandle
,
290 ACCESS_MASK DesiredAccess
,
291 POBJECT_ATTRIBUTES ObjectAttributes
,
292 HANDLE ProcessHandle
,
294 PCONTEXT ThreadContext
,
295 PINITIAL_TEB InitialTeb
,
296 BOOLEAN CreateSuspended
)
298 return(ZwCreateThread(ThreadHandle
,
308 NTSTATUS
ZwCreateThread(PHANDLE ThreadHandle
,
309 ACCESS_MASK DesiredAccess
,
310 POBJECT_ATTRIBUTES ObjectAttributes
,
311 HANDLE ProcessHandle
,
313 PCONTEXT ThreadContext
,
314 PINITIAL_TEB InitialTeb
,
315 BOOLEAN CreateSuspended
)
320 Status
= PsInitializeThread(ProcessHandle
,&Thread
,ThreadHandle
,
321 DesiredAccess
,ObjectAttributes
);
322 if (Status
!= STATUS_SUCCESS
)
327 HalInitTaskWithContext(Thread
,ThreadContext
);
328 Thread
->StartAddress
=NULL
;
335 if (!CreateSuspended
)
337 DPRINT("Not creating suspended\n");
338 PsResumeThread(Thread
);
340 DPRINT("Finished PsCreateThread()\n");
341 return(STATUS_SUCCESS
);
344 NTSTATUS
PsCreateSystemThread(PHANDLE ThreadHandle
,
345 ACCESS_MASK DesiredAccess
,
346 POBJECT_ATTRIBUTES ObjectAttributes
,
347 HANDLE ProcessHandle
,
349 PKSTART_ROUTINE StartRoutine
,
352 * FUNCTION: Creates a thread which executes in kernel mode
354 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
356 * DesiredAccess = Requested access to the thread
357 * ObjectAttributes = Object attributes (optional)
358 * ProcessHandle = Handle of process thread will run in
359 * NULL to use system process
360 * ClientId (OUT) = Caller supplied storage for the returned client id
361 * of the thread (optional)
362 * StartRoutine = Entry point for the thread
363 * StartContext = Argument supplied to the thread when it begins
365 * RETURNS: Success or failure status
371 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
372 ThreadHandle
,ProcessHandle
);
374 Status
= PsInitializeThread(ProcessHandle
,&Thread
,ThreadHandle
,
375 DesiredAccess
,ObjectAttributes
);
376 if (Status
!= STATUS_SUCCESS
)
381 Thread
->StartAddress
=StartRoutine
;
382 HalInitTask(Thread
,StartRoutine
,StartContext
);
386 *ClientId
=Thread
->Cid
;
389 PsResumeThread(Thread
);
391 return(STATUS_SUCCESS
);
394 LONG
KeSetBasePriorityThread(PKTHREAD Thread
, LONG Increment
)
399 KPRIORITY
KeSetPriorityThread(PKTHREAD Thread
, KPRIORITY Priority
)
401 KPRIORITY OldPriority
;
402 OldPriority
= Thread
->CurrentPriority
;
403 Thread
->CurrentPriority
= Priority
;
405 RemoveEntryList(&Thread
->Entry
);
406 PsInsertIntoThreadList(Thread
->CurrentPriority
,
407 CONTAINING_RECORD(Thread
,ETHREAD
,Tcb
));
412 NTSTATUS STDCALL
NtAlertResumeThread(IN HANDLE ThreadHandle
,
413 OUT PULONG SuspendCount
)
415 return(ZwAlertResumeThread(ThreadHandle
,SuspendCount
));
418 NTSTATUS STDCALL
ZwAlertResumeThread(IN HANDLE ThreadHandle
,
419 OUT PULONG SuspendCount
)
424 NTSTATUS STDCALL
NtAlertThread(IN HANDLE ThreadHandle
)
426 return(ZwAlertThread(ThreadHandle
));
429 NTSTATUS STDCALL
ZwAlertThread(IN HANDLE ThreadHandle
)
434 NTSTATUS STDCALL
NtGetContextThread(IN HANDLE ThreadHandle
,
435 OUT PCONTEXT Context
)
437 return(ZwGetContextThread(ThreadHandle
,Context
));
440 NTSTATUS STDCALL
ZwGetContextThread(IN HANDLE ThreadHandle
,
441 OUT PCONTEXT Context
)
446 NTSTATUS STDCALL
NtOpenThread(OUT PHANDLE ThreadHandle
,
447 IN ACCESS_MASK DesiredAccess
,
448 IN POBJECT_ATTRIBUTES ObjectAttributes
,
449 IN PCLIENT_ID ClientId
)
451 return(ZwOpenThread(ThreadHandle
,
457 NTSTATUS STDCALL
ZwOpenThread(OUT PHANDLE ThreadHandle
,
458 IN ACCESS_MASK DesiredAccess
,
459 IN POBJECT_ATTRIBUTES ObjectAttributes
,
460 IN PCLIENT_ID ClientId
)
465 NTSTATUS STDCALL
NtResumeThread(IN HANDLE ThreadHandle
,
466 IN PULONG SuspendCount
)
468 return(ZwResumeThread(ThreadHandle
,SuspendCount
));
471 NTSTATUS STDCALL
ZwResumeThread(IN HANDLE ThreadHandle
,
472 IN PULONG SuspendCount
)
474 * FUNCTION: Decrements a thread's resume count
476 * ThreadHandle = Handle to the thread that should be resumed
477 * ResumeCount = The resulting resume count.
479 * A thread is resumed if its suspend count is 0. This procedure maps to
480 * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )
487 Status
= ObReferenceObjectByHandle(ThreadHandle
,
488 THREAD_SUSPEND_RESUME
,
493 if (Status
!= STATUS_SUCCESS
)
498 (*SuspendCount
) = InterlockedDecrement(&Thread
->Tcb
.SuspendCount
);
499 if (Thread
->Tcb
.SuspendCount
<= 0)
501 Thread
->Tcb
.ThreadState
= THREAD_STATE_RUNNABLE
;
504 return(STATUS_SUCCESS
);
507 NTSTATUS STDCALL
NtSetContextThread(IN HANDLE ThreadHandle
,
510 return(ZwSetContextThread(ThreadHandle
,Context
));
513 NTSTATUS STDCALL
ZwSetContextThread(IN HANDLE ThreadHandle
,
519 NTSTATUS STDCALL
NtSuspendThread(IN HANDLE ThreadHandle
,
520 IN PULONG PreviousSuspendCount
)
522 return(ZwSuspendThread(ThreadHandle
,PreviousSuspendCount
));
525 NTSTATUS STDCALL
ZwSuspendThread(IN HANDLE ThreadHandle
,
526 IN PULONG PreviousSuspendCount
)
528 * FUNCTION: Increments a thread's suspend count
530 * ThreadHandle = Handle to the thread that should be resumed
531 * PreviousSuspendCount = The resulting/previous suspend count.
533 * A thread will be suspended if its suspend count is greater than 0.
534 * This procedure maps to the win32 SuspendThread function. (
535 * documentation about the the suspend count can be found here aswell )
536 * The suspend count is not increased if it is greater than
537 * MAXIMUM_SUSPEND_COUNT.
544 Status
= ObReferenceObjectByHandle(ThreadHandle
,
545 THREAD_SUSPEND_RESUME
,
550 if (Status
!= STATUS_SUCCESS
)
555 (*PreviousSuspendCount
) = InterlockedIncrement(&Thread
->Tcb
.SuspendCount
);
556 if (Thread
->Tcb
.SuspendCount
> 0)
558 Thread
->Tcb
.ThreadState
= THREAD_STATE_SUSPENDED
;
559 if (Thread
== PsGetCurrentThread())
565 return(STATUS_SUCCESS
);
568 NTSTATUS STDCALL
NtContinue(IN PCONTEXT Context
, IN CINT IrqLevel
)
570 return(ZwContinue(Context
,IrqLevel
));
573 NTSTATUS STDCALL
ZwContinue(IN PCONTEXT Context
, IN CINT IrqLevel
)
578 NTSTATUS STDCALL
NtYieldExecution(VOID
)
580 return(ZwYieldExecution());
583 NTSTATUS STDCALL
ZwYieldExecution(VOID
)
586 return(STATUS_SUCCESS
);