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>
29 #include <internal/ob.h>
32 #include <internal/debug.h>
34 /* TYPES *******************************************************************/
36 /* GLOBALS ******************************************************************/
38 POBJECT_TYPE PsThreadType
= NULL
;
40 #define NR_THREAD_PRIORITY_LEVELS (31)
41 #define THREAD_PRIORITY_MAX (15)
43 static KSPIN_LOCK ThreadListLock
= {0,};
46 * PURPOSE: List of threads associated with each priority level
48 static LIST_ENTRY PriorityListHead
[NR_THREAD_PRIORITY_LEVELS
]={{NULL
,NULL
},};
49 static BOOLEAN DoneInitYet
= FALSE
;
50 ULONG PiNrThreads
= 0;
51 ULONG PiNrRunnableThreads
= 0;
53 static PETHREAD CurrentThread
= NULL
;
55 static ULONG PiNextThreadUniqueId
= 0;
57 /* FUNCTIONS ***************************************************************/
59 PKTHREAD
KeGetCurrentThread(VOID
)
61 return(&(CurrentThread
->Tcb
));
64 PETHREAD
PsGetCurrentThread(VOID
)
66 return(CurrentThread
);
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
,
102 DPRINT("Offset %x\n", THREAD_PRIORITY_MAX
+ Priority
);
104 KeAcquireSpinLock(&ThreadListLock
,&oldlvl
);
105 InsertTailList(&PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
],
107 KeReleaseSpinLock(&ThreadListLock
,oldlvl
);
110 VOID
PsBeginThread(PKSTART_ROUTINE StartRoutine
, PVOID StartContext
)
114 // KeReleaseSpinLock(&ThreadListLock,PASSIVE_LEVEL);
115 Ret
= StartRoutine(StartContext
);
116 PsTerminateSystemThread(Ret
);
120 static PETHREAD
PsScanThreadList(KPRIORITY Priority
)
122 PLIST_ENTRY current_entry
;
124 PETHREAD oldest
= NULL
;
125 ULONG oldest_time
= 0;
127 // DPRINT("PsScanThreadList(Priority %d)\n",Priority);
129 current_entry
= PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
].Flink
;
130 while (current_entry
!= &PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
])
132 current
= CONTAINING_RECORD(current_entry
,ETHREAD
,Tcb
.Entry
);
134 if (current
->Tcb
.State
== THREAD_STATE_TERMINATED
&&
135 current
!= CurrentThread
)
137 PsReleaseThread(current
);
140 if (current
->Tcb
.State
== THREAD_STATE_RUNNABLE
)
142 if (oldest
== NULL
|| oldest_time
> current
->Tcb
.LastTick
)
145 oldest_time
= current
->Tcb
.LastTick
;
148 current_entry
= current_entry
->Flink
;
150 // DPRINT("PsScanThreadList() = %x\n",oldest);
154 VOID
PsDispatchThread(VOID
)
156 KPRIORITY CurrentPriority
;
159 LARGE_INTEGER TickCount
;
161 KeAcquireSpinLock(&ThreadListLock
,&irql
);
168 DPRINT("PsDispatchThread() Current %x\n",CurrentThread
);
170 if (CurrentThread
->Tcb
.State
==THREAD_STATE_RUNNING
)
172 CurrentThread
->Tcb
.State
=THREAD_STATE_RUNNABLE
;
175 for (CurrentPriority
=THREAD_PRIORITY_TIME_CRITICAL
;
176 CurrentPriority
>=THREAD_PRIORITY_IDLE
;
179 Candidate
= PsScanThreadList(CurrentPriority
);
180 if (Candidate
== CurrentThread
)
182 DPRINT("Scheduling current thread\n");
183 KeQueryTickCount(&TickCount
);
184 CurrentThread
->Tcb
.LastTick
= TickCount
.u
.LowPart
;
185 CurrentThread
->Tcb
.State
= THREAD_STATE_RUNNING
;
186 KeReleaseSpinLock(&ThreadListLock
,irql
);
189 if (Candidate
!= NULL
)
191 DPRINT("Scheduling %x\n",Candidate
);
193 Candidate
->Tcb
.State
= THREAD_STATE_RUNNING
;
195 KeQueryTickCount(&TickCount
);
196 CurrentThread
->Tcb
.LastTick
= TickCount
.u
.LowPart
;
198 CurrentThread
= Candidate
;
200 HalTaskSwitch(&CurrentThread
->Tcb
);
201 KeReleaseSpinLock(&ThreadListLock
, irql
);
205 DbgPrint("CRITICAL: No threads are runnable\n");
209 NTSTATUS
PsInitializeThread(HANDLE ProcessHandle
,
211 PHANDLE ThreadHandle
,
212 ACCESS_MASK DesiredAccess
,
213 POBJECT_ATTRIBUTES ThreadAttributes
)
220 Thread
= ObCreateObject(ThreadHandle
,
224 DPRINT("Thread = %x\n",Thread
);
225 Thread
->Tcb
.LastTick
= 0;
226 Thread
->Tcb
.State
= THREAD_STATE_SUSPENDED
;
227 Thread
->Tcb
.BasePriority
= THREAD_PRIORITY_NORMAL
;
228 Thread
->Tcb
.SuspendCount
= 1;
229 InitializeListHead(&Thread
->Tcb
.ApcState
.ApcListHead
[0]);
230 InitializeListHead(&Thread
->Tcb
.ApcState
.ApcListHead
[1]);
231 Thread
->Tcb
.KernelApcDisable
= 1;
233 if (ProcessHandle
!= NULL
)
235 Status
= ObReferenceObjectByHandle(ProcessHandle
,
236 PROCESS_CREATE_THREAD
,
239 (PVOID
*)&Thread
->ThreadsProcess
,
241 if (Status
!= STATUS_SUCCESS
)
243 DPRINT("Failed at %s:%d\n",__FILE__
,__LINE__
);
249 Thread
->ThreadsProcess
= SystemProcess
;
250 ObReferenceObjectByPointer(Thread
->ThreadsProcess
,
251 PROCESS_CREATE_THREAD
,
255 ObReferenceObjectByPointer(Thread
->ThreadsProcess
,
256 PROCESS_CREATE_THREAD
,
259 InitializeListHead(&(Thread
->IrpList
));
260 Thread
->Cid
.UniqueThread
= (HANDLE
)InterlockedIncrement(
261 &PiNextThreadUniqueId
);
262 Thread
->Cid
.UniqueProcess
= (HANDLE
)Thread
->ThreadsProcess
->UniqueProcessId
;
263 DbgPrint("Thread->Cid.UniqueThread %d\n",Thread
->Cid
.UniqueThread
);
264 ObReferenceObjectByPointer(Thread
,
268 PsInsertIntoThreadList(Thread
->Tcb
.BasePriority
,Thread
);
272 ObDereferenceObject(Thread
->ThreadsProcess
);
273 return(STATUS_SUCCESS
);
276 VOID
PsResumeThread(PETHREAD Thread
)
278 DPRINT("PsResumeThread(Thread %x)\n",Thread
);
279 Thread
->Tcb
.SuspendCount
--;
280 if (Thread
->Tcb
.SuspendCount
<= 0 &&
281 Thread
->Tcb
.State
!= THREAD_STATE_RUNNING
)
283 DPRINT("Setting thread to runnable\n");
284 Thread
->Tcb
.State
= 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
.State
= THREAD_STATE_SUSPENDED
;
296 if (Thread
== CurrentThread
)
303 VOID
PiDeleteThread(PVOID ObjectBody
)
305 DbgPrint("PiDeleteThread(ObjectBody %x)\n",ObjectBody
);
308 VOID
PsInitThreadManagment(VOID
)
310 * FUNCTION: Initialize thread managment
313 PETHREAD FirstThread
;
315 ANSI_STRING AnsiString
;
316 HANDLE FirstThreadHandle
;
318 KeInitializeSpinLock(&ThreadListLock
);
319 for (i
=0; i
<NR_THREAD_PRIORITY_LEVELS
; i
++)
321 InitializeListHead(&PriorityListHead
[i
]);
324 PsThreadType
= ExAllocatePool(NonPagedPool
,sizeof(OBJECT_TYPE
));
326 RtlInitAnsiString(&AnsiString
,"Thread");
327 RtlAnsiStringToUnicodeString(&PsThreadType
->TypeName
,&AnsiString
,TRUE
);
329 PsThreadType
->TotalObjects
= 0;
330 PsThreadType
->TotalHandles
= 0;
331 PsThreadType
->MaxObjects
= 0;
332 PsThreadType
->MaxHandles
= 0;
333 PsThreadType
->PagedPoolCharge
= 0;
334 PsThreadType
->NonpagedPoolCharge
= sizeof(ETHREAD
);
335 PsThreadType
->Dump
= NULL
;
336 PsThreadType
->Open
= NULL
;
337 PsThreadType
->Close
= NULL
;
338 PsThreadType
->Delete
= PiDeleteThread
;
339 PsThreadType
->Parse
= NULL
;
340 PsThreadType
->Security
= NULL
;
341 PsThreadType
->QueryName
= NULL
;
342 PsThreadType
->OkayToClose
= NULL
;
344 PsInitializeThread(NULL
,&FirstThread
,&FirstThreadHandle
,
345 THREAD_ALL_ACCESS
,NULL
);
346 HalInitFirstTask(FirstThread
);
347 FirstThread
->Tcb
.State
= THREAD_STATE_RUNNING
;
348 FirstThread
->Tcb
.SuspendCount
= 0;
350 DPRINT("FirstThread %x\n",FirstThread
);
352 CurrentThread
= FirstThread
;
357 NTSTATUS
NtCreateThread(PHANDLE ThreadHandle
,
358 ACCESS_MASK DesiredAccess
,
359 POBJECT_ATTRIBUTES ObjectAttributes
,
360 HANDLE ProcessHandle
,
362 PCONTEXT ThreadContext
,
363 PINITIAL_TEB InitialTeb
,
364 BOOLEAN CreateSuspended
)
366 return(ZwCreateThread(ThreadHandle
,
376 NTSTATUS
ZwCreateThread(PHANDLE ThreadHandle
,
377 ACCESS_MASK DesiredAccess
,
378 POBJECT_ATTRIBUTES ObjectAttributes
,
379 HANDLE ProcessHandle
,
381 PCONTEXT ThreadContext
,
382 PINITIAL_TEB InitialTeb
,
383 BOOLEAN CreateSuspended
)
388 DPRINT("ZwCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
389 ThreadHandle
,ThreadContext
);
391 Status
= PsInitializeThread(ProcessHandle
,&Thread
,ThreadHandle
,
392 DesiredAccess
,ObjectAttributes
);
393 if (!NT_SUCCESS(Status
))
398 Status
= HalInitTaskWithContext(Thread
,ThreadContext
);
399 if (!NT_SUCCESS(Status
))
403 Thread
->StartAddress
=NULL
;
410 if (!CreateSuspended
)
412 DPRINT("Not creating suspended\n");
413 PsResumeThread(Thread
);
415 DPRINT("Finished PsCreateThread()\n");
416 return(STATUS_SUCCESS
);
419 NTSTATUS
PsCreateSystemThread(PHANDLE ThreadHandle
,
420 ACCESS_MASK DesiredAccess
,
421 POBJECT_ATTRIBUTES ObjectAttributes
,
422 HANDLE ProcessHandle
,
424 PKSTART_ROUTINE StartRoutine
,
427 * FUNCTION: Creates a thread which executes in kernel mode
429 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
431 * DesiredAccess = Requested access to the thread
432 * ObjectAttributes = Object attributes (optional)
433 * ProcessHandle = Handle of process thread will run in
434 * NULL to use system process
435 * ClientId (OUT) = Caller supplied storage for the returned client id
436 * of the thread (optional)
437 * StartRoutine = Entry point for the thread
438 * StartContext = Argument supplied to the thread when it begins
440 * RETURNS: Success or failure status
446 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
447 ThreadHandle
,ProcessHandle
);
449 Status
= PsInitializeThread(ProcessHandle
,&Thread
,ThreadHandle
,
450 DesiredAccess
,ObjectAttributes
);
451 if (!NT_SUCCESS(Status
))
456 Thread
->StartAddress
=StartRoutine
;
457 Status
= HalInitTask(Thread
,StartRoutine
,StartContext
);
458 if (!NT_SUCCESS(Status
))
465 *ClientId
=Thread
->Cid
;
468 PsResumeThread(Thread
);
470 return(STATUS_SUCCESS
);
473 LONG
KeSetBasePriorityThread(PKTHREAD Thread
, LONG Increment
)
478 KPRIORITY
KeSetPriorityThread(PKTHREAD Thread
, KPRIORITY Priority
)
480 KPRIORITY OldPriority
;
481 OldPriority
= Thread
->BasePriority
;
482 Thread
->BasePriority
= Priority
;
484 RemoveEntryList(&Thread
->Entry
);
485 PsInsertIntoThreadList(Thread
->BasePriority
,
486 CONTAINING_RECORD(Thread
,ETHREAD
,Tcb
));
491 NTSTATUS STDCALL
NtAlertResumeThread(IN HANDLE ThreadHandle
,
492 OUT PULONG SuspendCount
)
494 return(ZwAlertResumeThread(ThreadHandle
,SuspendCount
));
497 NTSTATUS STDCALL
ZwAlertResumeThread(IN HANDLE ThreadHandle
,
498 OUT PULONG SuspendCount
)
503 NTSTATUS STDCALL
NtAlertThread(IN HANDLE ThreadHandle
)
505 return(ZwAlertThread(ThreadHandle
));
508 NTSTATUS STDCALL
ZwAlertThread(IN HANDLE ThreadHandle
)
513 NTSTATUS STDCALL
NtGetContextThread(IN HANDLE ThreadHandle
,
514 OUT PCONTEXT Context
)
516 return(ZwGetContextThread(ThreadHandle
,Context
));
519 NTSTATUS STDCALL
ZwGetContextThread(IN HANDLE ThreadHandle
,
520 OUT PCONTEXT Context
)
525 NTSTATUS STDCALL
NtOpenThread(OUT PHANDLE ThreadHandle
,
526 IN ACCESS_MASK DesiredAccess
,
527 IN POBJECT_ATTRIBUTES ObjectAttributes
,
528 IN PCLIENT_ID ClientId
)
530 return(ZwOpenThread(ThreadHandle
,
536 NTSTATUS STDCALL
ZwOpenThread(OUT PHANDLE ThreadHandle
,
537 IN ACCESS_MASK DesiredAccess
,
538 IN POBJECT_ATTRIBUTES ObjectAttributes
,
539 IN PCLIENT_ID ClientId
)
544 NTSTATUS STDCALL
NtResumeThread(IN HANDLE ThreadHandle
,
545 IN PULONG SuspendCount
)
547 return(ZwResumeThread(ThreadHandle
,SuspendCount
));
550 NTSTATUS STDCALL
ZwResumeThread(IN HANDLE ThreadHandle
,
551 IN PULONG SuspendCount
)
553 * FUNCTION: Decrements a thread's resume count
555 * ThreadHandle = Handle to the thread that should be resumed
556 * ResumeCount = The resulting resume count.
558 * A thread is resumed if its suspend count is 0. This procedure maps to
559 * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )
566 Status
= ObReferenceObjectByHandle(ThreadHandle
,
567 THREAD_SUSPEND_RESUME
,
572 if (Status
!= STATUS_SUCCESS
)
577 (*SuspendCount
) = InterlockedDecrement(&Thread
->Tcb
.SuspendCount
);
578 if (Thread
->Tcb
.SuspendCount
<= 0)
580 Thread
->Tcb
.State
= THREAD_STATE_RUNNABLE
;
583 ObDereferenceObject(Thread
);
584 return(STATUS_SUCCESS
);
587 NTSTATUS STDCALL
NtSetContextThread(IN HANDLE ThreadHandle
,
590 return(ZwSetContextThread(ThreadHandle
,Context
));
593 NTSTATUS STDCALL
ZwSetContextThread(IN HANDLE ThreadHandle
,
599 NTSTATUS STDCALL
NtSuspendThread(IN HANDLE ThreadHandle
,
600 IN PULONG PreviousSuspendCount
)
602 return(ZwSuspendThread(ThreadHandle
,PreviousSuspendCount
));
605 NTSTATUS STDCALL
ZwSuspendThread(IN HANDLE ThreadHandle
,
606 IN PULONG PreviousSuspendCount
)
608 * FUNCTION: Increments a thread's suspend count
610 * ThreadHandle = Handle to the thread that should be resumed
611 * PreviousSuspendCount = The resulting/previous suspend count.
613 * A thread will be suspended if its suspend count is greater than 0.
614 * This procedure maps to the win32 SuspendThread function. (
615 * documentation about the the suspend count can be found here aswell )
616 * The suspend count is not increased if it is greater than
617 * MAXIMUM_SUSPEND_COUNT.
624 Status
= ObReferenceObjectByHandle(ThreadHandle
,
625 THREAD_SUSPEND_RESUME
,
630 if (Status
!= STATUS_SUCCESS
)
635 (*PreviousSuspendCount
) = InterlockedIncrement(&Thread
->Tcb
.SuspendCount
);
636 if (Thread
->Tcb
.SuspendCount
> 0)
638 Thread
->Tcb
.State
= THREAD_STATE_SUSPENDED
;
639 if (Thread
== PsGetCurrentThread())
645 ObDereferenceObject(Thread
);
646 return(STATUS_SUCCESS
);
649 NTSTATUS STDCALL
NtContinue(IN PCONTEXT Context
, IN CINT IrqLevel
)
651 return(ZwContinue(Context
,IrqLevel
));
654 NTSTATUS STDCALL
ZwContinue(IN PCONTEXT Context
, IN CINT IrqLevel
)
659 NTSTATUS STDCALL
NtYieldExecution(VOID
)
661 return(ZwYieldExecution());
664 NTSTATUS STDCALL
ZwYieldExecution(VOID
)
667 return(STATUS_SUCCESS
);