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
;
48 ULONG PiNrThreads
= 0;
49 ULONG PiNrRunnableThreads
= 0;
51 static PETHREAD CurrentThread
= NULL
;
53 static ULONG NextThreadUniqueId
= 0;
55 /* FUNCTIONS ***************************************************************/
57 PKTHREAD
KeGetCurrentThread(VOID
)
59 return(&(CurrentThread
->Tcb
));
62 PETHREAD
PsGetCurrentThread(VOID
)
64 return((PETHREAD
)KeGetCurrentThread());
67 VOID
PiTerminateProcessThreads(PEPROCESS Process
, NTSTATUS ExitStatus
)
70 PLIST_ENTRY current_entry
;
74 KeAcquireSpinLock(&ThreadListLock
, &oldlvl
);
76 for (i
=0; i
<NR_THREAD_PRIORITY_LEVELS
; i
++)
78 current_entry
= PriorityListHead
[i
].Flink
;
79 while (current_entry
!= &PriorityListHead
[i
])
81 current
= CONTAINING_RECORD(current_entry
,ETHREAD
,Tcb
.Entry
);
82 if (current
->ThreadsProcess
== Process
&&
83 current
!= PsGetCurrentThread())
85 PsTerminateThread(current
, ExitStatus
);
87 current_entry
= current_entry
->Flink
;
91 KeReleaseSpinLock(&ThreadListLock
, oldlvl
);
94 static VOID
PsInsertIntoThreadList(KPRIORITY Priority
, PETHREAD Thread
)
98 DPRINT("PsInsertIntoThreadList(Priority %x, Thread %x)\n",Priority
,
101 KeAcquireSpinLock(&ThreadListLock
,&oldlvl
);
102 InsertTailList(&PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
],
104 KeReleaseSpinLock(&ThreadListLock
,oldlvl
);
107 VOID
PsBeginThread(PKSTART_ROUTINE StartRoutine
, PVOID StartContext
)
111 KeReleaseSpinLock(&ThreadListLock
,PASSIVE_LEVEL
);
112 Ret
= StartRoutine(StartContext
);
113 PsTerminateSystemThread(Ret
);
117 static PETHREAD
PsScanThreadList(KPRIORITY Priority
)
119 PLIST_ENTRY current_entry
;
121 PETHREAD oldest
= NULL
;
122 ULONG oldest_time
= 0;
124 // DPRINT("PsScanThreadList(Priority %d)\n",Priority);
126 current_entry
= PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
].Flink
;
127 while (current_entry
!= &PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
])
129 current
= CONTAINING_RECORD(current_entry
,ETHREAD
,Tcb
.Entry
);
131 if (current
->Tcb
.ThreadState
== THREAD_STATE_TERMINATED
&&
132 current
!= CurrentThread
)
134 PsReleaseThread(current
);
137 if (current
->Tcb
.ThreadState
== THREAD_STATE_RUNNABLE
)
139 if (oldest
== NULL
|| oldest_time
> current
->Tcb
.LastTick
)
142 oldest_time
= current
->Tcb
.LastTick
;
145 current_entry
= current_entry
->Flink
;
147 // DPRINT("PsScanThreadList() = %x\n",oldest);
151 VOID
PsDispatchThread(VOID
)
153 KPRIORITY CurrentPriority
;
156 LARGE_INTEGER TickCount
;
158 KeAcquireSpinLock(&ThreadListLock
,&irql
);
165 DPRINT("PsDispatchThread() Current %x\n",CurrentThread
);
167 if (CurrentThread
->Tcb
.ThreadState
==THREAD_STATE_RUNNING
)
169 CurrentThread
->Tcb
.ThreadState
=THREAD_STATE_RUNNABLE
;
172 for (CurrentPriority
=THREAD_PRIORITY_TIME_CRITICAL
;
173 CurrentPriority
>=THREAD_PRIORITY_IDLE
;
176 Candidate
= PsScanThreadList(CurrentPriority
);
177 if (Candidate
== CurrentThread
)
179 DPRINT("Scheduling current thread\n");
180 KeQueryTickCount(&TickCount
);
181 CurrentThread
->Tcb
.LastTick
= GET_LARGE_INTEGER_LOW_PART(TickCount
);
182 CurrentThread
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
183 KeReleaseSpinLock(&ThreadListLock
,irql
);
186 if (Candidate
!= NULL
)
188 DPRINT("Scheduling %x\n",Candidate
);
190 Candidate
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
192 KeQueryTickCount(&TickCount
);
193 CurrentThread
->Tcb
.LastTick
= GET_LARGE_INTEGER_LOW_PART(TickCount
);
195 CurrentThread
= Candidate
;
197 HalTaskSwitch(&CurrentThread
->Tcb
);
198 KeReleaseSpinLock(&ThreadListLock
,irql
);
202 DbgPrint("CRITICAL: No threads are runnable\n");
206 NTSTATUS
PsInitializeThread(HANDLE ProcessHandle
,
208 PHANDLE ThreadHandle
,
209 ACCESS_MASK DesiredAccess
,
210 POBJECT_ATTRIBUTES ThreadAttributes
)
219 Thread
= ObGenericCreateObject(ThreadHandle
,
223 DPRINT("Thread = %x\n",Thread
);
224 Thread
->Tcb
.LastTick
= 0;
225 Thread
->Tcb
.ThreadState
=THREAD_STATE_SUSPENDED
;
226 Thread
->Tcb
.BasePriority
=THREAD_PRIORITY_NORMAL
;
227 Thread
->Tcb
.CurrentPriority
=THREAD_PRIORITY_NORMAL
;
228 Thread
->Tcb
.ApcList
=ExAllocatePool(NonPagedPool
,sizeof(LIST_ENTRY
));
229 Thread
->Tcb
.SuspendCount
= 1;
230 if (ProcessHandle
!=NULL
)
232 Status
= ObReferenceObjectByHandle(ProcessHandle
,
233 PROCESS_CREATE_THREAD
,
236 (PVOID
*)&Thread
->ThreadsProcess
,
238 if (Status
!= STATUS_SUCCESS
)
240 DPRINT("Failed at %s:%d\n",__FILE__
,__LINE__
);
246 Thread
->ThreadsProcess
=SystemProcess
;
247 ObReferenceObjectByPointer(Thread
->ThreadsProcess
,
248 PROCESS_CREATE_THREAD
,
252 ObReferenceObjectByPointer(Thread
->ThreadsProcess
,
253 PROCESS_CREATE_THREAD
,
256 InitializeListHead(Thread
->Tcb
.ApcList
);
257 InitializeListHead(&(Thread
->IrpList
));
258 Thread
->Cid
.UniqueThread
=InterlockedIncrement(&NextThreadUniqueId
);
259 ObReferenceObjectByPointer(Thread
,
263 PsInsertIntoThreadList(Thread
->Tcb
.CurrentPriority
,Thread
);
267 ObDereferenceObject(Thread
->ThreadsProcess
);
268 return(STATUS_SUCCESS
);
271 VOID
PsResumeThread(PETHREAD Thread
)
273 DPRINT("PsResumeThread(Thread %x)\n",Thread
);
275 Thread
->Tcb
.SuspendCount
--;
276 DPRINT("Thread->Tcb.SuspendCount %d\n",Thread
->Tcb
.SuspendCount
);
277 DPRINT("Thread->Tcb.ThreadState %d THREAD_STATE_RUNNING %d\n",
278 Thread
->Tcb
.ThreadState
,THREAD_STATE_RUNNING
);
279 if (Thread
->Tcb
.SuspendCount
<= 0 &&
280 Thread
->Tcb
.ThreadState
!= THREAD_STATE_RUNNING
)
282 DPRINT("Setting thread to runnable\n");
283 Thread
->Tcb
.ThreadState
= THREAD_STATE_RUNNABLE
;
285 DPRINT("Finished PsResumeThread()\n");
288 VOID
PsSuspendThread(PETHREAD Thread
)
290 DPRINT("PsSuspendThread(Thread %x)\n",Thread
);
291 Thread
->Tcb
.SuspendCount
++;
292 if (Thread
->Tcb
.SuspendCount
> 0)
294 Thread
->Tcb
.ThreadState
= THREAD_STATE_SUSPENDED
;
295 if (Thread
== CurrentThread
)
302 void PsInitThreadManagment(void)
304 * FUNCTION: Initialize thread managment
307 PETHREAD FirstThread
;
309 ANSI_STRING AnsiString
;
310 HANDLE FirstThreadHandle
;
312 KeInitializeSpinLock(&ThreadListLock
);
313 for (i
=0; i
<NR_THREAD_PRIORITY_LEVELS
; i
++)
315 InitializeListHead(&PriorityListHead
[i
]);
318 PsThreadType
= ExAllocatePool(NonPagedPool
,sizeof(OBJECT_TYPE
));
320 RtlInitAnsiString(&AnsiString
,"Thread");
321 RtlAnsiStringToUnicodeString(&PsThreadType
->TypeName
,&AnsiString
,TRUE
);
323 PsThreadType
->TotalObjects
= 0;
324 PsThreadType
->TotalHandles
= 0;
325 PsThreadType
->MaxObjects
= 0;
326 PsThreadType
->MaxHandles
= 0;
327 PsThreadType
->PagedPoolCharge
= 0;
328 PsThreadType
->NonpagedPoolCharge
= sizeof(ETHREAD
);
329 PsThreadType
->Dump
= NULL
;
330 PsThreadType
->Open
= NULL
;
331 PsThreadType
->Close
= NULL
;
332 PsThreadType
->Delete
= NULL
;
333 PsThreadType
->Parse
= NULL
;
334 PsThreadType
->Security
= NULL
;
335 PsThreadType
->QueryName
= NULL
;
336 PsThreadType
->OkayToClose
= NULL
;
338 PsInitializeThread(NULL
,&FirstThread
,&FirstThreadHandle
,
339 THREAD_ALL_ACCESS
,NULL
);
340 HalInitFirstTask(FirstThread
);
341 FirstThread
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
342 FirstThread
->Tcb
.SuspendCount
= 0;
344 DPRINT("FirstThread %x\n",FirstThread
);
346 CurrentThread
= FirstThread
;
351 NTSTATUS
NtCreateThread(PHANDLE ThreadHandle
,
352 ACCESS_MASK DesiredAccess
,
353 POBJECT_ATTRIBUTES ObjectAttributes
,
354 HANDLE ProcessHandle
,
356 PCONTEXT ThreadContext
,
357 PINITIAL_TEB InitialTeb
,
358 BOOLEAN CreateSuspended
)
360 return(ZwCreateThread(ThreadHandle
,
370 NTSTATUS
ZwCreateThread(PHANDLE ThreadHandle
,
371 ACCESS_MASK DesiredAccess
,
372 POBJECT_ATTRIBUTES ObjectAttributes
,
373 HANDLE ProcessHandle
,
375 PCONTEXT ThreadContext
,
376 PINITIAL_TEB InitialTeb
,
377 BOOLEAN CreateSuspended
)
382 DPRINT("ZwCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
383 ThreadHandle
,ThreadContext
);
385 Status
= PsInitializeThread(ProcessHandle
,&Thread
,ThreadHandle
,
386 DesiredAccess
,ObjectAttributes
);
387 if (Status
!= STATUS_SUCCESS
)
392 HalInitTaskWithContext(Thread
,ThreadContext
);
393 Thread
->StartAddress
=NULL
;
400 if (!CreateSuspended
)
402 DPRINT("Not creating suspended\n");
403 PsResumeThread(Thread
);
405 DPRINT("Finished PsCreateThread()\n");
406 return(STATUS_SUCCESS
);
409 NTSTATUS
PsCreateSystemThread(PHANDLE ThreadHandle
,
410 ACCESS_MASK DesiredAccess
,
411 POBJECT_ATTRIBUTES ObjectAttributes
,
412 HANDLE ProcessHandle
,
414 PKSTART_ROUTINE StartRoutine
,
417 * FUNCTION: Creates a thread which executes in kernel mode
419 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
421 * DesiredAccess = Requested access to the thread
422 * ObjectAttributes = Object attributes (optional)
423 * ProcessHandle = Handle of process thread will run in
424 * NULL to use system process
425 * ClientId (OUT) = Caller supplied storage for the returned client id
426 * of the thread (optional)
427 * StartRoutine = Entry point for the thread
428 * StartContext = Argument supplied to the thread when it begins
430 * RETURNS: Success or failure status
436 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
437 ThreadHandle
,ProcessHandle
);
439 Status
= PsInitializeThread(ProcessHandle
,&Thread
,ThreadHandle
,
440 DesiredAccess
,ObjectAttributes
);
441 if (Status
!= STATUS_SUCCESS
)
446 Thread
->StartAddress
=StartRoutine
;
447 HalInitTask(Thread
,StartRoutine
,StartContext
);
451 *ClientId
=Thread
->Cid
;
454 PsResumeThread(Thread
);
456 return(STATUS_SUCCESS
);
459 LONG
KeSetBasePriorityThread(PKTHREAD Thread
, LONG Increment
)
464 KPRIORITY
KeSetPriorityThread(PKTHREAD Thread
, KPRIORITY Priority
)
466 KPRIORITY OldPriority
;
467 OldPriority
= Thread
->CurrentPriority
;
468 Thread
->CurrentPriority
= Priority
;
470 RemoveEntryList(&Thread
->Entry
);
471 PsInsertIntoThreadList(Thread
->CurrentPriority
,
472 CONTAINING_RECORD(Thread
,ETHREAD
,Tcb
));
477 NTSTATUS STDCALL
NtAlertResumeThread(IN HANDLE ThreadHandle
,
478 OUT PULONG SuspendCount
)
480 return(ZwAlertResumeThread(ThreadHandle
,SuspendCount
));
483 NTSTATUS STDCALL
ZwAlertResumeThread(IN HANDLE ThreadHandle
,
484 OUT PULONG SuspendCount
)
489 NTSTATUS STDCALL
NtAlertThread(IN HANDLE ThreadHandle
)
491 return(ZwAlertThread(ThreadHandle
));
494 NTSTATUS STDCALL
ZwAlertThread(IN HANDLE ThreadHandle
)
499 NTSTATUS STDCALL
NtGetContextThread(IN HANDLE ThreadHandle
,
500 OUT PCONTEXT Context
)
502 return(ZwGetContextThread(ThreadHandle
,Context
));
505 NTSTATUS STDCALL
ZwGetContextThread(IN HANDLE ThreadHandle
,
506 OUT PCONTEXT Context
)
511 NTSTATUS STDCALL
NtOpenThread(OUT PHANDLE ThreadHandle
,
512 IN ACCESS_MASK DesiredAccess
,
513 IN POBJECT_ATTRIBUTES ObjectAttributes
,
514 IN PCLIENT_ID ClientId
)
516 return(ZwOpenThread(ThreadHandle
,
522 NTSTATUS STDCALL
ZwOpenThread(OUT PHANDLE ThreadHandle
,
523 IN ACCESS_MASK DesiredAccess
,
524 IN POBJECT_ATTRIBUTES ObjectAttributes
,
525 IN PCLIENT_ID ClientId
)
530 NTSTATUS STDCALL
NtResumeThread(IN HANDLE ThreadHandle
,
531 IN PULONG SuspendCount
)
533 return(ZwResumeThread(ThreadHandle
,SuspendCount
));
536 NTSTATUS STDCALL
ZwResumeThread(IN HANDLE ThreadHandle
,
537 IN PULONG SuspendCount
)
539 * FUNCTION: Decrements a thread's resume count
541 * ThreadHandle = Handle to the thread that should be resumed
542 * ResumeCount = The resulting resume count.
544 * A thread is resumed if its suspend count is 0. This procedure maps to
545 * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )
552 Status
= ObReferenceObjectByHandle(ThreadHandle
,
553 THREAD_SUSPEND_RESUME
,
558 if (Status
!= STATUS_SUCCESS
)
563 (*SuspendCount
) = InterlockedDecrement(&Thread
->Tcb
.SuspendCount
);
564 if (Thread
->Tcb
.SuspendCount
<= 0)
566 Thread
->Tcb
.ThreadState
= THREAD_STATE_RUNNABLE
;
569 ObDereferenceObject(Thread
);
570 return(STATUS_SUCCESS
);
573 NTSTATUS STDCALL
NtSetContextThread(IN HANDLE ThreadHandle
,
576 return(ZwSetContextThread(ThreadHandle
,Context
));
579 NTSTATUS STDCALL
ZwSetContextThread(IN HANDLE ThreadHandle
,
585 NTSTATUS STDCALL
NtSuspendThread(IN HANDLE ThreadHandle
,
586 IN PULONG PreviousSuspendCount
)
588 return(ZwSuspendThread(ThreadHandle
,PreviousSuspendCount
));
591 NTSTATUS STDCALL
ZwSuspendThread(IN HANDLE ThreadHandle
,
592 IN PULONG PreviousSuspendCount
)
594 * FUNCTION: Increments a thread's suspend count
596 * ThreadHandle = Handle to the thread that should be resumed
597 * PreviousSuspendCount = The resulting/previous suspend count.
599 * A thread will be suspended if its suspend count is greater than 0.
600 * This procedure maps to the win32 SuspendThread function. (
601 * documentation about the the suspend count can be found here aswell )
602 * The suspend count is not increased if it is greater than
603 * MAXIMUM_SUSPEND_COUNT.
610 Status
= ObReferenceObjectByHandle(ThreadHandle
,
611 THREAD_SUSPEND_RESUME
,
616 if (Status
!= STATUS_SUCCESS
)
621 (*PreviousSuspendCount
) = InterlockedIncrement(&Thread
->Tcb
.SuspendCount
);
622 if (Thread
->Tcb
.SuspendCount
> 0)
624 Thread
->Tcb
.ThreadState
= THREAD_STATE_SUSPENDED
;
625 if (Thread
== PsGetCurrentThread())
631 ObDereferenceObject(Thread
);
632 return(STATUS_SUCCESS
);
635 NTSTATUS STDCALL
NtContinue(IN PCONTEXT Context
, IN CINT IrqLevel
)
637 return(ZwContinue(Context
,IrqLevel
));
640 NTSTATUS STDCALL
ZwContinue(IN PCONTEXT Context
, IN CINT IrqLevel
)
645 NTSTATUS STDCALL
NtYieldExecution(VOID
)
647 return(ZwYieldExecution());
650 NTSTATUS STDCALL
ZwYieldExecution(VOID
)
653 return(STATUS_SUCCESS
);