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>
29 #include <ntos/minmax.h>
30 #include <internal/ldr.h>
33 #include <internal/debug.h>
35 /* TYPES *******************************************************************/
37 /* GLOBALS ******************************************************************/
39 POBJECT_TYPE EXPORTED PsThreadType = NULL;
41 KSPIN_LOCK PiThreadListLock;
44 * PURPOSE: List of threads associated with each priority level
46 LIST_ENTRY PiThreadListHead;
47 static LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY];
48 static BOOLEAN DoneInitYet = FALSE;
49 static PETHREAD IdleThreads[MAXIMUM_PROCESSORS];
50 ULONG PiNrThreads = 0;
51 ULONG PiNrReadyThreads = 0;
52 static HANDLE PiReaperThreadHandle;
53 static KEVENT PiReaperThreadEvent;
54 static BOOL PiReaperThreadShouldTerminate = FALSE;
55 ULONG PiNrThreadsAwaitingReaping = 0;
57 static GENERIC_MAPPING PiThreadMapping = {THREAD_READ,
62 /* FUNCTIONS ***************************************************************/
64 PKTHREAD STDCALL KeGetCurrentThread(VOID)
66 return(KeGetCurrentKPCR()->CurrentThread);
69 PETHREAD STDCALL PsGetCurrentThread(VOID)
71 PKTHREAD CurrentThread = KeGetCurrentKPCR()->CurrentThread;
72 return(CONTAINING_RECORD(CurrentThread, ETHREAD, Tcb));
75 HANDLE STDCALL PsGetCurrentThreadId(VOID)
77 return(PsGetCurrentThread()->Cid.UniqueThread);
81 PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)
83 if (Priority >= MAXIMUM_PRIORITY || Priority < 0)
85 DPRINT1("Invalid thread priority\n");
88 InsertTailList(&PriorityListHead[Priority], &Thread->Tcb.QueueListEntry);
92 VOID PsDumpThreads(BOOLEAN IncludeSystem)
94 PLIST_ENTRY current_entry;
99 current_entry = PiThreadListHead.Flink;
102 while (current_entry != &PiThreadListHead)
107 current = CONTAINING_RECORD(current_entry, ETHREAD,
108 Tcb.ThreadListEntry);
112 DbgPrint("Too many threads on list\n");
115 if (IncludeSystem || current->ThreadsProcess->UniqueProcessId >= 6)
117 DbgPrint("current->Tcb.State %d PID.TID %d.%d Name %.8s Stack: \n",
119 current->ThreadsProcess->UniqueProcessId,
120 current->Cid.UniqueThread,
121 current->ThreadsProcess->ImageFileName);
122 if (current->Tcb.State == THREAD_STATE_READY ||
123 current->Tcb.State == THREAD_STATE_SUSPENDED ||
124 current->Tcb.State == THREAD_STATE_BLOCKED)
126 Esp = (PULONG)current->Tcb.KernelStack;
127 Ebp = (PULONG)Esp[3];
128 DbgPrint("Ebp 0x%.8X\n", Ebp);
130 while (Ebp != 0 && Ebp >= (PULONG)current->Tcb.StackLimit)
132 DbgPrint("%.8X %.8X%s", Ebp[0], Ebp[1],
133 (i % 8) == 7 ? "\n" : " ");
134 Ebp = (PULONG)Ebp[0];
143 current_entry = current_entry->Flink;
147 static PETHREAD PsScanThreadList (KPRIORITY Priority, ULONG Affinity)
149 PLIST_ENTRY current_entry;
152 current_entry = PriorityListHead[Priority].Flink;
153 while (current_entry != &PriorityListHead[Priority])
155 current = CONTAINING_RECORD(current_entry, ETHREAD,
157 assert(current->Tcb.State == THREAD_STATE_READY);
158 DPRINT("current->Tcb.UserAffinity %x Affinity %x PID %d %d\n",
159 current->Tcb.UserAffinity, Affinity, current->Cid.UniqueThread,
161 if (current->Tcb.UserAffinity & Affinity)
163 RemoveEntryList(¤t->Tcb.QueueListEntry);
166 current_entry = current_entry->Flink;
172 PiWakeupReaperThread(VOID)
174 KeSetEvent(&PiReaperThreadEvent, 0, FALSE);
178 PiReaperThreadMain(PVOID Ignored)
182 KeWaitForSingleObject(&PiReaperThreadEvent,
187 if (PiReaperThreadShouldTerminate)
189 PsTerminateSystemThread(0);
195 VOID PsDispatchThreadNoLock (ULONG NewThreadStatus)
197 KPRIORITY CurrentPriority;
200 PKTHREAD KCurrentThread = KeGetCurrentKPCR()->CurrentThread;
201 PETHREAD CurrentThread = CONTAINING_RECORD(KCurrentThread, ETHREAD, Tcb);
203 DPRINT("PsDispatchThread() %d/%d\n", KeGetCurrentProcessorNumber(),
204 CurrentThread->Cid.UniqueThread);
206 CurrentThread->Tcb.State = NewThreadStatus;
207 if (CurrentThread->Tcb.State == THREAD_STATE_READY)
210 PsInsertIntoThreadList(CurrentThread->Tcb.Priority,
213 if (CurrentThread->Tcb.State == THREAD_STATE_TERMINATED_1)
215 PiNrThreadsAwaitingReaping++;
218 Affinity = 1 << KeGetCurrentProcessorNumber();
219 for (CurrentPriority = HIGH_PRIORITY;
220 CurrentPriority >= LOW_PRIORITY;
223 Candidate = PsScanThreadList(CurrentPriority, Affinity);
224 if (Candidate == CurrentThread)
226 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
229 if (Candidate != NULL)
233 DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
235 Candidate->Tcb.State = THREAD_STATE_RUNNING;
237 OldThread = CurrentThread;
238 CurrentThread = Candidate;
240 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
241 if (PiNrThreadsAwaitingReaping > 0)
243 PiWakeupReaperThread();
245 KiArchContextSwitch(&CurrentThread->Tcb, &OldThread->Tcb);
249 CPRINT("CRITICAL: No threads are ready\n");
254 PsDispatchThread(ULONG NewThreadStatus)
263 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
267 KeGetCurrentKPCR()->CurrentThread->WaitIrql = oldIrql;
268 PsDispatchThreadNoLock(NewThreadStatus);
269 KeLowerIrql(oldIrql);
273 PsUnblockThread(PETHREAD Thread, PNTSTATUS WaitStatus)
277 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
278 if (WaitStatus != NULL)
280 Thread->Tcb.WaitStatus = *WaitStatus;
282 Thread->Tcb.State = THREAD_STATE_READY;
283 PsInsertIntoThreadList(Thread->Tcb.Priority, Thread);
284 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
288 PsBlockThread(PNTSTATUS Status, UCHAR Alertable, ULONG WaitMode,
289 BOOLEAN DispatcherLock, KIRQL WaitIrql)
292 PKTHREAD KThread = KeGetCurrentKPCR()->CurrentThread;
293 PETHREAD Thread = CONTAINING_RECORD (KThread, ETHREAD, Tcb);
294 PKWAIT_BLOCK WaitBlock;
296 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
298 if (KThread->ApcState.KernelApcPending)
302 KeAcquireDispatcherDatabaseLock(FALSE);
304 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
307 RemoveEntryList (&WaitBlock->WaitListEntry);
308 WaitBlock = WaitBlock->NextWaitBlock;
310 Thread->Tcb.WaitBlockList = NULL;
311 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
312 PsDispatchThreadNoLock (THREAD_STATE_READY);
315 *Status = STATUS_KERNEL_APC;
322 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
324 Thread->Tcb.Alertable = Alertable;
325 Thread->Tcb.WaitMode = WaitMode;
326 Thread->Tcb.WaitIrql = WaitIrql;
327 PsDispatchThreadNoLock(THREAD_STATE_BLOCKED);
330 *Status = Thread->Tcb.WaitStatus;
333 KeLowerIrql(WaitIrql);
337 PsFreezeAllThreads(PEPROCESS Process)
339 * Used by the debugging code to freeze all the process's threads
340 * while the debugger is examining their state.
344 PLIST_ENTRY current_entry;
347 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
349 current_entry = Process->ThreadListHead.Flink;
350 while (current_entry != &Process->ThreadListHead)
352 current = CONTAINING_RECORD(current_entry, ETHREAD,
353 Tcb.ProcessThreadListEntry);
356 * We have to be careful here, we can't just set the freeze the
357 * thread inside kernel mode since it may be holding a lock.
360 current_entry = current_entry->Flink;
363 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
367 PsApplicationProcessorInit(VOID)
369 KeGetCurrentKPCR()->CurrentThread =
370 (PVOID)IdleThreads[KeGetCurrentProcessorNumber()];
374 PsPrepareForApplicationProcessorInit(ULONG Id)
377 HANDLE IdleThreadHandle;
379 PsInitializeThread(NULL,
385 IdleThread->Tcb.State = THREAD_STATE_RUNNING;
386 IdleThread->Tcb.FreezeCount = 0;
387 IdleThread->Tcb.UserAffinity = 1 << Id;
388 IdleThread->Tcb.Priority = LOW_PRIORITY;
389 IdleThreads[Id] = IdleThread;
391 NtClose(IdleThreadHandle);
392 DPRINT("IdleThread for Processor %d has PID %d\n",
393 Id, IdleThread->Cid.UniqueThread);
397 PsInitThreadManagment(VOID)
399 * FUNCTION: Initialize thread managment
402 PETHREAD FirstThread;
404 HANDLE FirstThreadHandle;
407 KeInitializeSpinLock(&PiThreadListLock);
408 for (i=0; i < MAXIMUM_PRIORITY; i++)
410 InitializeListHead(&PriorityListHead[i]);
413 InitializeListHead(&PiThreadListHead);
415 PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
417 RtlInitUnicodeStringFromLiteral(&PsThreadType->TypeName, L"Thread");
419 PsThreadType->Tag = TAG('T', 'H', 'R', 'T');
420 PsThreadType->TotalObjects = 0;
421 PsThreadType->TotalHandles = 0;
422 PsThreadType->MaxObjects = 0;
423 PsThreadType->MaxHandles = 0;
424 PsThreadType->PagedPoolCharge = 0;
425 PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
426 PsThreadType->Mapping = &PiThreadMapping;
427 PsThreadType->Dump = NULL;
428 PsThreadType->Open = NULL;
429 PsThreadType->Close = NULL;
430 PsThreadType->Delete = PiDeleteThread;
431 PsThreadType->Parse = NULL;
432 PsThreadType->Security = NULL;
433 PsThreadType->QueryName = NULL;
434 PsThreadType->OkayToClose = NULL;
435 PsThreadType->Create = NULL;
436 PsThreadType->DuplicationNotify = NULL;
438 PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
439 THREAD_ALL_ACCESS,NULL, TRUE);
440 FirstThread->Tcb.State = THREAD_STATE_RUNNING;
441 FirstThread->Tcb.FreezeCount = 0;
442 KeGetCurrentKPCR()->CurrentThread = (PVOID)FirstThread;
443 NtClose(FirstThreadHandle);
445 DPRINT("FirstThread %x\n",FirstThread);
450 * Create the reaper thread
452 KeInitializeEvent(&PiReaperThreadEvent, SynchronizationEvent, FALSE);
453 Status = PsCreateSystemThread(&PiReaperThreadHandle,
460 if (!NT_SUCCESS(Status))
462 DPRINT1("PS: Failed to create reaper thread.\n");
468 KeSetBasePriorityThread (PKTHREAD Thread,
471 * Sets thread's base priority relative to the process' base priority
472 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
475 Thread->BasePriority =
476 ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
477 if (Thread->BasePriority < LOW_PRIORITY)
478 Thread->BasePriority = LOW_PRIORITY;
479 else if (Thread->BasePriority >= MAXIMUM_PRIORITY)
480 Thread->BasePriority = HIGH_PRIORITY;
481 Thread->Priority = Thread->BasePriority;
487 KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority)
489 KPRIORITY OldPriority;
492 if (Priority < 0 || Priority >= MAXIMUM_PRIORITY)
497 OldPriority = Thread->Priority;
498 Thread->Priority = (CHAR)Priority;
500 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
501 if (Thread->State == THREAD_STATE_READY)
503 RemoveEntryList(&Thread->QueueListEntry);
504 PsInsertIntoThreadList(Thread->BasePriority,
505 CONTAINING_RECORD(Thread,ETHREAD,Tcb));
507 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
513 NtAlertResumeThread(IN HANDLE ThreadHandle,
514 OUT PULONG SuspendCount)
520 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
524 NTSTATUS ThreadStatus;
526 Status = ObReferenceObjectByHandle(ThreadHandle,
527 THREAD_SUSPEND_RESUME,
532 if (Status != STATUS_SUCCESS)
537 ThreadStatus = STATUS_ALERTED;
538 (VOID)PsUnblockThread(Thread, &ThreadStatus);
540 ObDereferenceObject(Thread);
541 return(STATUS_SUCCESS);
545 NtOpenThread(OUT PHANDLE ThreadHandle,
546 IN ACCESS_MASK DesiredAccess,
547 IN POBJECT_ATTRIBUTES ObjectAttributes,
548 IN PCLIENT_ID ClientId)
554 NtContinue(IN PCONTEXT Context,
555 IN BOOLEAN TestAlert)
557 PKTRAP_FRAME TrapFrame;
560 * Copy the supplied context over the register information that was saved
561 * on entry to kernel mode, it will then be restored on exit
562 * FIXME: Validate the context
564 TrapFrame = KeGetCurrentThread()->TrapFrame;
565 if (TrapFrame == NULL)
567 CPRINT("NtContinue called but TrapFrame was NULL\n");
570 KeContextToTrapFrame(Context, TrapFrame);
571 return(STATUS_SUCCESS);
576 NtYieldExecution(VOID)
578 PsDispatchThread(THREAD_STATE_READY);
579 return(STATUS_SUCCESS);
584 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
585 OUT PEPROCESS *Process OPTIONAL,
586 OUT PETHREAD *Thread)
589 PLIST_ENTRY current_entry;
592 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
594 current_entry = PiThreadListHead.Flink;
595 while (current_entry != &PiThreadListHead)
597 current = CONTAINING_RECORD(current_entry,
599 Tcb.ThreadListEntry);
600 if (current->Cid.UniqueThread == Cid->UniqueThread &&
601 current->Cid.UniqueProcess == Cid->UniqueProcess)
604 *Process = current->ThreadsProcess;
606 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
607 return(STATUS_SUCCESS);
610 current_entry = current_entry->Flink;
613 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
615 return(STATUS_INVALID_PARAMETER);
620 PsLookupThreadByThreadId(IN PVOID ThreadId,
621 OUT PETHREAD *Thread)
624 PLIST_ENTRY current_entry;
627 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
629 current_entry = PiThreadListHead.Flink;
630 while (current_entry != &PiThreadListHead)
632 current = CONTAINING_RECORD(current_entry,
634 Tcb.ThreadListEntry);
635 if (current->Cid.UniqueThread == (HANDLE)ThreadId)
638 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
639 return(STATUS_SUCCESS);
642 current_entry = current_entry->Flink;
645 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
647 return(STATUS_INVALID_PARAMETER);