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 ULONG PriorityListMask = 0;
49 static BOOLEAN DoneInitYet = FALSE;
50 static PETHREAD IdleThreads[MAXIMUM_PROCESSORS];
51 ULONG PiNrThreads = 0;
52 ULONG PiNrReadyThreads = 0;
53 static HANDLE PiReaperThreadHandle;
54 static KEVENT PiReaperThreadEvent;
55 static BOOL PiReaperThreadShouldTerminate = FALSE;
56 ULONG PiNrThreadsAwaitingReaping = 0;
58 static GENERIC_MAPPING PiThreadMapping = {THREAD_READ,
63 /* FUNCTIONS ***************************************************************/
68 PKTHREAD STDCALL KeGetCurrentThread(VOID)
70 return(((PIKPCR) KeGetCurrentKPCR())->CurrentThread);
76 HANDLE STDCALL PsGetCurrentThreadId(VOID)
78 return(PsGetCurrentThread()->Cid.UniqueThread);
82 PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)
84 assert(THREAD_STATE_READY == Thread->Tcb.State);
85 if (Priority >= MAXIMUM_PRIORITY || Priority < LOW_PRIORITY)
87 DPRINT1("Invalid thread priority (%d)\n", Priority);
90 InsertTailList(&PriorityListHead[Priority], &Thread->Tcb.QueueListEntry);
91 PriorityListMask |= (1 << Priority);
95 static VOID PsRemoveFromThreadList(PETHREAD Thread)
97 assert(THREAD_STATE_READY == Thread->Tcb.State);
98 RemoveEntryList(&Thread->Tcb.QueueListEntry);
99 if (IsListEmpty(&PriorityListHead[(ULONG)Thread->Tcb.Priority]))
101 PriorityListMask &= ~(1 << Thread->Tcb.Priority);
107 VOID PsDumpThreads(BOOLEAN IncludeSystem)
109 PLIST_ENTRY current_entry;
114 current_entry = PiThreadListHead.Flink;
117 while (current_entry != &PiThreadListHead)
122 current = CONTAINING_RECORD(current_entry, ETHREAD,
123 Tcb.ThreadListEntry);
127 DbgPrint("Too many threads on list\n");
130 if (IncludeSystem || current->ThreadsProcess->UniqueProcessId >= 6)
132 DbgPrint("current->Tcb.State %d PID.TID %d.%d Name %.8s Stack: \n",
134 current->ThreadsProcess->UniqueProcessId,
135 current->Cid.UniqueThread,
136 current->ThreadsProcess->ImageFileName);
137 if (current->Tcb.State == THREAD_STATE_READY ||
138 current->Tcb.State == THREAD_STATE_SUSPENDED ||
139 current->Tcb.State == THREAD_STATE_BLOCKED)
141 Esp = (PULONG)current->Tcb.KernelStack;
142 Ebp = (PULONG)Esp[3];
143 DbgPrint("Ebp 0x%.8X\n", Ebp);
145 while (Ebp != 0 && Ebp >= (PULONG)current->Tcb.StackLimit)
147 DbgPrint("%.8X %.8X%s", Ebp[0], Ebp[1],
148 (i % 8) == 7 ? "\n" : " ");
149 Ebp = (PULONG)Ebp[0];
158 current_entry = current_entry->Flink;
162 static PETHREAD PsScanThreadList (KPRIORITY Priority, ULONG Affinity)
164 PLIST_ENTRY current_entry;
168 Mask = (1 << Priority);
169 if (PriorityListMask & Mask)
171 current_entry = PriorityListHead[Priority].Flink;
172 while (current_entry != &PriorityListHead[Priority])
174 current = CONTAINING_RECORD(current_entry, ETHREAD,
176 if (current->Tcb.State != THREAD_STATE_READY)
178 DPRINT1("%d/%d\n", current->Cid.UniqueThread, current->Tcb.State);
180 assert(current->Tcb.State == THREAD_STATE_READY);
181 DPRINT("current->Tcb.UserAffinity %x Affinity %x PID %d %d\n",
182 current->Tcb.UserAffinity, Affinity, current->Cid.UniqueThread,
184 if (current->Tcb.UserAffinity & Affinity)
186 PsRemoveFromThreadList(current);
189 current_entry = current_entry->Flink;
196 PiWakeupReaperThread(VOID)
198 KeSetEvent(&PiReaperThreadEvent, 0, FALSE);
202 PiReaperThreadMain(PVOID Ignored)
206 KeWaitForSingleObject(&PiReaperThreadEvent,
211 if (PiReaperThreadShouldTerminate)
213 PsTerminateSystemThread(0);
219 VOID PsDispatchThreadNoLock (ULONG NewThreadStatus)
221 KPRIORITY CurrentPriority;
224 PKTHREAD KCurrentThread = ((PIKPCR) KeGetCurrentKPCR())->CurrentThread;
225 PETHREAD CurrentThread = CONTAINING_RECORD(KCurrentThread, ETHREAD, Tcb);
227 DPRINT("PsDispatchThread() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
228 CurrentThread->Cid.UniqueThread, NewThreadStatus, CurrentThread->Tcb.State);
230 CurrentThread->Tcb.State = NewThreadStatus;
231 if (CurrentThread->Tcb.State == THREAD_STATE_READY)
233 PsInsertIntoThreadList(CurrentThread->Tcb.Priority,
236 if (CurrentThread->Tcb.State == THREAD_STATE_TERMINATED_1)
238 PiNrThreadsAwaitingReaping++;
241 Affinity = 1 << KeGetCurrentProcessorNumber();
242 for (CurrentPriority = HIGH_PRIORITY;
243 CurrentPriority >= LOW_PRIORITY;
246 Candidate = PsScanThreadList(CurrentPriority, Affinity);
247 if (Candidate == CurrentThread)
249 Candidate->Tcb.State = THREAD_STATE_RUNNING;
250 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
253 if (Candidate != NULL)
257 DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
259 Candidate->Tcb.State = THREAD_STATE_RUNNING;
261 OldThread = CurrentThread;
262 CurrentThread = Candidate;
265 * This code is moved to the end of KiArchContextSwitch.
266 * It should be execute after the context switch.
268 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
269 if (PiNrThreadsAwaitingReaping > 0)
271 PiWakeupReaperThread();
274 KiArchContextSwitch(&CurrentThread->Tcb, &OldThread->Tcb);
278 CPRINT("CRITICAL: No threads are ready\n");
283 PsDispatchThread(ULONG NewThreadStatus)
292 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
296 ((PIKPCR) KeGetCurrentKPCR())->CurrentThread->WaitIrql = oldIrql;
297 PsDispatchThreadNoLock(NewThreadStatus);
298 KeLowerIrql(oldIrql);
302 PsUnblockThread(PETHREAD Thread, PNTSTATUS WaitStatus)
306 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
307 if (THREAD_STATE_TERMINATED_1 == Thread->Tcb.State ||
308 THREAD_STATE_TERMINATED_2 == Thread->Tcb.State)
310 DPRINT("Can't unblock thread %d because it's terminating\n",
311 Thread->Cid.UniqueThread);
313 else if (THREAD_STATE_READY == Thread->Tcb.State ||
314 THREAD_STATE_RUNNING == Thread->Tcb.State)
316 DPRINT("Can't unblock thread %d because it's ready or running\n",
317 Thread->Cid.UniqueThread);
321 if (WaitStatus != NULL)
323 Thread->Tcb.WaitStatus = *WaitStatus;
325 Thread->Tcb.State = THREAD_STATE_READY;
326 PsInsertIntoThreadList(Thread->Tcb.Priority, Thread);
328 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
332 PsBlockThread(PNTSTATUS Status, UCHAR Alertable, ULONG WaitMode,
333 BOOLEAN DispatcherLock, KIRQL WaitIrql, UCHAR WaitReason)
338 PKWAIT_BLOCK WaitBlock;
340 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
342 KThread = ((PIKPCR) KeGetCurrentKPCR())->CurrentThread;
343 Thread = CONTAINING_RECORD (KThread, ETHREAD, Tcb);
344 if (KThread->ApcState.KernelApcPending)
348 KeAcquireDispatcherDatabaseLock(FALSE);
350 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
353 RemoveEntryList (&WaitBlock->WaitListEntry);
354 WaitBlock = WaitBlock->NextWaitBlock;
356 Thread->Tcb.WaitBlockList = NULL;
357 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
358 PsDispatchThreadNoLock (THREAD_STATE_READY);
361 *Status = STATUS_KERNEL_APC;
368 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
370 Thread->Tcb.Alertable = Alertable;
371 Thread->Tcb.WaitMode = WaitMode;
372 Thread->Tcb.WaitIrql = WaitIrql;
373 Thread->Tcb.WaitReason = WaitReason;
374 PsDispatchThreadNoLock(THREAD_STATE_BLOCKED);
378 *Status = Thread->Tcb.WaitStatus;
381 KeLowerIrql(WaitIrql);
385 PsFreezeAllThreads(PEPROCESS Process)
387 * Used by the debugging code to freeze all the process's threads
388 * while the debugger is examining their state.
392 PLIST_ENTRY current_entry;
395 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
397 current_entry = Process->ThreadListHead.Flink;
398 while (current_entry != &Process->ThreadListHead)
400 current = CONTAINING_RECORD(current_entry, ETHREAD,
401 Tcb.ProcessThreadListEntry);
404 * We have to be careful here, we can't just set the freeze the
405 * thread inside kernel mode since it may be holding a lock.
408 current_entry = current_entry->Flink;
411 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
415 PsApplicationProcessorInit(VOID)
417 ((PIKPCR) KeGetCurrentKPCR())->CurrentThread =
418 (PVOID)IdleThreads[KeGetCurrentProcessorNumber()];
422 PsPrepareForApplicationProcessorInit(ULONG Id)
425 HANDLE IdleThreadHandle;
427 PsInitializeThread(NULL,
433 IdleThread->Tcb.State = THREAD_STATE_RUNNING;
434 IdleThread->Tcb.FreezeCount = 0;
435 IdleThread->Tcb.UserAffinity = 1 << Id;
436 IdleThread->Tcb.Priority = LOW_PRIORITY;
437 IdleThreads[Id] = IdleThread;
439 NtClose(IdleThreadHandle);
440 DPRINT("IdleThread for Processor %d has PID %d\n",
441 Id, IdleThread->Cid.UniqueThread);
445 PsInitThreadManagment(VOID)
447 * FUNCTION: Initialize thread managment
450 PETHREAD FirstThread;
452 HANDLE FirstThreadHandle;
455 KeInitializeSpinLock(&PiThreadListLock);
456 for (i=0; i < MAXIMUM_PRIORITY; i++)
458 InitializeListHead(&PriorityListHead[i]);
461 InitializeListHead(&PiThreadListHead);
463 PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
465 RtlInitUnicodeStringFromLiteral(&PsThreadType->TypeName, L"Thread");
467 PsThreadType->Tag = TAG('T', 'H', 'R', 'T');
468 PsThreadType->TotalObjects = 0;
469 PsThreadType->TotalHandles = 0;
470 PsThreadType->MaxObjects = 0;
471 PsThreadType->MaxHandles = 0;
472 PsThreadType->PagedPoolCharge = 0;
473 PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
474 PsThreadType->Mapping = &PiThreadMapping;
475 PsThreadType->Dump = NULL;
476 PsThreadType->Open = NULL;
477 PsThreadType->Close = NULL;
478 PsThreadType->Delete = PiDeleteThread;
479 PsThreadType->Parse = NULL;
480 PsThreadType->Security = NULL;
481 PsThreadType->QueryName = NULL;
482 PsThreadType->OkayToClose = NULL;
483 PsThreadType->Create = NULL;
484 PsThreadType->DuplicationNotify = NULL;
486 PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
487 THREAD_ALL_ACCESS,NULL, TRUE);
488 FirstThread->Tcb.State = THREAD_STATE_RUNNING;
489 FirstThread->Tcb.FreezeCount = 0;
490 ((PIKPCR) KeGetCurrentKPCR())->CurrentThread = (PVOID)FirstThread;
491 NtClose(FirstThreadHandle);
493 DPRINT("FirstThread %x\n",FirstThread);
498 * Create the reaper thread
500 KeInitializeEvent(&PiReaperThreadEvent, SynchronizationEvent, FALSE);
501 Status = PsCreateSystemThread(&PiReaperThreadHandle,
506 (PKSTART_ROUTINE) PiReaperThreadMain,
508 if (!NT_SUCCESS(Status))
510 DPRINT1("PS: Failed to create reaper thread.\n");
519 KeSetBasePriorityThread (PKTHREAD Thread,
522 * Sets thread's base priority relative to the process' base priority
523 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
531 else if (Increment > 2)
535 Priority = ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
536 if (Priority < LOW_PRIORITY)
538 Priority = LOW_PRIORITY;
540 else if (Priority >= MAXIMUM_PRIORITY)
542 Thread->BasePriority = HIGH_PRIORITY;
544 KeSetPriorityThread(Thread, Priority);
553 KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority)
555 KPRIORITY OldPriority;
557 PKTHREAD CurrentThread;
560 if (Priority < LOW_PRIORITY || Priority >= MAXIMUM_PRIORITY)
565 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
567 OldPriority = Thread->Priority;
568 Thread->BasePriority = Thread->Priority = (CHAR)Priority;
570 if (OldPriority != Priority)
572 if (Thread->State == THREAD_STATE_READY)
574 PsRemoveFromThreadList((PETHREAD)Thread);
575 PsInsertIntoThreadList(Priority, (PETHREAD)Thread);
576 CurrentThread = ((PIKPCR) KeGetCurrentKPCR())->CurrentThread;
577 if (CurrentThread->Priority < Priority)
579 PsDispatchThreadNoLock(THREAD_STATE_READY);
580 KeLowerIrql(oldIrql);
581 return (OldPriority);
584 else if (Thread->State == THREAD_STATE_RUNNING)
586 if (Priority < OldPriority)
588 /* Check for threads with a higher priority */
589 Mask = ~((1 << (Priority + 1)) - 1);
590 if (PriorityListMask & Mask)
592 PsDispatchThreadNoLock(THREAD_STATE_READY);
593 KeLowerIrql(oldIrql);
594 return (OldPriority);
599 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
607 KeSetAffinityThread(PKTHREAD Thread,
610 * Sets thread's affinity
613 DPRINT1("KeSetAffinityThread() is a stub returning STATUS_SUCCESS");
614 return STATUS_SUCCESS; // FIXME: Use function below
615 //return ZwSetInformationThread(handle, ThreadAffinityMask,<pointer to affinity mask>,sizeof(KAFFINITY));
620 NtAlertResumeThread(IN HANDLE ThreadHandle,
621 OUT PULONG SuspendCount)
627 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
631 NTSTATUS ThreadStatus;
633 Status = ObReferenceObjectByHandle(ThreadHandle,
634 THREAD_SUSPEND_RESUME,
639 if (Status != STATUS_SUCCESS)
644 ThreadStatus = STATUS_ALERTED;
645 (VOID)PsUnblockThread(Thread, &ThreadStatus);
647 ObDereferenceObject(Thread);
648 return(STATUS_SUCCESS);
651 /**********************************************************************
657 NtOpenThread(OUT PHANDLE ThreadHandle,
658 IN ACCESS_MASK DesiredAccess,
659 IN POBJECT_ATTRIBUTES ObjectAttributes,
660 IN PCLIENT_ID ClientId)
662 NTSTATUS Status = STATUS_INVALID_PARAMETER;
664 if((NULL != ThreadHandle)&&(NULL != ObjectAttributes))
666 PETHREAD EThread = NULL;
669 && (ClientId->UniqueProcess)
670 && (ClientId->UniqueThread))
672 // It is an error to specify both
673 // ObjectAttributes.ObjectName
675 if((ObjectAttributes)
676 && (ObjectAttributes->ObjectName)
677 && (0 < ObjectAttributes->ObjectName->Length))
679 return(STATUS_INVALID_PARAMETER_MIX);
682 Status = PsLookupProcessThreadByCid(ClientId,
686 else if((ObjectAttributes)
687 && (ObjectAttributes->ObjectName)
688 && (0 < ObjectAttributes->ObjectName->Length))
690 // Three Ob attributes are forbidden
691 if(!(ObjectAttributes->Attributes &
692 (OBJ_PERMANENT | OBJ_EXCLUSIVE | OBJ_OPENIF)))
694 Status = ObReferenceObjectByName(ObjectAttributes->ObjectName,
695 ObjectAttributes->Attributes,
704 // EThread may be OK...
705 if(STATUS_SUCCESS == Status)
707 Status = ObCreateHandle(PsGetCurrentProcess(),
712 ObDereferenceObject(EThread);
719 NtContinue(IN PCONTEXT Context,
720 IN BOOLEAN TestAlert)
722 PKTRAP_FRAME TrapFrame;
725 * Copy the supplied context over the register information that was saved
726 * on entry to kernel mode, it will then be restored on exit
727 * FIXME: Validate the context
729 TrapFrame = KeGetCurrentThread()->TrapFrame;
730 if (TrapFrame == NULL)
732 CPRINT("NtContinue called but TrapFrame was NULL\n");
735 KeContextToTrapFrame(Context, TrapFrame);
736 return(STATUS_SUCCESS);
741 NtYieldExecution(VOID)
743 PsDispatchThread(THREAD_STATE_READY);
744 return(STATUS_SUCCESS);
752 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
753 OUT PEPROCESS *Process OPTIONAL,
754 OUT PETHREAD *Thread)
757 PLIST_ENTRY current_entry;
760 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
762 current_entry = PiThreadListHead.Flink;
763 while (current_entry != &PiThreadListHead)
765 current = CONTAINING_RECORD(current_entry,
767 Tcb.ThreadListEntry);
768 if (current->Cid.UniqueThread == Cid->UniqueThread &&
769 current->Cid.UniqueProcess == Cid->UniqueProcess)
773 *Process = current->ThreadsProcess;
774 ObReferenceObject(current->ThreadsProcess);
778 ObReferenceObject(current);
780 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
781 return(STATUS_SUCCESS);
784 current_entry = current_entry->Flink;
787 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
789 return(STATUS_INVALID_PARAMETER);
797 PsLookupThreadByThreadId(IN PVOID ThreadId,
798 OUT PETHREAD *Thread)
801 PLIST_ENTRY current_entry;
804 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
806 current_entry = PiThreadListHead.Flink;
807 while (current_entry != &PiThreadListHead)
809 current = CONTAINING_RECORD(current_entry,
811 Tcb.ThreadListEntry);
812 if (current->Cid.UniqueThread == (HANDLE)ThreadId)
815 ObReferenceObject(current);
816 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
817 return(STATUS_SUCCESS);
820 current_entry = current_entry->Flink;
823 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
825 return(STATUS_INVALID_PARAMETER);