1 /* $Id: thread.c,v 1.46 2000/05/13 13:51:08 dwelch Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/thread.c
6 * PURPOSE: Thread managment
7 * PROGRAMMER: David Welch (welch@mcmail.com)
10 * 12/10/99: Phillip Susi: Thread priorities, and APC work
16 * All of the routines that manipulate the thread queue synchronize on
21 /* INCLUDES ****************************************************************/
23 #include <ddk/ntddk.h>
24 #include <internal/ke.h>
25 #include <internal/ob.h>
27 #include <internal/string.h>
28 #include <internal/hal.h>
29 #include <internal/ps.h>
30 #include <internal/ob.h>
33 #include <internal/debug.h>
35 /* TYPES *******************************************************************/
37 /* GLOBALS ******************************************************************/
39 POBJECT_TYPE PsThreadType
= NULL
;
41 #define NR_THREAD_PRIORITY_LEVELS (32)
42 #define THREAD_PRIORITY_MAX (15)
44 KSPIN_LOCK PiThreadListLock
;
47 * PURPOSE: List of threads associated with each priority level
49 LIST_ENTRY PiThreadListHead
;
50 static LIST_ENTRY PriorityListHead
[NR_THREAD_PRIORITY_LEVELS
];
51 static BOOLEAN DoneInitYet
= FALSE
;
52 ULONG PiNrThreads
= 0;
53 ULONG PiNrRunnableThreads
= 0;
55 static PETHREAD CurrentThread
= NULL
;
57 /* FUNCTIONS ***************************************************************/
59 VOID
PsFreezeProcessThreads(PEPROCESS Process
)
62 PLIST_ENTRY current_entry
;
65 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
66 current_entry
= PiThreadListHead
.Flink
;
68 while (current_entry
!= &PiThreadListHead
)
70 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
73 current_entry
= current_entry
->Flink
;
75 if (current
->ThreadsProcess
== Process
&&
76 current
!= PsGetCurrentThread())
78 PsFreezeOtherThread(current
);
81 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
84 VOID
PsUnfreezeProcessThreads(PEPROCESS Process
)
87 PLIST_ENTRY current_entry
;
90 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
91 current_entry
= PiThreadListHead
.Flink
;
93 while (current_entry
!= &PiThreadListHead
)
95 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
98 current_entry
= current_entry
->Flink
;
100 if (current
->ThreadsProcess
== Process
&&
101 current
!= PsGetCurrentThread())
103 PsUnfreezeOtherThread(current
);
106 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
109 PKTHREAD
KeGetCurrentThread(VOID
)
111 return(&(CurrentThread
->Tcb
));
114 PETHREAD
PsGetCurrentThread(VOID
)
116 return(CurrentThread
);
119 HANDLE
PsGetCurrentThreadId(VOID
)
121 return(CurrentThread
->Cid
.UniqueThread
);
124 static VOID
PsInsertIntoThreadList(KPRIORITY Priority
, PETHREAD Thread
)
126 // DPRINT("PsInsertIntoThreadList(Priority %x, Thread %x)\n",Priority,
128 // DPRINT("Offset %x\n", THREAD_PRIORITY_MAX + Priority);
130 InsertTailList(&PriorityListHead
[THREAD_PRIORITY_MAX
+Priority
],
131 &Thread
->Tcb
.QueueListEntry
);
132 PiNrRunnableThreads
++;
135 VOID
PsDumpThreads(VOID
)
137 PLIST_ENTRY current_entry
;
143 current_entry
= PiThreadListHead
.Flink
;
146 while (current_entry
!= &PiThreadListHead
)
148 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
149 Tcb
.ThreadListEntry
);
151 if (t
>= PiNrThreads
)
153 DbgPrint("Too many threads on list\n");
156 DbgPrint("current %x current->Tcb.State %d eip %x ",
157 current
, current
->Tcb
.State
,
158 current
->Tcb
.Context
.eip
);
159 // KeDumpStackFrames(0, 16);
160 DbgPrint("PID %d ", current
->ThreadsProcess
->UniqueProcessId
);
163 current_entry
= current_entry
->Flink
;
167 static PETHREAD
PsScanThreadList (KPRIORITY Priority
)
169 PLIST_ENTRY current_entry
;
172 // DPRINT("PsScanThreadList(Priority %d)\n",Priority);
174 current_entry
= RemoveHeadList(
175 &PriorityListHead
[THREAD_PRIORITY_MAX
+ Priority
]);
176 if (current_entry
!= &PriorityListHead
[THREAD_PRIORITY_MAX
+ Priority
])
178 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
190 VOID
PsDispatchThreadNoLock (ULONG NewThreadStatus
)
192 KPRIORITY CurrentPriority
;
195 CurrentThread
->Tcb
.State
= NewThreadStatus
;
196 PiNrRunnableThreads
--;
197 if (CurrentThread
->Tcb
.State
== THREAD_STATE_RUNNABLE
)
199 PsInsertIntoThreadList(CurrentThread
->Tcb
.Priority
,
203 for (CurrentPriority
= THREAD_PRIORITY_TIME_CRITICAL
;
204 (CurrentPriority
>= THREAD_PRIORITY_IDLE
);
207 Candidate
= PsScanThreadList(CurrentPriority
);
208 if (Candidate
== CurrentThread
)
210 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock
);
213 if (Candidate
!= NULL
)
215 DPRINT("Scheduling %x(%d)\n",Candidate
, CurrentPriority
);
217 Candidate
->Tcb
.State
= THREAD_STATE_RUNNING
;
219 CurrentThread
= Candidate
;
221 KeReleaseSpinLockFromDpcLevel( &PiThreadListLock
);
222 HalTaskSwitch(&CurrentThread
->Tcb
);
227 DbgPrint("CRITICAL: No threads are runnable\n");
231 VOID
PsDispatchThread(ULONG NewThreadStatus
)
240 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
241 CurrentThread
->Tcb
.WaitIrql
= oldIrql
; // save wait Irql
242 PsDispatchThreadNoLock(NewThreadStatus
);
243 KeLowerIrql(oldIrql
);
244 // DPRINT("oldIrql %d\n",oldIrql);
248 * Suspend and resume may only be called to suspend the current thread, except by apc.c
251 ULONG
PsUnfreezeThread(PETHREAD Thread
, PNTSTATUS WaitStatus
)
256 DPRINT("PsUnfreezeThread(Thread %x, WaitStatus %x)\n", Thread
, WaitStatus
);
258 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
260 Thread
->Tcb
.FreezeCount
--;
261 r
= Thread
->Tcb
.FreezeCount
;
263 if (WaitStatus
!= NULL
)
265 Thread
->Tcb
.WaitStatus
= *WaitStatus
;
270 DPRINT("Resuming thread\n");
271 Thread
->Tcb
.State
= THREAD_STATE_RUNNABLE
;
272 PsInsertIntoThreadList(Thread
->Tcb
.Priority
, Thread
);
275 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
279 VOID
PsUnfreezeOtherThread(PETHREAD Thread
)
283 Thread
->Tcb
.FreezeCount
--;
284 r
= Thread
->Tcb
.FreezeCount
;
288 Thread
->Tcb
.State
= THREAD_STATE_RUNNABLE
;
289 PsInsertIntoThreadList(Thread
->Tcb
.Priority
, Thread
);
293 VOID
PsFreezeOtherThread(PETHREAD Thread
)
297 Thread
->Tcb
.FreezeCount
++;
298 r
= Thread
->Tcb
.FreezeCount
;
305 if (Thread
->Tcb
.State
== THREAD_STATE_RUNNABLE
)
307 RemoveEntryList(&Thread
->Tcb
.QueueListEntry
);
309 Thread
->Tcb
.State
= THREAD_STATE_FROZEN
;
312 ULONG
PsFreezeThread(PETHREAD Thread
,
320 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
322 Thread
->Tcb
.FreezeCount
++;
323 r
= Thread
->Tcb
.FreezeCount
;
329 DPRINT("Not suspending thread\n");
330 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
334 Thread
->Tcb
.Alertable
= Alertable
;
335 Thread
->Tcb
.WaitMode
= WaitMode
;
337 if (PsGetCurrentThread() != Thread
)
339 DPRINT("Suspending other\n");
340 if (Thread
->Tcb
.State
== THREAD_STATE_RUNNABLE
)
342 RemoveEntryList(&Thread
->Tcb
.QueueListEntry
);
344 Thread
->Tcb
.State
= THREAD_STATE_FROZEN
;
345 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
349 DPRINT("Suspending self\n");
350 Thread
->Tcb
.WaitIrql
= oldIrql
;
351 PsDispatchThreadNoLock(THREAD_STATE_FROZEN
);
354 *Status
= Thread
->Tcb
.WaitStatus
;
356 KeLowerIrql(oldIrql
);
361 VOID
PsInitThreadManagment(VOID
)
363 * FUNCTION: Initialize thread managment
366 PETHREAD FirstThread
;
368 ANSI_STRING AnsiString
;
369 HANDLE FirstThreadHandle
;
371 KeInitializeSpinLock(&PiThreadListLock
);
372 for (i
=0; i
<NR_THREAD_PRIORITY_LEVELS
; i
++)
374 InitializeListHead(&PriorityListHead
[i
]);
376 InitializeListHead(&PiThreadListHead
);
378 PsThreadType
= ExAllocatePool(NonPagedPool
,sizeof(OBJECT_TYPE
));
380 RtlInitAnsiString(&AnsiString
,"Thread");
381 RtlAnsiStringToUnicodeString(&PsThreadType
->TypeName
,&AnsiString
,TRUE
);
383 PsThreadType
->TotalObjects
= 0;
384 PsThreadType
->TotalHandles
= 0;
385 PsThreadType
->MaxObjects
= 0;
386 PsThreadType
->MaxHandles
= 0;
387 PsThreadType
->PagedPoolCharge
= 0;
388 PsThreadType
->NonpagedPoolCharge
= sizeof(ETHREAD
);
389 PsThreadType
->Dump
= NULL
;
390 PsThreadType
->Open
= NULL
;
391 PsThreadType
->Close
= PiCloseThread
;
392 PsThreadType
->Delete
= PiDeleteThread
;
393 PsThreadType
->Parse
= NULL
;
394 PsThreadType
->Security
= NULL
;
395 PsThreadType
->QueryName
= NULL
;
396 PsThreadType
->OkayToClose
= NULL
;
397 PsThreadType
->Create
= NULL
;
399 PsInitializeThread(NULL
,&FirstThread
,&FirstThreadHandle
,
400 THREAD_ALL_ACCESS
,NULL
);
401 HalInitFirstTask(FirstThread
);
402 FirstThread
->Tcb
.State
= THREAD_STATE_RUNNING
;
403 FirstThread
->Tcb
.FreezeCount
= 0;
404 ZwClose(FirstThreadHandle
);
406 DPRINT("FirstThread %x\n",FirstThread
);
408 CurrentThread
= FirstThread
;
415 * Sets thread's base priority relative to the process' base priority
416 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
418 LONG
KeSetBasePriorityThread(PKTHREAD Thread
, LONG Increment
)
420 Thread
->BasePriority
= ((PETHREAD
)Thread
)->ThreadsProcess
->Pcb
.BasePriority
+ Increment
;
421 if( Thread
->BasePriority
< 0 )
422 Thread
->BasePriority
= 0;
423 else if( Thread
->BasePriority
>= NR_THREAD_PRIORITY_LEVELS
)
424 Thread
->BasePriority
= NR_THREAD_PRIORITY_LEVELS
- 1;
425 Thread
->Priority
= Thread
->BasePriority
;
430 KPRIORITY
KeSetPriorityThread(PKTHREAD Thread
, KPRIORITY Priority
)
432 KPRIORITY OldPriority
;
435 OldPriority
= Thread
->Priority
;
436 Thread
->Priority
= Priority
;
438 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
439 RemoveEntryList(&Thread
->QueueListEntry
);
440 PsInsertIntoThreadList(Thread
->BasePriority
,
441 CONTAINING_RECORD(Thread
,ETHREAD
,Tcb
));
442 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
447 NTSTATUS STDCALL
NtAlertResumeThread(IN HANDLE ThreadHandle
,
448 OUT PULONG SuspendCount
)
454 NTSTATUS STDCALL
NtAlertThread (IN HANDLE ThreadHandle
)
458 NTSTATUS ThreadStatus
;
460 Status
= ObReferenceObjectByHandle(ThreadHandle
,
461 THREAD_SUSPEND_RESUME
,
466 if (Status
!= STATUS_SUCCESS
)
471 ThreadStatus
= STATUS_ALERTED
;
472 (VOID
)PsUnfreezeThread(Thread
, &ThreadStatus
);
474 ObDereferenceObject(Thread
);
475 return(STATUS_SUCCESS
);
479 NTSTATUS STDCALL
NtGetContextThread (IN HANDLE ThreadHandle
,
480 OUT PCONTEXT Context
)
486 NTSTATUS STDCALL
NtOpenThread(OUT PHANDLE ThreadHandle
,
487 IN ACCESS_MASK DesiredAccess
,
488 IN POBJECT_ATTRIBUTES ObjectAttributes
,
489 IN PCLIENT_ID ClientId
)
495 NTSTATUS STDCALL
NtResumeThread (IN HANDLE ThreadHandle
,
496 IN PULONG SuspendCount
)
498 * FUNCTION: Decrements a thread's resume count
500 * ThreadHandle = Handle to the thread that should be resumed
501 * ResumeCount = The resulting resume count.
503 * A thread is resumed if its suspend count is 0. This procedure maps to
504 * the win32 ResumeThread function. ( documentation about the the suspend count can be found here aswell )
509 return(STATUS_UNSUCCESSFUL
);
513 NTSTATUS STDCALL
NtSetContextThread (IN HANDLE ThreadHandle
,
520 NTSTATUS STDCALL
NtSuspendThread (IN HANDLE ThreadHandle
,
521 IN PULONG PreviousSuspendCount
)
523 * FUNCTION: Increments a thread's suspend count
525 * ThreadHandle = Handle to the thread that should be resumed
526 * PreviousSuspendCount = The resulting/previous suspend count.
528 * A thread will be suspended if its suspend count is greater than 0.
529 * This procedure maps to the win32 SuspendThread function. (
530 * documentation about the the suspend count can be found here aswell )
531 * The suspend count is not increased if it is greater than
532 * MAXIMUM_SUSPEND_COUNT.
537 return(STATUS_UNSUCCESSFUL
);
541 NTSTATUS STDCALL
NtContinue(IN PCONTEXT Context
,
546 StackTop
= KeGetStackTopThread(PsGetCurrentThread());
548 memcpy(StackTop
, Context
, sizeof(CONTEXT
));
550 return(STATUS_SUCCESS
);
554 NTSTATUS STDCALL
NtYieldExecution(VOID
)
556 PsDispatchThread(THREAD_STATE_RUNNABLE
);
557 return(STATUS_SUCCESS
);