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 PETHREAD STDCALL PsGetCurrentThread(VOID)
75 PKTHREAD CurrentThread = KeGetCurrentKPCR()->CurrentThread;
76 return(CONTAINING_RECORD(CurrentThread, ETHREAD, Tcb));
81 HANDLE STDCALL PsGetCurrentThreadId(VOID)
83 return(PsGetCurrentThread()->Cid.UniqueThread);
87 PsInsertIntoThreadList(KPRIORITY Priority, PETHREAD Thread)
89 if (Priority >= MAXIMUM_PRIORITY || Priority < 0)
91 DPRINT1("Invalid thread priority\n");
94 InsertTailList(&PriorityListHead[Priority], &Thread->Tcb.QueueListEntry);
98 VOID PsDumpThreads(BOOLEAN IncludeSystem)
100 PLIST_ENTRY current_entry;
105 current_entry = PiThreadListHead.Flink;
108 while (current_entry != &PiThreadListHead)
113 current = CONTAINING_RECORD(current_entry, ETHREAD,
114 Tcb.ThreadListEntry);
118 DbgPrint("Too many threads on list\n");
121 if (IncludeSystem || current->ThreadsProcess->UniqueProcessId >= 6)
123 DbgPrint("current->Tcb.State %d PID.TID %d.%d Name %.8s Stack: \n",
125 current->ThreadsProcess->UniqueProcessId,
126 current->Cid.UniqueThread,
127 current->ThreadsProcess->ImageFileName);
128 if (current->Tcb.State == THREAD_STATE_READY ||
129 current->Tcb.State == THREAD_STATE_SUSPENDED ||
130 current->Tcb.State == THREAD_STATE_BLOCKED)
132 Esp = (PULONG)current->Tcb.KernelStack;
133 Ebp = (PULONG)Esp[3];
134 DbgPrint("Ebp 0x%.8X\n", Ebp);
136 while (Ebp != 0 && Ebp >= (PULONG)current->Tcb.StackLimit)
138 DbgPrint("%.8X %.8X%s", Ebp[0], Ebp[1],
139 (i % 8) == 7 ? "\n" : " ");
140 Ebp = (PULONG)Ebp[0];
149 current_entry = current_entry->Flink;
153 static PETHREAD PsScanThreadList (KPRIORITY Priority, ULONG Affinity)
155 PLIST_ENTRY current_entry;
158 current_entry = PriorityListHead[Priority].Flink;
159 while (current_entry != &PriorityListHead[Priority])
161 current = CONTAINING_RECORD(current_entry, ETHREAD,
163 assert(current->Tcb.State == THREAD_STATE_READY);
164 DPRINT("current->Tcb.UserAffinity %x Affinity %x PID %d %d\n",
165 current->Tcb.UserAffinity, Affinity, current->Cid.UniqueThread,
167 if (current->Tcb.UserAffinity & Affinity)
169 RemoveEntryList(¤t->Tcb.QueueListEntry);
172 current_entry = current_entry->Flink;
178 PiWakeupReaperThread(VOID)
180 KeSetEvent(&PiReaperThreadEvent, 0, FALSE);
184 PiReaperThreadMain(PVOID Ignored)
188 KeWaitForSingleObject(&PiReaperThreadEvent,
193 if (PiReaperThreadShouldTerminate)
195 PsTerminateSystemThread(0);
201 VOID PsDispatchThreadNoLock (ULONG NewThreadStatus)
203 KPRIORITY CurrentPriority;
206 PKTHREAD KCurrentThread = KeGetCurrentKPCR()->CurrentThread;
207 PETHREAD CurrentThread = CONTAINING_RECORD(KCurrentThread, ETHREAD, Tcb);
209 DPRINT("PsDispatchThread() %d/%d\n", KeGetCurrentProcessorNumber(),
210 CurrentThread->Cid.UniqueThread);
212 CurrentThread->Tcb.State = NewThreadStatus;
213 if (CurrentThread->Tcb.State == THREAD_STATE_READY)
216 PsInsertIntoThreadList(CurrentThread->Tcb.Priority,
219 if (CurrentThread->Tcb.State == THREAD_STATE_TERMINATED_1)
221 PiNrThreadsAwaitingReaping++;
224 Affinity = 1 << KeGetCurrentProcessorNumber();
225 for (CurrentPriority = HIGH_PRIORITY;
226 CurrentPriority >= LOW_PRIORITY;
229 Candidate = PsScanThreadList(CurrentPriority, Affinity);
230 if (Candidate == CurrentThread)
232 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
235 if (Candidate != NULL)
239 DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
241 Candidate->Tcb.State = THREAD_STATE_RUNNING;
243 OldThread = CurrentThread;
244 CurrentThread = Candidate;
246 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock);
247 if (PiNrThreadsAwaitingReaping > 0)
249 PiWakeupReaperThread();
251 KiArchContextSwitch(&CurrentThread->Tcb, &OldThread->Tcb);
255 CPRINT("CRITICAL: No threads are ready\n");
260 PsDispatchThread(ULONG NewThreadStatus)
269 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
273 KeGetCurrentKPCR()->CurrentThread->WaitIrql = oldIrql;
274 PsDispatchThreadNoLock(NewThreadStatus);
275 KeLowerIrql(oldIrql);
279 PsUnblockThread(PETHREAD Thread, PNTSTATUS WaitStatus)
283 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
284 if (WaitStatus != NULL)
286 Thread->Tcb.WaitStatus = *WaitStatus;
288 Thread->Tcb.State = THREAD_STATE_READY;
289 PsInsertIntoThreadList(Thread->Tcb.Priority, Thread);
290 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
294 PsBlockThread(PNTSTATUS Status, UCHAR Alertable, ULONG WaitMode,
295 BOOLEAN DispatcherLock, KIRQL WaitIrql)
298 PKTHREAD KThread = KeGetCurrentKPCR()->CurrentThread;
299 PETHREAD Thread = CONTAINING_RECORD (KThread, ETHREAD, Tcb);
300 PKWAIT_BLOCK WaitBlock;
302 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
304 if (KThread->ApcState.KernelApcPending)
308 KeAcquireDispatcherDatabaseLock(FALSE);
310 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
313 RemoveEntryList (&WaitBlock->WaitListEntry);
314 WaitBlock = WaitBlock->NextWaitBlock;
316 Thread->Tcb.WaitBlockList = NULL;
317 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
318 PsDispatchThreadNoLock (THREAD_STATE_READY);
321 *Status = STATUS_KERNEL_APC;
328 KeReleaseDispatcherDatabaseLockAtDpcLevel(FALSE);
330 Thread->Tcb.Alertable = Alertable;
331 Thread->Tcb.WaitMode = WaitMode;
332 Thread->Tcb.WaitIrql = WaitIrql;
333 PsDispatchThreadNoLock(THREAD_STATE_BLOCKED);
336 *Status = Thread->Tcb.WaitStatus;
339 KeLowerIrql(WaitIrql);
343 PsFreezeAllThreads(PEPROCESS Process)
345 * Used by the debugging code to freeze all the process's threads
346 * while the debugger is examining their state.
350 PLIST_ENTRY current_entry;
353 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
355 current_entry = Process->ThreadListHead.Flink;
356 while (current_entry != &Process->ThreadListHead)
358 current = CONTAINING_RECORD(current_entry, ETHREAD,
359 Tcb.ProcessThreadListEntry);
362 * We have to be careful here, we can't just set the freeze the
363 * thread inside kernel mode since it may be holding a lock.
366 current_entry = current_entry->Flink;
369 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
373 PsApplicationProcessorInit(VOID)
375 KeGetCurrentKPCR()->CurrentThread =
376 (PVOID)IdleThreads[KeGetCurrentProcessorNumber()];
380 PsPrepareForApplicationProcessorInit(ULONG Id)
383 HANDLE IdleThreadHandle;
385 PsInitializeThread(NULL,
391 IdleThread->Tcb.State = THREAD_STATE_RUNNING;
392 IdleThread->Tcb.FreezeCount = 0;
393 IdleThread->Tcb.UserAffinity = 1 << Id;
394 IdleThread->Tcb.Priority = LOW_PRIORITY;
395 IdleThreads[Id] = IdleThread;
397 NtClose(IdleThreadHandle);
398 DPRINT("IdleThread for Processor %d has PID %d\n",
399 Id, IdleThread->Cid.UniqueThread);
402 #endif /* LIBCAPTIVE */
405 PsInitThreadManagment(VOID)
407 * FUNCTION: Initialize thread managment
410 PETHREAD FirstThread;
412 HANDLE FirstThreadHandle;
415 #endif /* LIBCAPTIVE */
417 KeInitializeSpinLock(&PiThreadListLock);
418 for (i=0; i < MAXIMUM_PRIORITY; i++)
420 InitializeListHead(&PriorityListHead[i]);
423 InitializeListHead(&PiThreadListHead);
425 PsThreadType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
427 RtlInitUnicodeStringFromLiteral(&PsThreadType->TypeName, REACTOS_UCS2(L"Thread"));
429 PsThreadType->Tag = TAG('T', 'H', 'R', 'T');
430 PsThreadType->TotalObjects = 0;
431 PsThreadType->TotalHandles = 0;
432 PsThreadType->MaxObjects = 0;
433 PsThreadType->MaxHandles = 0;
434 PsThreadType->PagedPoolCharge = 0;
435 PsThreadType->NonpagedPoolCharge = sizeof(ETHREAD);
436 PsThreadType->Mapping = &PiThreadMapping;
437 PsThreadType->Dump = NULL;
438 PsThreadType->Open = NULL;
439 PsThreadType->Close = NULL;
441 PsThreadType->Delete = PiDeleteThread;
442 #else /* !LIBCAPTIVE */
443 PsThreadType->Delete = NULL;
444 #endif /* !LIBCAPTIVE */
445 PsThreadType->Parse = NULL;
446 PsThreadType->Security = NULL;
447 PsThreadType->QueryName = NULL;
448 PsThreadType->OkayToClose = NULL;
449 PsThreadType->Create = NULL;
450 PsThreadType->DuplicationNotify = NULL;
452 PsInitializeThread(NULL,&FirstThread,&FirstThreadHandle,
453 THREAD_ALL_ACCESS,NULL, TRUE);
454 FirstThread->Tcb.State = THREAD_STATE_RUNNING;
455 FirstThread->Tcb.FreezeCount = 0;
456 KeGetCurrentKPCR()->CurrentThread = (PVOID)FirstThread;
457 NtClose(FirstThreadHandle);
459 DPRINT("FirstThread %x\n",FirstThread);
465 * Create the reaper thread
467 KeInitializeEvent(&PiReaperThreadEvent, SynchronizationEvent, FALSE);
468 Status = PsCreateSystemThread(&PiReaperThreadHandle,
475 if (!NT_SUCCESS(Status))
477 DPRINT1("PS: Failed to create reaper thread.\n");
480 #endif /* LIBCAPTIVE */
486 KeSetBasePriorityThread (PKTHREAD Thread,
489 * Sets thread's base priority relative to the process' base priority
490 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
493 Thread->BasePriority =
494 ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
495 if (Thread->BasePriority < LOW_PRIORITY)
496 Thread->BasePriority = LOW_PRIORITY;
497 else if (Thread->BasePriority >= MAXIMUM_PRIORITY)
498 Thread->BasePriority = HIGH_PRIORITY;
499 Thread->Priority = Thread->BasePriority;
505 KeSetPriorityThread (PKTHREAD Thread, KPRIORITY Priority)
507 KPRIORITY OldPriority;
510 if (Priority < 0 || Priority >= MAXIMUM_PRIORITY)
515 OldPriority = Thread->Priority;
516 Thread->Priority = (CHAR)Priority;
518 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
519 if (Thread->State == THREAD_STATE_READY)
521 RemoveEntryList(&Thread->QueueListEntry);
522 PsInsertIntoThreadList(Thread->BasePriority,
523 CONTAINING_RECORD(Thread,ETHREAD,Tcb));
525 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
531 NtAlertResumeThread(IN HANDLE ThreadHandle,
532 OUT PULONG SuspendCount)
538 NTSTATUS STDCALL NtAlertThread (IN HANDLE ThreadHandle)
542 NTSTATUS ThreadStatus;
544 Status = ObReferenceObjectByHandle(ThreadHandle,
545 THREAD_SUSPEND_RESUME,
550 if (Status != STATUS_SUCCESS)
555 ThreadStatus = STATUS_ALERTED;
556 (VOID)PsUnblockThread(Thread, &ThreadStatus);
558 ObDereferenceObject(Thread);
559 return(STATUS_SUCCESS);
563 NtOpenThread(OUT PHANDLE ThreadHandle,
564 IN ACCESS_MASK DesiredAccess,
565 IN POBJECT_ATTRIBUTES ObjectAttributes,
566 IN PCLIENT_ID ClientId)
572 NtContinue(IN PCONTEXT Context,
573 IN BOOLEAN TestAlert)
575 PKTRAP_FRAME TrapFrame;
578 * Copy the supplied context over the register information that was saved
579 * on entry to kernel mode, it will then be restored on exit
580 * FIXME: Validate the context
582 TrapFrame = KeGetCurrentThread()->TrapFrame;
583 if (TrapFrame == NULL)
585 CPRINT("NtContinue called but TrapFrame was NULL\n");
588 KeContextToTrapFrame(Context, TrapFrame);
589 return(STATUS_SUCCESS);
594 NtYieldExecution(VOID)
596 PsDispatchThread(THREAD_STATE_READY);
597 return(STATUS_SUCCESS);
602 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
603 OUT PEPROCESS *Process OPTIONAL,
604 OUT PETHREAD *Thread)
607 PLIST_ENTRY current_entry;
610 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
612 current_entry = PiThreadListHead.Flink;
613 while (current_entry != &PiThreadListHead)
615 current = CONTAINING_RECORD(current_entry,
617 Tcb.ThreadListEntry);
618 if (current->Cid.UniqueThread == Cid->UniqueThread &&
619 current->Cid.UniqueProcess == Cid->UniqueProcess)
622 *Process = current->ThreadsProcess;
624 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
625 return(STATUS_SUCCESS);
628 current_entry = current_entry->Flink;
631 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
633 return(STATUS_INVALID_PARAMETER);
638 PsLookupThreadByThreadId(IN PVOID ThreadId,
639 OUT PETHREAD *Thread)
642 PLIST_ENTRY current_entry;
645 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
647 current_entry = PiThreadListHead.Flink;
648 while (current_entry != &PiThreadListHead)
650 current = CONTAINING_RECORD(current_entry,
652 Tcb.ThreadListEntry);
653 if (current->Cid.UniqueThread == (HANDLE)ThreadId)
656 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
657 return(STATUS_SUCCESS);
660 current_entry = current_entry->Flink;
663 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
665 return(STATUS_INVALID_PARAMETER);
668 #endif /* LIBCAPTIVE */