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 static LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY];
47 static BOOLEAN DoneInitYet = FALSE;
48 LIST_ENTRY PiThreadListHead;
50 static PETHREAD IdleThreads[MAXIMUM_PROCESSORS];
51 #endif /* LIBCAPTIVE */
52 ULONG PiNrThreads = 0;
54 ULONG PiNrReadyThreads = 0;
55 static HANDLE PiReaperThreadHandle;
56 static KEVENT PiReaperThreadEvent;
57 static BOOL PiReaperThreadShouldTerminate = FALSE;
58 ULONG PiNrThreadsAwaitingReaping = 0;
59 #endif /* LIBCAPTIVE */
61 static GENERIC_MAPPING PiThreadMapping = {THREAD_READ,
66 /* FUNCTIONS ***************************************************************/
70 PKTHREAD STDCALL KeGetCurrentThread(VOID)
72 return(KeGetCurrentKPCR()->CurrentThread);
75 #endif /* LIBCAPTIVE */
77 PETHREAD STDCALL PsGetCurrentThread(VOID)
79 PKTHREAD CurrentThread = KeGetCurrentKPCR()->CurrentThread;
80 return(CONTAINING_RECORD(CurrentThread, ETHREAD, Tcb));
85 HANDLE STDCALL PsGetCurrentThreadId(VOID)
87 return(PsGetCurrentThread()->Cid.UniqueThread);
91 PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)
93 if (Priority >= MAXIMUM_PRIORITY || Priority < 0)
95 DPRINT1("Invalid thread priority\n");
98 InsertTailList(&PriorityListHead[Priority], &Thread->Tcb.QueueListEntry);
102 VOID PsDumpThreads(BOOLEAN IncludeSystem)
104 PLIST_ENTRY current_entry;
109 current_entry = PiThreadListHead.Flink;
112 while (current_entry != &PiThreadListHead)
117 current = CONTAINING_RECORD(current_entry, ETHREAD,
118 Tcb.ThreadListEntry);
122 DbgPrint("Too many threads on list\n");
125 if (IncludeSystem || current->ThreadsProcess->UniqueProcessId >= 6)
127 DbgPrint("current->Tcb.State %d PID.TID %d.%d Name %.8s Stack: \n",
129 current->ThreadsProcess->UniqueProcessId,
130 current->Cid.UniqueThread,
131 current->ThreadsProcess->ImageFileName);
132 if (current->Tcb.State == THREAD_STATE_READY ||
133 current->Tcb.State == THREAD_STATE_SUSPENDED ||
134 current->Tcb.State == THREAD_STATE_BLOCKED)
136 Esp = (PULONG)current->Tcb.KernelStack;
137 Ebp = (PULONG)Esp[3];
138 DbgPrint("Ebp 0x%.8X\n", Ebp);
140 while (Ebp != 0 && Ebp >= (PULONG)current->Tcb.StackLimit)
142 DbgPrint("%.8X %.8X%s", Ebp[0], Ebp[1],
143 (i % 8) == 7 ? "\n" : " ");
144 Ebp = (PULONG)Ebp[0];
153 current_entry = current_entry->Flink;
157 static PETHREAD PsScanThreadList (KPRIORITY Priority, ULONG Affinity)
159 PLIST_ENTRY current_entry;
162 current_entry = PriorityListHead[Priority].Flink;
163 while (current_entry != &PriorityListHead[Priority])
165 current = CONTAINING_RECORD(current_entry, ETHREAD,
167 assert(current->Tcb.State == THREAD_STATE_READY);
168 DPRINT("current->Tcb.UserAffinity %x Affinity %x PID %d %d\n",
169 current->Tcb.UserAffinity, Affinity, current->Cid.UniqueThread,
171 if (current->Tcb.UserAffinity & Affinity)
173 RemoveEntryList(¤t->Tcb.QueueListEntry);
176 current_entry = current_entry->Flink;
182 PiWakeupReaperThread(VOID)
184 KeSetEvent(&PiReaperThreadEvent, 0, FALSE);
188 PiReaperThreadMain(PVOID Ignored)
192 KeWaitForSingleObject(&PiReaperThreadEvent,
197 if (PiReaperThreadShouldTerminate)
199 PsTerminateSystemThread(0);
205 VOID PsDispatchThreadNoLock (ULONG NewThreadStatus)
207 KPRIORITY CurrentPriority;
210 PKTHREAD KCurrentThread = KeGetCurrentKPCR()->CurrentThread;
211 PETHREAD CurrentThread = CONTAINING_RECORD(KCurrentThread, ETHREAD, Tcb);
213 DPRINT("PsDispatchThread() %d/%d\n", KeGetCurrentProcessorNumber(),
214 CurrentThread->Cid.UniqueThread);
216 CurrentThread->Tcb.State = NewThreadStatus;
217 if (CurrentThread->Tcb.State == THREAD_STATE_READY)
220 PsInsertIntoThreadList(CurrentThread->Tcb.Priority,
223 if (CurrentThread->Tcb.State == THREAD_STATE_TERMINATED_1)
225 PiNrThreadsAwaitingReaping++;
228 Affinity = 1 << KeGetCurrentProcessorNumber();
229 for (CurrentPriority = HIGH_PRIORITY;
230 CurrentPriority >= LOW_PRIORITY;
233 Candidate = PsScanThreadList(CurrentPriority, Affinity);
234 if (Candidate == CurrentThread)
236 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
239 if (Candidate != NULL)
243 DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
245 Candidate->Tcb.State = THREAD_STATE_RUNNING;
247 OldThread = CurrentThread;
248 CurrentThread = Candidate;
250 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
251 if (PiNrThreadsAwaitingReaping > 0)
253 PiWakeupReaperThread();
255 KiArchContextSwitch(&CurrentThread->Tcb, &OldThread->Tcb);
259 CPRINT("CRITICAL: No threads are ready\n");
264 PsDispatchThread(ULONG NewThreadStatus)
273 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
277 KeGetCurrentKPCR()->CurrentThread->WaitIrql = oldIrql;
278 PsDispatchThreadNoLock(NewThreadStatus);
279 KeLowerIrql(oldIrql);
283 PsUnblockThread(PETHREAD Thread, PNTSTATUS WaitStatus)
287 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
288 if (WaitStatus != NULL)
290 Thread->Tcb.WaitStatus = *WaitStatus;
292 Thread->Tcb.State = THREAD_STATE_READY;
293 PsInsertIntoThreadList(Thread->Tcb.Priority, Thread);
294 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
298 PsBlockThread(PNTSTATUS Status, UCHAR Alertable, ULONG WaitMode,
299 BOOLEAN DispatcherLock, KIRQL WaitIrql)
302 PKTHREAD KThread = KeGetCurrentKPCR()->CurrentThread;
303 PETHREAD Thread = CONTAINING_RECORD (KThread, ETHREAD, Tcb);
304 PKWAIT_BLOCK WaitBlock;
306 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
308 if (KThread->ApcState.KernelApcPending)
312 KeAcquireDispatcherDatabaseLock(FALSE);
314 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
317 RemoveEntryList (&WaitBlock->WaitListEntry);
318 WaitBlock = WaitBlock->NextWaitBlock;
320 Thread->Tcb.WaitBlockList = NULL;
321 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
322 PsDispatchThreadNoLock (THREAD_STATE_READY);
325 *Status = STATUS_KERNEL_APC;
332 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
334 Thread->Tcb.Alertable = Alertable;
335 Thread->Tcb.WaitMode = WaitMode;
336 Thread->Tcb.WaitIrql = WaitIrql;
337 PsDispatchThreadNoLock(THREAD_STATE_BLOCKED);
340 *Status = Thread->Tcb.WaitStatus;
343 KeLowerIrql(WaitIrql);
347 PsFreezeAllThreads(PEPROCESS Process)
349 * Used by the debugging code to freeze all the process's threads
350 * while the debugger is examining their state.
354 PLIST_ENTRY current_entry;
357 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
359 current_entry = Process->ThreadListHead.Flink;
360 while (current_entry != &Process->ThreadListHead)
362 current = CONTAINING_RECORD(current_entry, ETHREAD,
363 Tcb.ProcessThreadListEntry);
366 * We have to be careful here, we can't just set the freeze the
367 * thread inside kernel mode since it may be holding a lock.
370 current_entry = current_entry->Flink;
373 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
377 PsApplicationProcessorInit(VOID)
379 KeGetCurrentKPCR()->CurrentThread =
380 (PVOID)IdleThreads[KeGetCurrentProcessorNumber()];
384 PsPrepareForApplicationProcessorInit(ULONG Id)
387 HANDLE IdleThreadHandle;
389 PsInitializeThread(NULL,
395 IdleThread->Tcb.State = THREAD_STATE_RUNNING;
396 IdleThread->Tcb.FreezeCount = 0;
397 IdleThread->Tcb.UserAffinity = 1 << Id;
398 IdleThread->Tcb.Priority = LOW_PRIORITY;
399 IdleThreads[Id] = IdleThread;
401 NtClose(IdleThreadHandle);
402 DPRINT("IdleThread for Processor %d has PID %d\n",
403 Id, IdleThread->Cid.UniqueThread);
406 #endif /* LIBCAPTIVE */
409 PsInitThreadManagment(VOID)
411 * FUNCTION: Initialize thread managment
414 PETHREAD FirstThread;
416 HANDLE FirstThreadHandle;
419 #endif /* LIBCAPTIVE */
421 KeInitializeSpinLock(&PiThreadListLock);
422 for (i=0; i < MAXIMUM_PRIORITY; i++)
424 InitializeListHead(&PriorityListHead[i]);
427 InitializeListHead(&PiThreadListHead);
429 PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
431 RtlInitUnicodeStringFromLiteral(&PsThreadType->TypeName, REACTOS_UCS2(L"Thread"));
433 PsThreadType->Tag = TAG('T', 'H', 'R', 'T');
434 PsThreadType->TotalObjects = 0;
435 PsThreadType->TotalHandles = 0;
436 PsThreadType->MaxObjects = 0;
437 PsThreadType->MaxHandles = 0;
438 PsThreadType->PagedPoolCharge = 0;
439 PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
440 PsThreadType->Mapping = &PiThreadMapping;
441 PsThreadType->Dump = NULL;
442 PsThreadType->Open = NULL;
443 PsThreadType->Close = NULL;
445 PsThreadType->Delete = PiDeleteThread;
446 #else /* !LIBCAPTIVE */
447 PsThreadType->Delete = NULL;
448 #endif /* !LIBCAPTIVE */
449 PsThreadType->Parse = NULL;
450 PsThreadType->Security = NULL;
451 PsThreadType->QueryName = NULL;
452 PsThreadType->OkayToClose = NULL;
453 PsThreadType->Create = NULL;
454 PsThreadType->DuplicationNotify = NULL;
456 PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
457 THREAD_ALL_ACCESS,NULL, TRUE);
458 FirstThread->Tcb.State = THREAD_STATE_RUNNING;
459 FirstThread->Tcb.FreezeCount = 0;
460 KeGetCurrentKPCR()->CurrentThread = (PVOID)FirstThread;
461 NtClose(FirstThreadHandle);
463 DPRINT("FirstThread %x\n",FirstThread);
469 * Create the reaper thread
471 KeInitializeEvent(&PiReaperThreadEvent, SynchronizationEvent, FALSE);
472 Status = PsCreateSystemThread(&PiReaperThreadHandle,
479 if (!NT_SUCCESS(Status))
481 DPRINT1("PS: Failed to create reaper thread.\n");
484 #endif /* LIBCAPTIVE */
490 KeSetBasePriorityThread (PKTHREAD Thread,
493 * Sets thread's base priority relative to the process' base priority
494 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
497 Thread->BasePriority =
498 ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
499 if (Thread->BasePriority < LOW_PRIORITY)
500 Thread->BasePriority = LOW_PRIORITY;
501 else if (Thread->BasePriority >= MAXIMUM_PRIORITY)
502 Thread->BasePriority = HIGH_PRIORITY;
503 Thread->Priority = Thread->BasePriority;
509 KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority)
511 KPRIORITY OldPriority;
514 if (Priority < 0 || Priority >= MAXIMUM_PRIORITY)
519 OldPriority = Thread->Priority;
520 Thread->Priority = (CHAR)Priority;
522 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
523 if (Thread->State == THREAD_STATE_READY)
525 RemoveEntryList(&Thread->QueueListEntry);
526 PsInsertIntoThreadList(Thread->BasePriority,
527 CONTAINING_RECORD(Thread,ETHREAD,Tcb));
529 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
535 NtAlertResumeThread(IN HANDLE ThreadHandle,
536 OUT PULONG SuspendCount)
542 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
546 NTSTATUS ThreadStatus;
548 Status = ObReferenceObjectByHandle(ThreadHandle,
549 THREAD_SUSPEND_RESUME,
554 if (Status != STATUS_SUCCESS)
559 ThreadStatus = STATUS_ALERTED;
560 (VOID)PsUnblockThread(Thread, &ThreadStatus);
562 ObDereferenceObject(Thread);
563 return(STATUS_SUCCESS);
567 NtOpenThread(OUT PHANDLE ThreadHandle,
568 IN ACCESS_MASK DesiredAccess,
569 IN POBJECT_ATTRIBUTES ObjectAttributes,
570 IN PCLIENT_ID ClientId)
576 NtContinue(IN PCONTEXT Context,
577 IN BOOLEAN TestAlert)
579 PKTRAP_FRAME TrapFrame;
582 * Copy the supplied context over the register information that was saved
583 * on entry to kernel mode, it will then be restored on exit
584 * FIXME: Validate the context
586 TrapFrame = KeGetCurrentThread()->TrapFrame;
587 if (TrapFrame == NULL)
589 CPRINT("NtContinue called but TrapFrame was NULL\n");
592 KeContextToTrapFrame(Context, TrapFrame);
593 return(STATUS_SUCCESS);
598 NtYieldExecution(VOID)
600 PsDispatchThread(THREAD_STATE_READY);
601 return(STATUS_SUCCESS);
606 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
607 OUT PEPROCESS *Process OPTIONAL,
608 OUT PETHREAD *Thread)
611 PLIST_ENTRY current_entry;
614 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
616 current_entry = PiThreadListHead.Flink;
617 while (current_entry != &PiThreadListHead)
619 current = CONTAINING_RECORD(current_entry,
621 Tcb.ThreadListEntry);
622 if (current->Cid.UniqueThread == Cid->UniqueThread &&
623 current->Cid.UniqueProcess == Cid->UniqueProcess)
626 *Process = current->ThreadsProcess;
628 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
629 return(STATUS_SUCCESS);
632 current_entry = current_entry->Flink;
635 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
637 return(STATUS_INVALID_PARAMETER);
642 PsLookupThreadByThreadId(IN PVOID ThreadId,
643 OUT PETHREAD *Thread)
646 PLIST_ENTRY current_entry;
649 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
651 current_entry = PiThreadListHead.Flink;
652 while (current_entry != &PiThreadListHead)
654 current = CONTAINING_RECORD(current_entry,
656 Tcb.ThreadListEntry);
657 if (current->Cid.UniqueThread == (HANDLE)ThreadId)
660 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
661 return(STATUS_SUCCESS);
664 current_entry = current_entry->Flink;
667 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
669 return(STATUS_INVALID_PARAMETER);
672 #endif /* LIBCAPTIVE */