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;
55 //static ULONG NextProcessUniqueId = 0;
57 /* FUNCTIONS ***************************************************************/
59 PKTHREAD
KeGetCurrentThread(VOID
)
61 return(&(CurrentThread
->Tcb
));
64 PETHREAD
PsGetCurrentThread(VOID
)
66 return((PETHREAD
)KeGetCurrentThread());
69 VOID
PiTerminateProcessThreads(PEPROCESS Process
, NTSTATUS ExitStatus
)
72 PLIST_ENTRY current_entry
;
76 KeAcquireSpinLock(&ThreadListLock
, &oldlvl
);
78 for (i
=0; i
<NR_THREAD_PRIORITY_LEVELS
; i
++)
80 current_entry
= PriorityListHead
[i
].Flink
;
81 while (current_entry
!= &PriorityListHead
[i
])
83 current
= CONTAINING_RECORD(current_entry
,ETHREAD
,Tcb
.Entry
);
84 if (current
->ThreadsProcess
== Process
&&
85 current
!= PsGetCurrentThread())
87 PsTerminateOtherThread(current
, ExitStatus
);
89 current_entry
= current_entry
->Flink
;
93 KeReleaseSpinLock(&ThreadListLock
, oldlvl
);
96 static VOID
PsInsertIntoThreadList(KPRIORITY Priority
, PETHREAD Thread
)
100 DPRINT("PsInsertIntoThreadList(Priority %x, Thread %x)\n",Priority
,
103 KeAcquireSpinLock(&ThreadListLock
,&oldlvl
);
104 InsertTailList(&PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
],
106 KeReleaseSpinLock(&ThreadListLock
,oldlvl
);
109 VOID
PsBeginThread(PKSTART_ROUTINE StartRoutine
, PVOID StartContext
)
113 KeReleaseSpinLock(&ThreadListLock
,PASSIVE_LEVEL
);
114 Ret
= StartRoutine(StartContext
);
115 PsTerminateSystemThread(Ret
);
119 static PETHREAD
PsScanThreadList(KPRIORITY Priority
)
121 PLIST_ENTRY current_entry
;
123 PETHREAD oldest
= NULL
;
124 ULONG oldest_time
= 0;
126 // DPRINT("PsScanThreadList(Priority %d)\n",Priority);
128 current_entry
= PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
].Flink
;
129 while (current_entry
!= &PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
])
131 current
= CONTAINING_RECORD(current_entry
,ETHREAD
,Tcb
.Entry
);
133 if (current
->Tcb
.ThreadState
== THREAD_STATE_TERMINATED
&&
134 current
!= CurrentThread
)
136 PsReleaseThread(current
);
139 if (current
->Tcb
.ThreadState
== THREAD_STATE_RUNNABLE
)
141 if (oldest
== NULL
|| oldest_time
> current
->Tcb
.LastTick
)
144 oldest_time
= current
->Tcb
.LastTick
;
147 current_entry
= current_entry
->Flink
;
149 // DPRINT("PsScanThreadList() = %x\n",oldest);
153 VOID
PsDispatchThread(VOID
)
155 KPRIORITY CurrentPriority
;
158 LARGE_INTEGER TickCount
;
160 KeAcquireSpinLock(&ThreadListLock
,&irql
);
167 DPRINT("PsDispatchThread() Current %x\n",CurrentThread
);
169 if (CurrentThread
->Tcb
.ThreadState
==THREAD_STATE_RUNNING
)
171 CurrentThread
->Tcb
.ThreadState
=THREAD_STATE_RUNNABLE
;
174 for (CurrentPriority
=THREAD_PRIORITY_TIME_CRITICAL
;
175 CurrentPriority
>=THREAD_PRIORITY_IDLE
;
178 Candidate
= PsScanThreadList(CurrentPriority
);
179 if (Candidate
== CurrentThread
)
181 DPRINT("Scheduling current thread\n");
182 KeQueryTickCount(&TickCount
);
183 CurrentThread
->Tcb
.LastTick
= GET_LARGE_INTEGER_LOW_PART(TickCount
);
184 CurrentThread
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
185 KeReleaseSpinLock(&ThreadListLock
,irql
);
188 if (Candidate
!= NULL
)
190 DPRINT("Scheduling %x\n",Candidate
);
192 Candidate
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
194 KeQueryTickCount(&TickCount
);
195 CurrentThread
->Tcb
.LastTick
= GET_LARGE_INTEGER_LOW_PART(TickCount
);
197 CurrentThread
= Candidate
;
199 HalTaskSwitch(&CurrentThread
->Tcb
);
200 KeReleaseSpinLock(&ThreadListLock
,irql
);
204 DbgPrint("CRITICAL: No threads are runnable\n");
208 NTSTATUS
PsInitializeThread(HANDLE ProcessHandle
,
210 PHANDLE ThreadHandle
,
211 ACCESS_MASK DesiredAccess
,
212 POBJECT_ATTRIBUTES ThreadAttributes
)
219 Thread
= ObCreateObject(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
= (HANDLE
)InterlockedIncrement(
259 &NextThreadUniqueId
);
260 DbgPrint("Thread->Cid.UniqueThread %d\n",Thread
->Cid
.UniqueThread
);
261 ObReferenceObjectByPointer(Thread
,
265 PsInsertIntoThreadList(Thread
->Tcb
.CurrentPriority
,Thread
);
269 ObDereferenceObject(Thread
->ThreadsProcess
);
270 return(STATUS_SUCCESS
);
273 VOID
PsResumeThread(PETHREAD Thread
)
275 DPRINT("PsResumeThread(Thread %x)\n",Thread
);
276 Thread
->Tcb
.SuspendCount
--;
277 if (Thread
->Tcb
.SuspendCount
<= 0 &&
278 Thread
->Tcb
.ThreadState
!= THREAD_STATE_RUNNING
)
280 DPRINT("Setting thread to runnable\n");
281 Thread
->Tcb
.ThreadState
= THREAD_STATE_RUNNABLE
;
283 DPRINT("Finished PsResumeThread()\n");
286 VOID
PsSuspendThread(PETHREAD Thread
)
288 DPRINT("PsSuspendThread(Thread %x)\n",Thread
);
289 Thread
->Tcb
.SuspendCount
++;
290 if (Thread
->Tcb
.SuspendCount
> 0)
292 Thread
->Tcb
.ThreadState
= THREAD_STATE_SUSPENDED
;
293 if (Thread
== CurrentThread
)
300 void PsInitThreadManagment(void)
302 * FUNCTION: Initialize thread managment
305 PETHREAD FirstThread
;
307 ANSI_STRING AnsiString
;
308 HANDLE FirstThreadHandle
;
310 KeInitializeSpinLock(&ThreadListLock
);
311 for (i
=0; i
<NR_THREAD_PRIORITY_LEVELS
; i
++)
313 InitializeListHead(&PriorityListHead
[i
]);
316 PsThreadType
= ExAllocatePool(NonPagedPool
,sizeof(OBJECT_TYPE
));
318 RtlInitAnsiString(&AnsiString
,"Thread");
319 RtlAnsiStringToUnicodeString(&PsThreadType
->TypeName
,&AnsiString
,TRUE
);
321 PsThreadType
->TotalObjects
= 0;
322 PsThreadType
->TotalHandles
= 0;
323 PsThreadType
->MaxObjects
= 0;
324 PsThreadType
->MaxHandles
= 0;
325 PsThreadType
->PagedPoolCharge
= 0;
326 PsThreadType
->NonpagedPoolCharge
= sizeof(ETHREAD
);
327 PsThreadType
->Dump
= NULL
;
328 PsThreadType
->Open
= NULL
;
329 PsThreadType
->Close
= NULL
;
330 PsThreadType
->Delete
= NULL
;
331 PsThreadType
->Parse
= NULL
;
332 PsThreadType
->Security
= NULL
;
333 PsThreadType
->QueryName
= NULL
;
334 PsThreadType
->OkayToClose
= NULL
;
336 PsInitializeThread(NULL
,&FirstThread
,&FirstThreadHandle
,
337 THREAD_ALL_ACCESS
,NULL
);
338 HalInitFirstTask(FirstThread
);
339 FirstThread
->Tcb
.ThreadState
= THREAD_STATE_RUNNING
;
340 FirstThread
->Tcb
.SuspendCount
= 0;
342 DPRINT("FirstThread %x\n",FirstThread
);
344 CurrentThread
= FirstThread
;
349 NTSTATUS
NtCreateThread(PHANDLE ThreadHandle
,
350 ACCESS_MASK DesiredAccess
,
351 POBJECT_ATTRIBUTES ObjectAttributes
,
352 HANDLE ProcessHandle
,
354 PCONTEXT ThreadContext
,
355 PINITIAL_TEB InitialTeb
,
356 BOOLEAN CreateSuspended
)
358 return(ZwCreateThread(ThreadHandle
,
368 NTSTATUS
ZwCreateThread(PHANDLE ThreadHandle
,
369 ACCESS_MASK DesiredAccess
,
370 POBJECT_ATTRIBUTES ObjectAttributes
,
371 HANDLE ProcessHandle
,
373 PCONTEXT ThreadContext
,
374 PINITIAL_TEB InitialTeb
,
375 BOOLEAN CreateSuspended
)
380 DPRINT("ZwCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
381 ThreadHandle
,ThreadContext
);
383 Status
= PsInitializeThread(ProcessHandle
,&Thread
,ThreadHandle
,
384 DesiredAccess
,ObjectAttributes
);
385 if (!NT_SUCCESS(Status
))
390 Status
= HalInitTaskWithContext(Thread
,ThreadContext
);
391 if (!NT_SUCCESS(Status
))
395 Thread
->StartAddress
=NULL
;
402 if (!CreateSuspended
)
404 DPRINT("Not creating suspended\n");
405 PsResumeThread(Thread
);
407 DPRINT("Finished PsCreateThread()\n");
408 return(STATUS_SUCCESS
);
411 NTSTATUS
PsCreateSystemThread(PHANDLE ThreadHandle
,
412 ACCESS_MASK DesiredAccess
,
413 POBJECT_ATTRIBUTES ObjectAttributes
,
414 HANDLE ProcessHandle
,
416 PKSTART_ROUTINE StartRoutine
,
419 * FUNCTION: Creates a thread which executes in kernel mode
421 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
423 * DesiredAccess = Requested access to the thread
424 * ObjectAttributes = Object attributes (optional)
425 * ProcessHandle = Handle of process thread will run in
426 * NULL to use system process
427 * ClientId (OUT) = Caller supplied storage for the returned client id
428 * of the thread (optional)
429 * StartRoutine = Entry point for the thread
430 * StartContext = Argument supplied to the thread when it begins
432 * RETURNS: Success or failure status
438 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
439 ThreadHandle
,ProcessHandle
);
441 Status
= PsInitializeThread(ProcessHandle
,&Thread
,ThreadHandle
,
442 DesiredAccess
,ObjectAttributes
);
443 if (!NT_SUCCESS(Status
))
448 Thread
->StartAddress
=StartRoutine
;
449 Status
= HalInitTask(Thread
,StartRoutine
,StartContext
);
450 if (!NT_SUCCESS(Status
))
457 *ClientId
=Thread
->Cid
;
460 PsResumeThread(Thread
);
462 return(STATUS_SUCCESS
);
465 LONG
KeSetBasePriorityThread(PKTHREAD Thread
, LONG Increment
)
470 KPRIORITY
KeSetPriorityThread(PKTHREAD Thread
, KPRIORITY Priority
)
472 KPRIORITY OldPriority
;
473 OldPriority
= Thread
->CurrentPriority
;
474 Thread
->CurrentPriority
= Priority
;
476 RemoveEntryList(&Thread
->Entry
);
477 PsInsertIntoThreadList(Thread
->CurrentPriority
,
478 CONTAINING_RECORD(Thread
,ETHREAD
,Tcb
));
483 NTSTATUS STDCALL
NtAlertResumeThread(IN HANDLE ThreadHandle
,
484 OUT PULONG SuspendCount
)
486 return(ZwAlertResumeThread(ThreadHandle
,SuspendCount
));
489 NTSTATUS STDCALL
ZwAlertResumeThread(IN HANDLE ThreadHandle
,
490 OUT PULONG SuspendCount
)
495 NTSTATUS STDCALL
NtAlertThread(IN HANDLE ThreadHandle
)
497 return(ZwAlertThread(ThreadHandle
));
500 NTSTATUS STDCALL
ZwAlertThread(IN HANDLE ThreadHandle
)
505 NTSTATUS STDCALL
NtGetContextThread(IN HANDLE ThreadHandle
,
506 OUT PCONTEXT Context
)
508 return(ZwGetContextThread(ThreadHandle
,Context
));
511 NTSTATUS STDCALL
ZwGetContextThread(IN HANDLE ThreadHandle
,
512 OUT PCONTEXT Context
)
517 NTSTATUS STDCALL
NtOpenThread(OUT PHANDLE ThreadHandle
,
518 IN ACCESS_MASK DesiredAccess
,
519 IN POBJECT_ATTRIBUTES ObjectAttributes
,
520 IN PCLIENT_ID ClientId
)
522 return(ZwOpenThread(ThreadHandle
,
528 NTSTATUS STDCALL
ZwOpenThread(OUT PHANDLE ThreadHandle
,
529 IN ACCESS_MASK DesiredAccess
,
530 IN POBJECT_ATTRIBUTES ObjectAttributes
,
531 IN PCLIENT_ID ClientId
)
536 NTSTATUS STDCALL
NtResumeThread(IN HANDLE ThreadHandle
,
537 IN PULONG SuspendCount
)
539 return(ZwResumeThread(ThreadHandle
,SuspendCount
));
542 NTSTATUS STDCALL
ZwResumeThread(IN HANDLE ThreadHandle
,
543 IN PULONG SuspendCount
)
545 * FUNCTION: Decrements a thread's resume count
547 * ThreadHandle = Handle to the thread that should be resumed
548 * ResumeCount = The resulting resume count.
550 * A thread is resumed if its suspend count is 0. This procedure maps to
551 * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )
558 Status
= ObReferenceObjectByHandle(ThreadHandle
,
559 THREAD_SUSPEND_RESUME
,
564 if (Status
!= STATUS_SUCCESS
)
569 (*SuspendCount
) = InterlockedDecrement(&Thread
->Tcb
.SuspendCount
);
570 if (Thread
->Tcb
.SuspendCount
<= 0)
572 Thread
->Tcb
.ThreadState
= THREAD_STATE_RUNNABLE
;
575 ObDereferenceObject(Thread
);
576 return(STATUS_SUCCESS
);
579 NTSTATUS STDCALL
NtSetContextThread(IN HANDLE ThreadHandle
,
582 return(ZwSetContextThread(ThreadHandle
,Context
));
585 NTSTATUS STDCALL
ZwSetContextThread(IN HANDLE ThreadHandle
,
591 NTSTATUS STDCALL
NtSuspendThread(IN HANDLE ThreadHandle
,
592 IN PULONG PreviousSuspendCount
)
594 return(ZwSuspendThread(ThreadHandle
,PreviousSuspendCount
));
597 NTSTATUS STDCALL
ZwSuspendThread(IN HANDLE ThreadHandle
,
598 IN PULONG PreviousSuspendCount
)
600 * FUNCTION: Increments a thread's suspend count
602 * ThreadHandle = Handle to the thread that should be resumed
603 * PreviousSuspendCount = The resulting/previous suspend count.
605 * A thread will be suspended if its suspend count is greater than 0.
606 * This procedure maps to the win32 SuspendThread function. (
607 * documentation about the the suspend count can be found here aswell )
608 * The suspend count is not increased if it is greater than
609 * MAXIMUM_SUSPEND_COUNT.
616 Status
= ObReferenceObjectByHandle(ThreadHandle
,
617 THREAD_SUSPEND_RESUME
,
622 if (Status
!= STATUS_SUCCESS
)
627 (*PreviousSuspendCount
) = InterlockedIncrement(&Thread
->Tcb
.SuspendCount
);
628 if (Thread
->Tcb
.SuspendCount
> 0)
630 Thread
->Tcb
.ThreadState
= THREAD_STATE_SUSPENDED
;
631 if (Thread
== PsGetCurrentThread())
637 ObDereferenceObject(Thread
);
638 return(STATUS_SUCCESS
);
641 NTSTATUS STDCALL
NtContinue(IN PCONTEXT Context
, IN CINT IrqLevel
)
643 return(ZwContinue(Context
,IrqLevel
));
646 NTSTATUS STDCALL
ZwContinue(IN PCONTEXT Context
, IN CINT IrqLevel
)
651 NTSTATUS STDCALL
NtYieldExecution(VOID
)
653 return(ZwYieldExecution());
656 NTSTATUS STDCALL
ZwYieldExecution(VOID
)
659 return(STATUS_SUCCESS
);