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 ***************************************************************/
68 PKTHREAD STDCALL KeGetCurrentThread(VOID)
70 return(KeGetCurrentKPCR()->CurrentThread);
73 HANDLE STDCALL PsGetCurrentThreadId(VOID)
75 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, UCHAR WaitReason)
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 Thread->Tcb.WaitReason = WaitReason;
328 PsDispatchThreadNoLock(THREAD_STATE_BLOCKED);
332 *Status = Thread->Tcb.WaitStatus;
335 KeLowerIrql(WaitIrql);
339 PsFreezeAllThreads(PEPROCESS Process)
341 * Used by the debugging code to freeze all the process's threads
342 * while the debugger is examining their state.
346 PLIST_ENTRY current_entry;
349 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
351 current_entry = Process->ThreadListHead.Flink;
352 while (current_entry != &Process->ThreadListHead)
354 current = CONTAINING_RECORD(current_entry, ETHREAD,
355 Tcb.ProcessThreadListEntry);
358 * We have to be careful here, we can't just set the freeze the
359 * thread inside kernel mode since it may be holding a lock.
362 current_entry = current_entry->Flink;
365 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
369 PsApplicationProcessorInit(VOID)
371 KeGetCurrentKPCR()->CurrentThread =
372 (PVOID)IdleThreads[KeGetCurrentProcessorNumber()];
376 PsPrepareForApplicationProcessorInit(ULONG Id)
379 HANDLE IdleThreadHandle;
381 PsInitializeThread(NULL,
387 IdleThread->Tcb.State = THREAD_STATE_RUNNING;
388 IdleThread->Tcb.FreezeCount = 0;
389 IdleThread->Tcb.UserAffinity = 1 << Id;
390 IdleThread->Tcb.Priority = LOW_PRIORITY;
391 IdleThreads[Id] = IdleThread;
393 NtClose(IdleThreadHandle);
394 DPRINT("IdleThread for Processor %d has PID %d\n",
395 Id, IdleThread->Cid.UniqueThread);
398 #endif /* LIBCAPTIVE */
401 PsInitThreadManagment(VOID)
403 * FUNCTION: Initialize thread managment
406 PETHREAD FirstThread;
408 HANDLE FirstThreadHandle;
411 #endif /* LIBCAPTIVE */
413 KeInitializeSpinLock(&PiThreadListLock);
414 for (i=0; i < MAXIMUM_PRIORITY; i++)
416 InitializeListHead(&PriorityListHead[i]);
419 InitializeListHead(&PiThreadListHead);
421 PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
423 RtlInitUnicodeStringFromLiteral(&PsThreadType->TypeName, REACTOS_UCS2(L"Thread"));
425 PsThreadType->Tag = TAG('T', 'H', 'R', 'T');
426 PsThreadType->TotalObjects = 0;
427 PsThreadType->TotalHandles = 0;
428 PsThreadType->MaxObjects = 0;
429 PsThreadType->MaxHandles = 0;
430 PsThreadType->PagedPoolCharge = 0;
431 PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
432 PsThreadType->Mapping = &PiThreadMapping;
433 PsThreadType->Dump = NULL;
434 PsThreadType->Open = NULL;
435 PsThreadType->Close = NULL;
437 PsThreadType->Delete = PiDeleteThread;
438 #else /* !LIBCAPTIVE */
439 PsThreadType->Delete = NULL;
440 #endif /* !LIBCAPTIVE */
441 PsThreadType->Parse = NULL;
442 PsThreadType->Security = NULL;
443 PsThreadType->QueryName = NULL;
444 PsThreadType->OkayToClose = NULL;
445 PsThreadType->Create = NULL;
446 PsThreadType->DuplicationNotify = NULL;
448 PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
449 THREAD_ALL_ACCESS,NULL, TRUE);
450 FirstThread->Tcb.State = THREAD_STATE_RUNNING;
451 FirstThread->Tcb.FreezeCount = 0;
452 FirstThread->Tcb.Queue = ExAllocatePool(NonPagedPool, sizeof(*FirstThread->Tcb.Queue));
453 KeInitializeQueue(FirstThread->Tcb.Queue, 0);
454 KeGetCurrentKPCR()->CurrentThread = (PVOID)FirstThread;
455 NtClose(FirstThreadHandle);
457 DPRINT("FirstThread %x\n",FirstThread);
463 * Create the reaper thread
465 KeInitializeEvent(&PiReaperThreadEvent, SynchronizationEvent, FALSE);
466 Status = PsCreateSystemThread(&PiReaperThreadHandle,
473 if (!NT_SUCCESS(Status))
475 DPRINT1("PS: Failed to create reaper thread.\n");
478 #endif /* LIBCAPTIVE */
484 KeSetBasePriorityThread (PKTHREAD Thread,
487 * Sets thread's base priority relative to the process' base priority
488 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
491 Thread->BasePriority =
492 ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
493 if (Thread->BasePriority < LOW_PRIORITY)
494 Thread->BasePriority = LOW_PRIORITY;
495 else if (Thread->BasePriority >= MAXIMUM_PRIORITY)
496 Thread->BasePriority = HIGH_PRIORITY;
497 Thread->Priority = Thread->BasePriority;
503 KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority)
505 KPRIORITY OldPriority;
508 if (Priority < 0 || Priority >= MAXIMUM_PRIORITY)
513 OldPriority = Thread->Priority;
514 Thread->Priority = (CHAR)Priority;
516 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
517 if (Thread->State == THREAD_STATE_READY)
519 RemoveEntryList(&Thread->QueueListEntry);
520 PsInsertIntoThreadList(Thread->BasePriority,
521 CONTAINING_RECORD(Thread,ETHREAD,Tcb));
523 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
528 KeSetAffinityThread(PKTHREAD Thread,
531 * Sets thread's affinity
534 DPRINT1("KeSetAffinityThread() is a stub returning STATUS_SUCCESS");
535 return STATUS_SUCCESS; // FIXME: Use function below
536 //return ZwSetInformationThread(handle, ThreadAffinityMask,<pointer to affinity mask>,sizeof(KAFFINITY));
541 NtAlertResumeThread(IN HANDLE ThreadHandle,
542 OUT PULONG SuspendCount)
548 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
552 NTSTATUS ThreadStatus;
554 Status = ObReferenceObjectByHandle(ThreadHandle,
555 THREAD_SUSPEND_RESUME,
560 if (Status != STATUS_SUCCESS)
565 ThreadStatus = STATUS_ALERTED;
566 (VOID)PsUnblockThread(Thread, &ThreadStatus);
568 ObDereferenceObject(Thread);
569 return(STATUS_SUCCESS);
573 NtOpenThread(OUT PHANDLE ThreadHandle,
574 IN ACCESS_MASK DesiredAccess,
575 IN POBJECT_ATTRIBUTES ObjectAttributes,
576 IN PCLIENT_ID ClientId)
582 NtContinue(IN PCONTEXT Context,
583 IN BOOLEAN TestAlert)
585 PKTRAP_FRAME TrapFrame;
588 * Copy the supplied context over the register information that was saved
589 * on entry to kernel mode, it will then be restored on exit
590 * FIXME: Validate the context
592 TrapFrame = KeGetCurrentThread()->TrapFrame;
593 if (TrapFrame == NULL)
595 CPRINT("NtContinue called but TrapFrame was NULL\n");
598 KeContextToTrapFrame(Context, TrapFrame);
599 return(STATUS_SUCCESS);
604 NtYieldExecution(VOID)
606 PsDispatchThread(THREAD_STATE_READY);
607 return(STATUS_SUCCESS);
612 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
613 OUT PEPROCESS *Process OPTIONAL,
614 OUT PETHREAD *Thread)
617 PLIST_ENTRY current_entry;
620 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
622 current_entry = PiThreadListHead.Flink;
623 while (current_entry != &PiThreadListHead)
625 current = CONTAINING_RECORD(current_entry,
627 Tcb.ThreadListEntry);
628 if (current->Cid.UniqueThread == Cid->UniqueThread &&
629 current->Cid.UniqueProcess == Cid->UniqueProcess)
633 *Process = current->ThreadsProcess;
634 ObReferenceObject(current->ThreadsProcess);
638 ObReferenceObject(current);
640 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
641 return(STATUS_SUCCESS);
644 current_entry = current_entry->Flink;
647 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
649 return(STATUS_INVALID_PARAMETER);
654 PsLookupThreadByThreadId(IN PVOID ThreadId,
655 OUT PETHREAD *Thread)
658 PLIST_ENTRY current_entry;
661 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
663 current_entry = PiThreadListHead.Flink;
664 while (current_entry != &PiThreadListHead)
666 current = CONTAINING_RECORD(current_entry,
668 Tcb.ThreadListEntry);
669 if (current->Cid.UniqueThread == (HANDLE)ThreadId)
672 ObReferenceObject(current);
673 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
674 return(STATUS_SUCCESS);
677 current_entry = current_entry->Flink;
680 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
682 return(STATUS_INVALID_PARAMETER);
685 #endif /* LIBCAPTIVE */