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];
48 static ULONG PriorityListMask = 0;
49 #endif /* LIBCAPTIVE */
50 static BOOLEAN DoneInitYet = FALSE;
51 LIST_ENTRY PiThreadListHead;
53 static PETHREAD IdleThreads[MAXIMUM_PROCESSORS];
54 #endif /* LIBCAPTIVE */
55 ULONG PiNrThreads = 0;
57 ULONG PiNrReadyThreads = 0;
58 static HANDLE PiReaperThreadHandle;
59 static KEVENT PiReaperThreadEvent;
60 static BOOL PiReaperThreadShouldTerminate = FALSE;
61 ULONG PiNrThreadsAwaitingReaping = 0;
62 #endif /* LIBCAPTIVE */
64 static GENERIC_MAPPING PiThreadMapping = {THREAD_READ,
69 /* FUNCTIONS ***************************************************************/
74 PKTHREAD STDCALL KeGetCurrentThread(VOID)
76 return(((PIKPCR) KeGetCurrentKPCR())->CurrentThread);
82 HANDLE STDCALL PsGetCurrentThreadId(VOID)
84 return(PsGetCurrentThread()->Cid.UniqueThread);
90 PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)
92 assert(THREAD_STATE_READY == Thread->Tcb.State);
93 if (Priority >= MAXIMUM_PRIORITY || Priority < LOW_PRIORITY)
95 DPRINT1("Invalid thread priority (%d)\n", Priority);
98 InsertTailList(&PriorityListHead[Priority], &Thread->Tcb.QueueListEntry);
99 PriorityListMask |= (1 << Priority);
103 static VOID PsRemoveFromThreadList(PETHREAD Thread)
105 assert(THREAD_STATE_READY == Thread->Tcb.State);
106 RemoveEntryList(&Thread->Tcb.QueueListEntry);
107 if (IsListEmpty(&PriorityListHead[(ULONG)Thread->Tcb.Priority]))
109 PriorityListMask &= ~(1 << Thread->Tcb.Priority);
115 VOID PsDumpThreads(BOOLEAN IncludeSystem)
117 PLIST_ENTRY current_entry;
122 current_entry = PiThreadListHead.Flink;
125 while (current_entry != &PiThreadListHead)
130 current = CONTAINING_RECORD(current_entry, ETHREAD,
131 Tcb.ThreadListEntry);
135 DbgPrint("Too many threads on list\n");
138 if (IncludeSystem || current->ThreadsProcess->UniqueProcessId >= 6)
140 DbgPrint("current->Tcb.State %d PID.TID %d.%d Name %.8s Stack: \n",
142 current->ThreadsProcess->UniqueProcessId,
143 current->Cid.UniqueThread,
144 current->ThreadsProcess->ImageFileName);
145 if (current->Tcb.State == THREAD_STATE_READY ||
146 current->Tcb.State == THREAD_STATE_SUSPENDED ||
147 current->Tcb.State == THREAD_STATE_BLOCKED)
149 Esp = (PULONG)current->Tcb.KernelStack;
150 Ebp = (PULONG)Esp[3];
151 DbgPrint("Ebp 0x%.8X\n", Ebp);
153 while (Ebp != 0 && Ebp >= (PULONG)current->Tcb.StackLimit)
155 DbgPrint("%.8X %.8X%s", Ebp[0], Ebp[1],
156 (i % 8) == 7 ? "\n" : " ");
157 Ebp = (PULONG)Ebp[0];
166 current_entry = current_entry->Flink;
170 static PETHREAD PsScanThreadList (KPRIORITY Priority, ULONG Affinity)
172 PLIST_ENTRY current_entry;
176 Mask = (1 << Priority);
177 if (PriorityListMask & Mask)
179 current_entry = PriorityListHead[Priority].Flink;
180 while (current_entry != &PriorityListHead[Priority])
182 current = CONTAINING_RECORD(current_entry, ETHREAD,
184 if (current->Tcb.State != THREAD_STATE_READY)
186 DPRINT1("%d/%d\n", current->Cid.UniqueThread, current->Tcb.State);
188 assert(current->Tcb.State == THREAD_STATE_READY);
189 DPRINT("current->Tcb.UserAffinity %x Affinity %x PID %d %d\n",
190 current->Tcb.UserAffinity, Affinity, current->Cid.UniqueThread,
192 if (current->Tcb.UserAffinity & Affinity)
194 PsRemoveFromThreadList(current);
197 current_entry = current_entry->Flink;
204 PiWakeupReaperThread(VOID)
206 KeSetEvent(&PiReaperThreadEvent, 0, FALSE);
210 PiReaperThreadMain(PVOID Ignored)
214 KeWaitForSingleObject(&PiReaperThreadEvent,
219 if (PiReaperThreadShouldTerminate)
221 PsTerminateSystemThread(0);
227 VOID PsDispatchThreadNoLock (ULONG NewThreadStatus)
229 KPRIORITY CurrentPriority;
232 PKTHREAD KCurrentThread = ((PIKPCR) KeGetCurrentKPCR())->CurrentThread;
233 PETHREAD CurrentThread = CONTAINING_RECORD(KCurrentThread, ETHREAD, Tcb);
235 DPRINT("PsDispatchThread() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
236 CurrentThread->Cid.UniqueThread, NewThreadStatus, CurrentThread->Tcb.State);
238 CurrentThread->Tcb.State = NewThreadStatus;
239 if (CurrentThread->Tcb.State == THREAD_STATE_READY)
241 PsInsertIntoThreadList(CurrentThread->Tcb.Priority,
244 if (CurrentThread->Tcb.State == THREAD_STATE_TERMINATED_1)
246 PiNrThreadsAwaitingReaping++;
249 Affinity = 1 << KeGetCurrentProcessorNumber();
250 for (CurrentPriority = HIGH_PRIORITY;
251 CurrentPriority >= LOW_PRIORITY;
254 Candidate = PsScanThreadList(CurrentPriority, Affinity);
255 if (Candidate == CurrentThread)
257 Candidate->Tcb.State = THREAD_STATE_RUNNING;
258 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
261 if (Candidate != NULL)
265 DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
267 Candidate->Tcb.State = THREAD_STATE_RUNNING;
269 OldThread = CurrentThread;
270 CurrentThread = Candidate;
273 * This code is moved to the end of KiArchContextSwitch.
274 * It should be execute after the context switch.
276 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
277 if (PiNrThreadsAwaitingReaping > 0)
279 PiWakeupReaperThread();
282 KiArchContextSwitch(&CurrentThread->Tcb, &OldThread->Tcb);
286 CPRINT("CRITICAL: No threads are ready\n");
291 PsDispatchThread(ULONG NewThreadStatus)
300 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
304 ((PIKPCR) KeGetCurrentKPCR())->CurrentThread->WaitIrql = oldIrql;
305 PsDispatchThreadNoLock(NewThreadStatus);
306 KeLowerIrql(oldIrql);
310 PsUnblockThread(PETHREAD Thread, PNTSTATUS WaitStatus)
314 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
315 if (THREAD_STATE_TERMINATED_1 == Thread->Tcb.State ||
316 THREAD_STATE_TERMINATED_2 == Thread->Tcb.State)
318 DPRINT("Can't unblock thread %d because it's terminating\n",
319 Thread->Cid.UniqueThread);
321 else if (THREAD_STATE_READY == Thread->Tcb.State ||
322 THREAD_STATE_RUNNING == Thread->Tcb.State)
324 DPRINT("Can't unblock thread %d because it's ready or running\n",
325 Thread->Cid.UniqueThread);
329 if (WaitStatus != NULL)
331 Thread->Tcb.WaitStatus = *WaitStatus;
333 Thread->Tcb.State = THREAD_STATE_READY;
334 PsInsertIntoThreadList(Thread->Tcb.Priority, Thread);
336 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
340 PsBlockThread(PNTSTATUS Status, UCHAR Alertable, ULONG WaitMode,
341 BOOLEAN DispatcherLock, KIRQL WaitIrql, UCHAR WaitReason)
346 PKWAIT_BLOCK WaitBlock;
348 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
350 KThread = ((PIKPCR) KeGetCurrentKPCR())->CurrentThread;
351 Thread = CONTAINING_RECORD (KThread, ETHREAD, Tcb);
352 if (KThread->ApcState.KernelApcPending)
356 KeAcquireDispatcherDatabaseLock(FALSE);
358 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
361 RemoveEntryList (&WaitBlock->WaitListEntry);
362 WaitBlock = WaitBlock->NextWaitBlock;
364 Thread->Tcb.WaitBlockList = NULL;
365 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
366 PsDispatchThreadNoLock (THREAD_STATE_READY);
369 *Status = STATUS_KERNEL_APC;
376 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
378 Thread->Tcb.Alertable = Alertable;
379 Thread->Tcb.WaitMode = WaitMode;
380 Thread->Tcb.WaitIrql = WaitIrql;
381 Thread->Tcb.WaitReason = WaitReason;
382 PsDispatchThreadNoLock(THREAD_STATE_BLOCKED);
386 *Status = Thread->Tcb.WaitStatus;
389 KeLowerIrql(WaitIrql);
393 PsFreezeAllThreads(PEPROCESS Process)
395 * Used by the debugging code to freeze all the process's threads
396 * while the debugger is examining their state.
400 PLIST_ENTRY current_entry;
403 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
405 current_entry = Process->ThreadListHead.Flink;
406 while (current_entry != &Process->ThreadListHead)
408 current = CONTAINING_RECORD(current_entry, ETHREAD,
409 Tcb.ProcessThreadListEntry);
412 * We have to be careful here, we can't just set the freeze the
413 * thread inside kernel mode since it may be holding a lock.
416 current_entry = current_entry->Flink;
419 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
423 PsApplicationProcessorInit(VOID)
425 ((PIKPCR) KeGetCurrentKPCR())->CurrentThread =
426 (PVOID)IdleThreads[KeGetCurrentProcessorNumber()];
430 PsPrepareForApplicationProcessorInit(ULONG Id)
433 HANDLE IdleThreadHandle;
435 PsInitializeThread(NULL,
441 IdleThread->Tcb.State = THREAD_STATE_RUNNING;
442 IdleThread->Tcb.FreezeCount = 0;
443 IdleThread->Tcb.UserAffinity = 1 << Id;
444 IdleThread->Tcb.Priority = LOW_PRIORITY;
445 IdleThreads[Id] = IdleThread;
447 NtClose(IdleThreadHandle);
448 DPRINT("IdleThread for Processor %d has PID %d\n",
449 Id, IdleThread->Cid.UniqueThread);
452 #endif /* LIBCAPTIVE */
455 PsInitThreadManagment(VOID)
457 * FUNCTION: Initialize thread managment
460 PETHREAD FirstThread;
462 HANDLE FirstThreadHandle;
465 #endif /* LIBCAPTIVE */
467 KeInitializeSpinLock(&PiThreadListLock);
468 for (i=0; i < MAXIMUM_PRIORITY; i++)
470 InitializeListHead(&PriorityListHead[i]);
473 InitializeListHead(&PiThreadListHead);
475 PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
477 RtlInitUnicodeStringFromLiteral(&PsThreadType->TypeName, REACTOS_UCS2(L"Thread"));
479 PsThreadType->Tag = TAG('T', 'H', 'R', 'T');
480 PsThreadType->TotalObjects = 0;
481 PsThreadType->TotalHandles = 0;
482 PsThreadType->MaxObjects = 0;
483 PsThreadType->MaxHandles = 0;
484 PsThreadType->PagedPoolCharge = 0;
485 PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
486 PsThreadType->Mapping = &PiThreadMapping;
487 PsThreadType->Dump = NULL;
488 PsThreadType->Open = NULL;
489 PsThreadType->Close = NULL;
491 PsThreadType->Delete = PiDeleteThread;
492 #else /* !LIBCAPTIVE */
493 PsThreadType->Delete = NULL;
494 #endif /* !LIBCAPTIVE */
495 PsThreadType->Parse = NULL;
496 PsThreadType->Security = NULL;
497 PsThreadType->QueryName = NULL;
498 PsThreadType->OkayToClose = NULL;
499 PsThreadType->Create = NULL;
500 PsThreadType->DuplicationNotify = NULL;
502 PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
503 THREAD_ALL_ACCESS,NULL, TRUE);
504 FirstThread->Tcb.State = THREAD_STATE_RUNNING;
505 FirstThread->Tcb.FreezeCount = 0;
506 FirstThread->Tcb.Queue = ExAllocatePool(NonPagedPool, sizeof(*FirstThread->Tcb.Queue));
507 KeInitializeQueue(FirstThread->Tcb.Queue, 0);
508 ((PIKPCR) KeGetCurrentKPCR())->CurrentThread = (PVOID)FirstThread;
509 NtClose(FirstThreadHandle);
511 DPRINT("FirstThread %x\n",FirstThread);
517 * Create the reaper thread
519 KeInitializeEvent(&PiReaperThreadEvent, SynchronizationEvent, FALSE);
520 Status = PsCreateSystemThread(&PiReaperThreadHandle,
525 (PKSTART_ROUTINE) PiReaperThreadMain,
527 if (!NT_SUCCESS(Status))
529 DPRINT1("PS: Failed to create reaper thread.\n");
532 #endif /* LIBCAPTIVE */
541 KeSetBasePriorityThread (PKTHREAD Thread,
544 * Sets thread's base priority relative to the process' base priority
545 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
553 else if (Increment > 2)
557 Priority = ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
558 if (Priority < LOW_PRIORITY)
560 Priority = LOW_PRIORITY;
562 else if (Priority >= MAXIMUM_PRIORITY)
564 Thread->BasePriority = HIGH_PRIORITY;
566 KeSetPriorityThread(Thread, Priority);
575 KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority)
577 KPRIORITY OldPriority;
579 PKTHREAD CurrentThread;
582 if (Priority < LOW_PRIORITY || Priority >= MAXIMUM_PRIORITY)
587 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
589 OldPriority = Thread->Priority;
590 Thread->BasePriority = Thread->Priority = (CHAR)Priority;
592 if (OldPriority != Priority)
594 if (Thread->State == THREAD_STATE_READY)
596 PsRemoveFromThreadList((PETHREAD)Thread);
597 PsInsertIntoThreadList(Priority, (PETHREAD)Thread);
598 CurrentThread = ((PIKPCR) KeGetCurrentKPCR())->CurrentThread;
599 if (CurrentThread->Priority < Priority)
601 PsDispatchThreadNoLock(THREAD_STATE_READY);
602 KeLowerIrql(oldIrql);
603 return (OldPriority);
606 else if (Thread->State == THREAD_STATE_RUNNING)
608 if (Priority < OldPriority)
610 /* Check for threads with a higher priority */
611 Mask = ~((1 << (Priority + 1)) - 1);
612 if (PriorityListMask & Mask)
614 PsDispatchThreadNoLock(THREAD_STATE_READY);
615 KeLowerIrql(oldIrql);
616 return (OldPriority);
621 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
629 KeSetAffinityThread(PKTHREAD Thread,
632 * Sets thread's affinity
635 DPRINT1("KeSetAffinityThread() is a stub returning STATUS_SUCCESS");
636 return STATUS_SUCCESS; // FIXME: Use function below
637 //return ZwSetInformationThread(handle, ThreadAffinityMask,<pointer to affinity mask>,sizeof(KAFFINITY));
642 NtAlertResumeThread(IN HANDLE ThreadHandle,
643 OUT PULONG SuspendCount)
649 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
653 NTSTATUS ThreadStatus;
655 Status = ObReferenceObjectByHandle(ThreadHandle,
656 THREAD_SUSPEND_RESUME,
661 if (Status != STATUS_SUCCESS)
666 ThreadStatus = STATUS_ALERTED;
667 (VOID)PsUnblockThread(Thread, &ThreadStatus);
669 ObDereferenceObject(Thread);
670 return(STATUS_SUCCESS);
673 /**********************************************************************
679 NtOpenThread(OUT PHANDLE ThreadHandle,
680 IN ACCESS_MASK DesiredAccess,
681 IN POBJECT_ATTRIBUTES ObjectAttributes,
682 IN PCLIENT_ID ClientId)
684 NTSTATUS Status = STATUS_INVALID_PARAMETER;
686 if((NULL != ThreadHandle)&&(NULL != ObjectAttributes))
688 PETHREAD EThread = NULL;
691 && (ClientId->UniqueProcess)
692 && (ClientId->UniqueThread))
694 // It is an error to specify both
695 // ObjectAttributes.ObjectName
697 if((ObjectAttributes)
698 && (ObjectAttributes->ObjectName)
699 && (0 < ObjectAttributes->ObjectName->Length))
701 return(STATUS_INVALID_PARAMETER_MIX);
704 Status = PsLookupProcessThreadByCid(ClientId,
708 else if((ObjectAttributes)
709 && (ObjectAttributes->ObjectName)
710 && (0 < ObjectAttributes->ObjectName->Length))
712 // Three Ob attributes are forbidden
713 if(!(ObjectAttributes->Attributes &
714 (OBJ_PERMANENT | OBJ_EXCLUSIVE | OBJ_OPENIF)))
716 Status = ObReferenceObjectByName(ObjectAttributes->ObjectName,
717 ObjectAttributes->Attributes,
726 // EThread may be OK...
727 if(STATUS_SUCCESS == Status)
729 Status = ObCreateHandle(PsGetCurrentProcess(),
734 ObDereferenceObject(EThread);
741 NtContinue(IN PCONTEXT Context,
742 IN BOOLEAN TestAlert)
744 PKTRAP_FRAME TrapFrame;
747 * Copy the supplied context over the register information that was saved
748 * on entry to kernel mode, it will then be restored on exit
749 * FIXME: Validate the context
751 TrapFrame = KeGetCurrentThread()->TrapFrame;
752 if (TrapFrame == NULL)
754 CPRINT("NtContinue called but TrapFrame was NULL\n");
757 KeContextToTrapFrame(Context, TrapFrame);
758 return(STATUS_SUCCESS);
763 NtYieldExecution(VOID)
765 PsDispatchThread(THREAD_STATE_READY);
766 return(STATUS_SUCCESS);
774 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
775 OUT PEPROCESS *Process OPTIONAL,
776 OUT PETHREAD *Thread)
779 PLIST_ENTRY current_entry;
782 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
784 current_entry = PiThreadListHead.Flink;
785 while (current_entry != &PiThreadListHead)
787 current = CONTAINING_RECORD(current_entry,
789 Tcb.ThreadListEntry);
790 if (current->Cid.UniqueThread == Cid->UniqueThread &&
791 current->Cid.UniqueProcess == Cid->UniqueProcess)
795 *Process = current->ThreadsProcess;
796 ObReferenceObject(current->ThreadsProcess);
800 ObReferenceObject(current);
802 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
803 return(STATUS_SUCCESS);
806 current_entry = current_entry->Flink;
809 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
811 return(STATUS_INVALID_PARAMETER);
819 PsLookupThreadByThreadId(IN PVOID ThreadId,
820 OUT PETHREAD *Thread)
823 PLIST_ENTRY current_entry;
826 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
828 current_entry = PiThreadListHead.Flink;
829 while (current_entry != &PiThreadListHead)
831 current = CONTAINING_RECORD(current_entry,
833 Tcb.ThreadListEntry);
834 if (current->Cid.UniqueThread == (HANDLE)ThreadId)
837 ObReferenceObject(current);
838 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
839 return(STATUS_SUCCESS);
842 current_entry = current_entry->Flink;
845 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
847 return(STATUS_INVALID_PARAMETER);
850 #endif /* LIBCAPTIVE */