1 /* $Id: thread.c,v 1.81 2001/12/31 19:06:48 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>
26 #include <internal/ps.h>
27 #include <internal/ob.h>
28 #include <internal/pool.h>
31 #include <internal/debug.h>
33 /* TYPES *******************************************************************/
35 /* GLOBALS ******************************************************************/
37 POBJECT_TYPE EXPORTED PsThreadType
= NULL
;
39 KSPIN_LOCK PiThreadListLock
;
42 * PURPOSE: List of threads associated with each priority level
44 LIST_ENTRY PiThreadListHead
;
45 static LIST_ENTRY PriorityListHead
[MAXIMUM_PRIORITY
];
46 static BOOLEAN DoneInitYet
= FALSE
;
47 static PETHREAD IdleThreads
[MAXIMUM_PROCESSORS
];
48 ULONG PiNrThreads
= 0;
49 ULONG PiNrRunnableThreads
= 0;
51 static GENERIC_MAPPING PiThreadMapping
= {THREAD_READ
,
56 /* FUNCTIONS ***************************************************************/
58 PKTHREAD STDCALL
KeGetCurrentThread(VOID
)
60 return(KeGetCurrentKPCR()->CurrentThread
);
63 PETHREAD STDCALL
PsGetCurrentThread(VOID
)
65 PKTHREAD CurrentThread
= KeGetCurrentKPCR()->CurrentThread
;
66 return(CONTAINING_RECORD(CurrentThread
, ETHREAD
, Tcb
));
69 HANDLE STDCALL
PsGetCurrentThreadId(VOID
)
71 return(PsGetCurrentThread()->Cid
.UniqueThread
);
75 PsInsertIntoThreadList(KPRIORITY Priority
, PETHREAD Thread
)
77 if (Priority
>= MAXIMUM_PRIORITY
|| Priority
< 0)
79 DPRINT1("Invalid thread priority\n");
82 InsertTailList(&PriorityListHead
[Priority
], &Thread
->Tcb
.QueueListEntry
);
83 PiNrRunnableThreads
++;
86 VOID
PsDumpThreads(BOOLEAN IncludeSystem
)
88 PLIST_ENTRY current_entry
;
93 current_entry
= PiThreadListHead
.Flink
;
96 while (current_entry
!= &PiThreadListHead
)
101 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
102 Tcb
.ThreadListEntry
);
106 DbgPrint("Too many threads on list\n");
109 if (IncludeSystem
|| current
->ThreadsProcess
->UniqueProcessId
>= 6)
111 DbgPrint("current->Tcb.State %d PID.TID %d.%d Name %.8s\n",
112 current
->Tcb
.State
, current
->ThreadsProcess
->UniqueProcessId
,
113 current
->Cid
.UniqueThread
, current
->ThreadsProcess
->ImageFileName
);
114 if (current
->Tcb
.State
== THREAD_STATE_RUNNABLE
||
115 current
->Tcb
.State
== THREAD_STATE_SUSPENDED
||
116 current
->Tcb
.State
== THREAD_STATE_BLOCKED
)
118 Esp
= (PULONG
)current
->Tcb
.KernelStack
;
119 Ebp
= (PULONG
)Esp
[3];
120 DbgPrint("Ebp 0x%.8X\n", Ebp
);
122 while (Ebp
!= 0 && Ebp
>= (PULONG
)current
->Tcb
.StackLimit
)
124 DbgPrint("Frame: 0x%.8X Eip: 0x%.8X%s", Ebp
[0], Ebp
[1],
125 (i
% 2) == 1 ? "\n" : "");
126 Ebp
= (PULONG
)Ebp
[0];
134 current_entry
= current_entry
->Flink
;
138 static PETHREAD
PsScanThreadList (KPRIORITY Priority
, ULONG Affinity
)
141 PLIST_ENTRY current_entry
;
144 current_entry
= RemoveHeadList(&PriorityListHead
[Priority
]);
145 if (current_entry
!= &PriorityListHead
[Priority
])
147 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
157 PLIST_ENTRY current_entry
;
160 current_entry
= PriorityListHead
[Priority
].Flink
;
161 while (current_entry
!= &PriorityListHead
[Priority
])
163 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
165 assert(current
->Tcb
.State
== THREAD_STATE_RUNNABLE
);
166 DPRINT("current->Tcb.UserAffinity %x Affinity %x PID %d %d\n",
167 current
->Tcb
.UserAffinity
, Affinity
, current
->Cid
.UniqueThread
,
169 if (current
->Tcb
.UserAffinity
& Affinity
)
171 RemoveEntryList(¤t
->Tcb
.QueueListEntry
);
174 current_entry
= current_entry
->Flink
;
181 VOID
PsDispatchThreadNoLock (ULONG NewThreadStatus
)
183 KPRIORITY CurrentPriority
;
186 PKTHREAD KCurrentThread
= KeGetCurrentKPCR()->CurrentThread
;
187 PETHREAD CurrentThread
= CONTAINING_RECORD(KCurrentThread
, ETHREAD
, Tcb
);
189 DPRINT("PsDispatchThread() %d/%d\n", KeGetCurrentProcessorNumber(),
190 CurrentThread
->Cid
.UniqueThread
);
192 CurrentThread
->Tcb
.State
= NewThreadStatus
;
193 if (CurrentThread
->Tcb
.State
== THREAD_STATE_RUNNABLE
)
195 PiNrRunnableThreads
++;
196 PsInsertIntoThreadList(CurrentThread
->Tcb
.Priority
,
200 Affinity
= 1 << KeGetCurrentProcessorNumber();
201 for (CurrentPriority
= HIGH_PRIORITY
;
202 CurrentPriority
>= LOW_PRIORITY
;
205 Candidate
= PsScanThreadList(CurrentPriority
, Affinity
);
206 if (Candidate
== CurrentThread
)
208 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock
);
211 if (Candidate
!= NULL
)
215 DPRINT("Scheduling %x(%d)\n",Candidate
, CurrentPriority
);
217 Candidate
->Tcb
.State
= THREAD_STATE_RUNNING
;
219 OldThread
= CurrentThread
;
220 CurrentThread
= Candidate
;
222 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock
);
223 Ki386ContextSwitch(&CurrentThread
->Tcb
, &OldThread
->Tcb
);
228 DbgPrint("CRITICAL: No threads are runnable\n");
233 PsDispatchThread(ULONG NewThreadStatus
)
242 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
246 KeGetCurrentKPCR()->CurrentThread
->WaitIrql
= oldIrql
;
247 PsDispatchThreadNoLock(NewThreadStatus
);
248 KeLowerIrql(oldIrql
);
252 PsUnblockThread(PETHREAD Thread
, PNTSTATUS WaitStatus
)
256 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
257 if (WaitStatus
!= NULL
)
259 Thread
->Tcb
.WaitStatus
= *WaitStatus
;
261 Thread
->Tcb
.State
= THREAD_STATE_RUNNABLE
;
262 PsInsertIntoThreadList(Thread
->Tcb
.Priority
, Thread
);
263 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
267 PsBlockThread(PNTSTATUS Status
, UCHAR Alertable
, ULONG WaitMode
,
268 BOOLEAN DispatcherLock
, KIRQL WaitIrql
)
271 PETHREAD Thread
= PsGetCurrentThread();
273 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
277 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE
);
280 Thread
->Tcb
.Alertable
= Alertable
;
281 Thread
->Tcb
.WaitMode
= WaitMode
;
282 Thread
->Tcb
.WaitIrql
= WaitIrql
;
283 PsDispatchThreadNoLock(THREAD_STATE_BLOCKED
);
287 *Status
= Thread
->Tcb
.WaitStatus
;
289 KeLowerIrql(WaitIrql
);
293 PsFreezeAllThreads(PEPROCESS Process
)
295 * Used by the debugging code to freeze all the process's threads
296 * while the debugger is examining their state.
300 PLIST_ENTRY current_entry
;
303 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
305 current_entry
= Process
->ThreadListHead
.Flink
;
306 while (current_entry
!= &Process
->ThreadListHead
)
308 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
309 Tcb
.ProcessThreadListEntry
);
312 * We have to be careful here, we can't just set the freeze the
313 * thread inside kernel mode since it may be holding a lock.
316 current_entry
= current_entry
->Flink
;
319 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
323 PsApplicationProcessorInit(VOID
)
325 KeGetCurrentKPCR()->CurrentThread
=
326 (PVOID
)IdleThreads
[KeGetCurrentProcessorNumber()];
330 PsPrepareForApplicationProcessorInit(ULONG Id
)
333 HANDLE IdleThreadHandle
;
335 PsInitializeThread(NULL
,
341 IdleThread
->Tcb
.State
= THREAD_STATE_RUNNING
;
342 IdleThread
->Tcb
.FreezeCount
= 0;
343 IdleThread
->Tcb
.UserAffinity
= 1 << Id
;
344 IdleThread
->Tcb
.Priority
= LOW_PRIORITY
;
345 IdleThreads
[Id
] = IdleThread
;
347 NtClose(IdleThreadHandle
);
348 DbgPrint("IdleThread for Processor %d has PID %d\n",
349 Id
, IdleThread
->Cid
.UniqueThread
);
353 PsInitThreadManagment(VOID
)
355 * FUNCTION: Initialize thread managment
358 PETHREAD FirstThread
;
360 HANDLE FirstThreadHandle
;
362 KeInitializeSpinLock(&PiThreadListLock
);
363 for (i
=0; i
< MAXIMUM_PRIORITY
; i
++)
365 InitializeListHead(&PriorityListHead
[i
]);
368 InitializeListHead(&PiThreadListHead
);
370 PsThreadType
= ExAllocatePool(NonPagedPool
,sizeof(OBJECT_TYPE
));
372 RtlInitUnicodeString(&PsThreadType
->TypeName
, L
"Thread");
374 PsThreadType
->Tag
= TAG('T', 'H', 'R', 'T');
375 PsThreadType
->TotalObjects
= 0;
376 PsThreadType
->TotalHandles
= 0;
377 PsThreadType
->MaxObjects
= 0;
378 PsThreadType
->MaxHandles
= 0;
379 PsThreadType
->PagedPoolCharge
= 0;
380 PsThreadType
->NonpagedPoolCharge
= sizeof(ETHREAD
);
381 PsThreadType
->Mapping
= &PiThreadMapping
;
382 PsThreadType
->Dump
= NULL
;
383 PsThreadType
->Open
= NULL
;
384 PsThreadType
->Close
= PiCloseThread
;
385 PsThreadType
->Delete
= PiDeleteThread
;
386 PsThreadType
->Parse
= NULL
;
387 PsThreadType
->Security
= NULL
;
388 PsThreadType
->QueryName
= NULL
;
389 PsThreadType
->OkayToClose
= NULL
;
390 PsThreadType
->Create
= NULL
;
391 PsThreadType
->DuplicationNotify
= NULL
;
393 PsInitializeThread(NULL
,&FirstThread
,&FirstThreadHandle
,
394 THREAD_ALL_ACCESS
,NULL
, TRUE
);
395 HalInitFirstTask(FirstThread
);
396 FirstThread
->Tcb
.State
= THREAD_STATE_RUNNING
;
397 FirstThread
->Tcb
.FreezeCount
= 0;
398 KeGetCurrentKPCR()->CurrentThread
= (PVOID
)FirstThread
;
399 NtClose(FirstThreadHandle
);
401 DPRINT("FirstThread %x\n",FirstThread
);
408 * Sets thread's base priority relative to the process' base priority
409 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
412 KeSetBasePriorityThread (PKTHREAD Thread
,
415 Thread
->BasePriority
=
416 ((PETHREAD
)Thread
)->ThreadsProcess
->Pcb
.BasePriority
+ Increment
;
417 if (Thread
->BasePriority
< LOW_PRIORITY
)
418 Thread
->BasePriority
= LOW_PRIORITY
;
419 else if (Thread
->BasePriority
>= MAXIMUM_PRIORITY
)
420 Thread
->BasePriority
= HIGH_PRIORITY
;
421 Thread
->Priority
= Thread
->BasePriority
;
427 KeSetPriorityThread (PKTHREAD Thread
, KPRIORITY Priority
)
429 KPRIORITY OldPriority
;
432 if (Priority
< 0 || Priority
>= MAXIMUM_PRIORITY
)
437 OldPriority
= Thread
->Priority
;
438 Thread
->Priority
= (CHAR
)Priority
;
440 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
441 if (Thread
->State
== THREAD_STATE_RUNNABLE
)
443 RemoveEntryList(&Thread
->QueueListEntry
);
444 PsInsertIntoThreadList(Thread
->BasePriority
,
445 CONTAINING_RECORD(Thread
,ETHREAD
,Tcb
));
447 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
453 NtAlertResumeThread(IN HANDLE ThreadHandle
,
454 OUT PULONG SuspendCount
)
460 NTSTATUS STDCALL
NtAlertThread (IN HANDLE ThreadHandle
)
465 NTSTATUS ThreadStatus
;
467 Status
= ObReferenceObjectByHandle(ThreadHandle
,
468 THREAD_SUSPEND_RESUME
,
473 if (Status
!= STATUS_SUCCESS
)
478 ThreadStatus
= STATUS_ALERTED
;
479 (VOID
)PsUnblockThread(Thread
, &ThreadStatus
);
481 ObDereferenceObject(Thread
);
482 return(STATUS_SUCCESS
);
488 NtOpenThread(OUT PHANDLE ThreadHandle
,
489 IN ACCESS_MASK DesiredAccess
,
490 IN POBJECT_ATTRIBUTES ObjectAttributes
,
491 IN PCLIENT_ID ClientId
)
497 NtCallbackReturn (PVOID Result
,
506 NtW32Call (IN ULONG RoutineIndex
,
508 IN ULONG ArgumentLength
,
509 OUT PVOID
* Result OPTIONAL
,
510 OUT PULONG ResultLength OPTIONAL
)
516 NtContinue(IN PCONTEXT Context
,
517 IN BOOLEAN TestAlert
)
519 PKTRAP_FRAME TrapFrame
;
522 * Copy the supplied context over the register information that was saved
523 * on entry to kernel mode, it will then be restored on exit
524 * FIXME: Validate the context
526 TrapFrame
= KeGetCurrentThread()->TrapFrame
;
527 if (TrapFrame
== NULL
)
529 DbgPrint("NtContinue called but TrapFrame was NULL\n");
532 KeContextToTrapFrame(Context
, TrapFrame
);
533 return(STATUS_SUCCESS
);
538 NtYieldExecution(VOID
)
540 PsDispatchThread(THREAD_STATE_RUNNABLE
);
541 return(STATUS_SUCCESS
);