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>
26 #include <internal/string.h>
27 #include <internal/hal.h>
28 #include <internal/ps.h>
31 #include <internal/debug.h>
33 /* TYPES *******************************************************************/
35 /* GLOBALS ******************************************************************/
37 POBJECT_TYPE PsThreadType
= NULL
;
39 #define NR_THREAD_PRIORITY_LEVELS (31)
40 #define THREAD_PRIORITY_MAX (15)
42 static KSPIN_LOCK ThreadListLock
= {0,};
45 * PURPOSE: List of threads associated with each priority level
47 static LIST_ENTRY PriorityListHead
[NR_THREAD_PRIORITY_LEVELS
]={{NULL
,NULL
},};
48 static BOOLEAN DoneInitYet
= FALSE
;
49 ULONG PiNrThreads
= 0;
50 ULONG PiNrRunnableThreads
= 0;
52 static PETHREAD CurrentThread
= NULL
;
54 static ULONG NextThreadUniqueId
= 0;
56 /* FUNCTIONS ***************************************************************/
58 PKTHREAD
KeGetCurrentThread(VOID
)
60 return(&(CurrentThread
->Tcb
));
63 PETHREAD
PsGetCurrentThread(VOID
)
65 return((PETHREAD
)KeGetCurrentThread());
68 VOID
PiTerminateProcessThreads(PEPROCESS Process
, NTSTATUS ExitStatus
)
71 PLIST_ENTRY current_entry
;
75 KeAcquireSpinLock(&ThreadListLock
, &oldlvl
);
77 for (i
=0; i
<NR_THREAD_PRIORITY_LEVELS
; i
++)
79 current_entry
= PriorityListHead
[i
].Flink
;
80 while (current_entry
!= &PriorityListHead
[i
])
82 current
= CONTAINING_RECORD(current_entry
,ETHREAD
,Tcb
.Entry
);
83 if (current
->ThreadsProcess
== Process
&&
84 current
!= PsGetCurrentThread())
86 PsTerminateOtherThread(current
, ExitStatus
);
88 current_entry
= current_entry
->Flink
;
92 KeReleaseSpinLock(&ThreadListLock
, oldlvl
);
95 static VOID
PsInsertIntoThreadList(KPRIORITY Priority
, PETHREAD Thread
)
99 DPRINT("PsInsertIntoThreadList(Priority %x, Thread %x)\n",Priority
,
102 KeAcquireSpinLock(&ThreadListLock
,&oldlvl
);
103 InsertTailList(&PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
],
105 KeReleaseSpinLock(&ThreadListLock
,oldlvl
);
108 VOID
PsBeginThread(PKSTART_ROUTINE StartRoutine
, PVOID StartContext
)
112 KeReleaseSpinLock(&ThreadListLock
,PASSIVE_LEVEL
);
113 Ret
= StartRoutine(StartContext
);
114 PsTerminateSystemThread(Ret
);
118 static PETHREAD
PsScanThreadList(KPRIORITY Priority
)
120 PLIST_ENTRY current_entry
;
122 PETHREAD oldest
= NULL
;
123 ULONG oldest_time
= 0;
125 // DPRINT("PsScanThreadList(Priority %d)\n",Priority);
127 current_entry
= PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
].Flink
;
128 while (current_entry
!= &PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
])
130 current
= CONTAINING_RECORD(current_entry
,ETHREAD
,Tcb
.Entry
);
132 if (current
->Tcb
.ThreadState
== THREAD_STATE_TERMINATED
&&
133 current
!= CurrentThread
)
135 PsReleaseThread(current
);
138 if (current
->Tcb
.ThreadState
== THREAD_STATE_RUNNABLE
)
140 if (oldest
== NULL
|| oldest_time
> current
->Tcb
.LastTick
)
143 oldest_time
= current
->Tcb
.LastTick
;
146 current_entry
= current_entry
->Flink
;
148 // DPRINT("PsScanThreadList() = %x\n",oldest);
152 VOID
PsDispatchThread(VOID
)
154 KPRIORITY CurrentPriority
;
157 LARGE_INTEGER TickCount
;
159 KeAcquireSpinLock(&ThreadListLock
,&irql
);
166 DPRINT("PsDispatchThread() Current %x\n",CurrentThread
);
168 if (CurrentThread
->Tcb
.ThreadState
==THREAD_STATE_RUNNING
)
170 CurrentThread
->Tcb
.ThreadState
=THREAD_STATE_RUNNABLE
;
173 for (CurrentPriority
=THREAD_PRIORITY_TIME_CRITICAL
;
174 CurrentPriority
>=THREAD_PRIORITY_IDLE
;
177 Candidate
= PsScanThreadList(CurrentPriority
);
178 if (Candidate
== CurrentThread
)
180 DPRINT("Scheduling current thread\n");
181 KeQueryTickCount(&TickCount
);
182 CurrentThread
->Tcb
.LastTick
= GET_LARGE_INTEGER_LOW_PART(TickCount
);
183 CurrentThread
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
184 KeReleaseSpinLock(&ThreadListLock
,irql
);
187 if (Candidate
!= NULL
)
189 DPRINT("Scheduling %x\n",Candidate
);
191 Candidate
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
193 KeQueryTickCount(&TickCount
);
194 CurrentThread
->Tcb
.LastTick
= GET_LARGE_INTEGER_LOW_PART(TickCount
);
196 CurrentThread
= Candidate
;
198 HalTaskSwitch(&CurrentThread
->Tcb
);
199 KeReleaseSpinLock(&ThreadListLock
,irql
);
203 DbgPrint("CRITICAL: No threads are runnable\n");
207 NTSTATUS
PsInitializeThread(HANDLE ProcessHandle
,
209 PHANDLE ThreadHandle
,
210 ACCESS_MASK DesiredAccess
,
211 POBJECT_ATTRIBUTES ThreadAttributes
)
220 Thread
= ObCreateObject(ThreadHandle
,
224 DPRINT("Thread = %x\n",Thread
);
225 Thread
->Tcb
.LastTick
= 0;
226 Thread
->Tcb
.ThreadState
=THREAD_STATE_SUSPENDED
;
227 Thread
->Tcb
.BasePriority
=THREAD_PRIORITY_NORMAL
;
228 Thread
->Tcb
.CurrentPriority
=THREAD_PRIORITY_NORMAL
;
229 Thread
->Tcb
.ApcList
=ExAllocatePool(NonPagedPool
,sizeof(LIST_ENTRY
));
230 Thread
->Tcb
.SuspendCount
= 1;
231 if (ProcessHandle
!=NULL
)
233 Status
= ObReferenceObjectByHandle(ProcessHandle
,
234 PROCESS_CREATE_THREAD
,
237 (PVOID
*)&Thread
->ThreadsProcess
,
239 if (Status
!= STATUS_SUCCESS
)
241 DPRINT("Failed at %s:%d\n",__FILE__
,__LINE__
);
247 Thread
->ThreadsProcess
=SystemProcess
;
248 ObReferenceObjectByPointer(Thread
->ThreadsProcess
,
249 PROCESS_CREATE_THREAD
,
253 ObReferenceObjectByPointer(Thread
->ThreadsProcess
,
254 PROCESS_CREATE_THREAD
,
257 InitializeListHead(Thread
->Tcb
.ApcList
);
258 InitializeListHead(&(Thread
->IrpList
));
259 Thread
->Cid
.UniqueThread
=InterlockedIncrement(&NextThreadUniqueId
);
260 ObReferenceObjectByPointer(Thread
,
264 PsInsertIntoThreadList(Thread
->Tcb
.CurrentPriority
,Thread
);
268 ObDereferenceObject(Thread
->ThreadsProcess
);
269 return(STATUS_SUCCESS
);
272 VOID
PsResumeThread(PETHREAD Thread
)
274 DPRINT("PsResumeThread(Thread %x)\n",Thread
);
276 Thread
->Tcb
.SuspendCount
--;
277 DPRINT("Thread->Tcb.SuspendCount %d\n",Thread
->Tcb
.SuspendCount
);
278 DPRINT("Thread->Tcb.ThreadState %d THREAD_STATE_RUNNING %d\n",
279 Thread
->Tcb
.ThreadState
,THREAD_STATE_RUNNING
);
280 if (Thread
->Tcb
.SuspendCount
<= 0 &&
281 Thread
->Tcb
.ThreadState
!= THREAD_STATE_RUNNING
)
283 DPRINT("Setting thread to runnable\n");
284 Thread
->Tcb
.ThreadState
= THREAD_STATE_RUNNABLE
;
286 DPRINT("Finished PsResumeThread()\n");
289 VOID
PsSuspendThread(PETHREAD Thread
)
291 DPRINT("PsSuspendThread(Thread %x)\n",Thread
);
292 Thread
->Tcb
.SuspendCount
++;
293 if (Thread
->Tcb
.SuspendCount
> 0)
295 Thread
->Tcb
.ThreadState
= THREAD_STATE_SUSPENDED
;
296 if (Thread
== CurrentThread
)
303 void PsInitThreadManagment(void)
305 * FUNCTION: Initialize thread managment
308 PETHREAD FirstThread
;
310 ANSI_STRING AnsiString
;
311 HANDLE FirstThreadHandle
;
313 KeInitializeSpinLock(&ThreadListLock
);
314 for (i
=0; i
<NR_THREAD_PRIORITY_LEVELS
; i
++)
316 InitializeListHead(&PriorityListHead
[i
]);
319 PsThreadType
= ExAllocatePool(NonPagedPool
,sizeof(OBJECT_TYPE
));
321 RtlInitAnsiString(&AnsiString
,"Thread");
322 RtlAnsiStringToUnicodeString(&PsThreadType
->TypeName
,&AnsiString
,TRUE
);
324 PsThreadType
->TotalObjects
= 0;
325 PsThreadType
->TotalHandles
= 0;
326 PsThreadType
->MaxObjects
= 0;
327 PsThreadType
->MaxHandles
= 0;
328 PsThreadType
->PagedPoolCharge
= 0;
329 PsThreadType
->NonpagedPoolCharge
= sizeof(ETHREAD
);
330 PsThreadType
->Dump
= NULL
;
331 PsThreadType
->Open
= NULL
;
332 PsThreadType
->Close
= NULL
;
333 PsThreadType
->Delete
= NULL
;
334 PsThreadType
->Parse
= NULL
;
335 PsThreadType
->Security
= NULL
;
336 PsThreadType
->QueryName
= NULL
;
337 PsThreadType
->OkayToClose
= NULL
;
339 PsInitializeThread(NULL
,&FirstThread
,&FirstThreadHandle
,
340 THREAD_ALL_ACCESS
,NULL
);
341 HalInitFirstTask(FirstThread
);
342 FirstThread
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
343 FirstThread
->Tcb
.SuspendCount
= 0;
345 DPRINT("FirstThread %x\n",FirstThread
);
347 CurrentThread
= FirstThread
;
352 NTSTATUS
NtCreateThread(PHANDLE ThreadHandle
,
353 ACCESS_MASK DesiredAccess
,
354 POBJECT_ATTRIBUTES ObjectAttributes
,
355 HANDLE ProcessHandle
,
357 PCONTEXT ThreadContext
,
358 PINITIAL_TEB InitialTeb
,
359 BOOLEAN CreateSuspended
)
361 return(ZwCreateThread(ThreadHandle
,
371 NTSTATUS
ZwCreateThread(PHANDLE ThreadHandle
,
372 ACCESS_MASK DesiredAccess
,
373 POBJECT_ATTRIBUTES ObjectAttributes
,
374 HANDLE ProcessHandle
,
376 PCONTEXT ThreadContext
,
377 PINITIAL_TEB InitialTeb
,
378 BOOLEAN CreateSuspended
)
383 DPRINT("ZwCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
384 ThreadHandle
,ThreadContext
);
386 Status
= PsInitializeThread(ProcessHandle
,&Thread
,ThreadHandle
,
387 DesiredAccess
,ObjectAttributes
);
388 if (Status
!= STATUS_SUCCESS
)
393 HalInitTaskWithContext(Thread
,ThreadContext
);
394 Thread
->StartAddress
=NULL
;
401 if (!CreateSuspended
)
403 DPRINT("Not creating suspended\n");
404 PsResumeThread(Thread
);
406 DPRINT("Finished PsCreateThread()\n");
407 return(STATUS_SUCCESS
);
410 NTSTATUS
PsCreateSystemThread(PHANDLE ThreadHandle
,
411 ACCESS_MASK DesiredAccess
,
412 POBJECT_ATTRIBUTES ObjectAttributes
,
413 HANDLE ProcessHandle
,
415 PKSTART_ROUTINE StartRoutine
,
418 * FUNCTION: Creates a thread which executes in kernel mode
420 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
422 * DesiredAccess = Requested access to the thread
423 * ObjectAttributes = Object attributes (optional)
424 * ProcessHandle = Handle of process thread will run in
425 * NULL to use system process
426 * ClientId (OUT) = Caller supplied storage for the returned client id
427 * of the thread (optional)
428 * StartRoutine = Entry point for the thread
429 * StartContext = Argument supplied to the thread when it begins
431 * RETURNS: Success or failure status
437 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
438 ThreadHandle
,ProcessHandle
);
440 Status
= PsInitializeThread(ProcessHandle
,&Thread
,ThreadHandle
,
441 DesiredAccess
,ObjectAttributes
);
442 if (Status
!= STATUS_SUCCESS
)
447 Thread
->StartAddress
=StartRoutine
;
448 HalInitTask(Thread
,StartRoutine
,StartContext
);
452 *ClientId
=Thread
->Cid
;
455 PsResumeThread(Thread
);
457 return(STATUS_SUCCESS
);
460 LONG
KeSetBasePriorityThread(PKTHREAD Thread
, LONG Increment
)
465 KPRIORITY
KeSetPriorityThread(PKTHREAD Thread
, KPRIORITY Priority
)
467 KPRIORITY OldPriority
;
468 OldPriority
= Thread
->CurrentPriority
;
469 Thread
->CurrentPriority
= Priority
;
471 RemoveEntryList(&Thread
->Entry
);
472 PsInsertIntoThreadList(Thread
->CurrentPriority
,
473 CONTAINING_RECORD(Thread
,ETHREAD
,Tcb
));
478 NTSTATUS STDCALL
NtAlertResumeThread(IN HANDLE ThreadHandle
,
479 OUT PULONG SuspendCount
)
481 return(ZwAlertResumeThread(ThreadHandle
,SuspendCount
));
484 NTSTATUS STDCALL
ZwAlertResumeThread(IN HANDLE ThreadHandle
,
485 OUT PULONG SuspendCount
)
490 NTSTATUS STDCALL
NtAlertThread(IN HANDLE ThreadHandle
)
492 return(ZwAlertThread(ThreadHandle
));
495 NTSTATUS STDCALL
ZwAlertThread(IN HANDLE ThreadHandle
)
500 NTSTATUS STDCALL
NtGetContextThread(IN HANDLE ThreadHandle
,
501 OUT PCONTEXT Context
)
503 return(ZwGetContextThread(ThreadHandle
,Context
));
506 NTSTATUS STDCALL
ZwGetContextThread(IN HANDLE ThreadHandle
,
507 OUT PCONTEXT Context
)
512 NTSTATUS STDCALL
NtOpenThread(OUT PHANDLE ThreadHandle
,
513 IN ACCESS_MASK DesiredAccess
,
514 IN POBJECT_ATTRIBUTES ObjectAttributes
,
515 IN PCLIENT_ID ClientId
)
517 return(ZwOpenThread(ThreadHandle
,
523 NTSTATUS STDCALL
ZwOpenThread(OUT PHANDLE ThreadHandle
,
524 IN ACCESS_MASK DesiredAccess
,
525 IN POBJECT_ATTRIBUTES ObjectAttributes
,
526 IN PCLIENT_ID ClientId
)
531 NTSTATUS STDCALL
NtResumeThread(IN HANDLE ThreadHandle
,
532 IN PULONG SuspendCount
)
534 return(ZwResumeThread(ThreadHandle
,SuspendCount
));
537 NTSTATUS STDCALL
ZwResumeThread(IN HANDLE ThreadHandle
,
538 IN PULONG SuspendCount
)
540 * FUNCTION: Decrements a thread's resume count
542 * ThreadHandle = Handle to the thread that should be resumed
543 * ResumeCount = The resulting resume count.
545 * A thread is resumed if its suspend count is 0. This procedure maps to
546 * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )
553 Status
= ObReferenceObjectByHandle(ThreadHandle
,
554 THREAD_SUSPEND_RESUME
,
559 if (Status
!= STATUS_SUCCESS
)
564 (*SuspendCount
) = InterlockedDecrement(&Thread
->Tcb
.SuspendCount
);
565 if (Thread
->Tcb
.SuspendCount
<= 0)
567 Thread
->Tcb
.ThreadState
= THREAD_STATE_RUNNABLE
;
570 ObDereferenceObject(Thread
);
571 return(STATUS_SUCCESS
);
574 NTSTATUS STDCALL
NtSetContextThread(IN HANDLE ThreadHandle
,
577 return(ZwSetContextThread(ThreadHandle
,Context
));
580 NTSTATUS STDCALL
ZwSetContextThread(IN HANDLE ThreadHandle
,
586 NTSTATUS STDCALL
NtSuspendThread(IN HANDLE ThreadHandle
,
587 IN PULONG PreviousSuspendCount
)
589 return(ZwSuspendThread(ThreadHandle
,PreviousSuspendCount
));
592 NTSTATUS STDCALL
ZwSuspendThread(IN HANDLE ThreadHandle
,
593 IN PULONG PreviousSuspendCount
)
595 * FUNCTION: Increments a thread's suspend count
597 * ThreadHandle = Handle to the thread that should be resumed
598 * PreviousSuspendCount = The resulting/previous suspend count.
600 * A thread will be suspended if its suspend count is greater than 0.
601 * This procedure maps to the win32 SuspendThread function. (
602 * documentation about the the suspend count can be found here aswell )
603 * The suspend count is not increased if it is greater than
604 * MAXIMUM_SUSPEND_COUNT.
611 Status
= ObReferenceObjectByHandle(ThreadHandle
,
612 THREAD_SUSPEND_RESUME
,
617 if (Status
!= STATUS_SUCCESS
)
622 (*PreviousSuspendCount
) = InterlockedIncrement(&Thread
->Tcb
.SuspendCount
);
623 if (Thread
->Tcb
.SuspendCount
> 0)
625 Thread
->Tcb
.ThreadState
= THREAD_STATE_SUSPENDED
;
626 if (Thread
== PsGetCurrentThread())
632 ObDereferenceObject(Thread
);
633 return(STATUS_SUCCESS
);
636 NTSTATUS STDCALL
NtContinue(IN PCONTEXT Context
, IN CINT IrqLevel
)
638 return(ZwContinue(Context
,IrqLevel
));
641 NTSTATUS STDCALL
ZwContinue(IN PCONTEXT Context
, IN CINT IrqLevel
)
646 NTSTATUS STDCALL
NtYieldExecution(VOID
)
648 return(ZwYieldExecution());
651 NTSTATUS STDCALL
ZwYieldExecution(VOID
)
654 return(STATUS_SUCCESS
);