1 /* $Id: thread.c,v 1.121 2003/11/02 01:16:21 ekohl Exp $
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 BOOLEAN 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 KeAcquireDispatcherDatabaseLockAtDpcLevel();
350 WaitBlock
= (PKWAIT_BLOCK
)Thread
->Tcb
.WaitBlockList
;
353 RemoveEntryList (&WaitBlock
->WaitListEntry
);
354 WaitBlock
= WaitBlock
->NextWaitBlock
;
356 Thread
->Tcb
.WaitBlockList
= NULL
;
357 KeReleaseDispatcherDatabaseLockFromDpcLevel();
358 PsDispatchThreadNoLock (THREAD_STATE_READY
);
361 *Status
= STATUS_KERNEL_APC
;
368 KeReleaseDispatcherDatabaseLockFromDpcLevel();
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 PsThreadType
->Tag
= TAG('T', 'H', 'R', 'T');
466 PsThreadType
->TotalObjects
= 0;
467 PsThreadType
->TotalHandles
= 0;
468 PsThreadType
->MaxObjects
= 0;
469 PsThreadType
->MaxHandles
= 0;
470 PsThreadType
->PagedPoolCharge
= 0;
471 PsThreadType
->NonpagedPoolCharge
= sizeof(ETHREAD
);
472 PsThreadType
->Mapping
= &PiThreadMapping
;
473 PsThreadType
->Dump
= NULL
;
474 PsThreadType
->Open
= NULL
;
475 PsThreadType
->Close
= NULL
;
476 PsThreadType
->Delete
= PiDeleteThread
;
477 PsThreadType
->Parse
= NULL
;
478 PsThreadType
->Security
= NULL
;
479 PsThreadType
->QueryName
= NULL
;
480 PsThreadType
->OkayToClose
= NULL
;
481 PsThreadType
->Create
= NULL
;
482 PsThreadType
->DuplicationNotify
= NULL
;
484 RtlInitUnicodeStringFromLiteral(&PsThreadType
->TypeName
, L
"Thread");
486 ObpCreateTypeObject(PsThreadType
);
488 PsInitializeThread(NULL
,&FirstThread
,&FirstThreadHandle
,
489 THREAD_ALL_ACCESS
,NULL
, TRUE
);
490 FirstThread
->Tcb
.State
= THREAD_STATE_RUNNING
;
491 FirstThread
->Tcb
.FreezeCount
= 0;
492 ((PIKPCR
) KeGetCurrentKPCR())->CurrentThread
= (PVOID
)FirstThread
;
493 NtClose(FirstThreadHandle
);
495 DPRINT("FirstThread %x\n",FirstThread
);
500 * Create the reaper thread
502 KeInitializeEvent(&PiReaperThreadEvent
, SynchronizationEvent
, FALSE
);
503 Status
= PsCreateSystemThread(&PiReaperThreadHandle
,
508 (PKSTART_ROUTINE
) PiReaperThreadMain
,
510 if (!NT_SUCCESS(Status
))
512 DPRINT1("PS: Failed to create reaper thread.\n");
521 KeSetBasePriorityThread (PKTHREAD Thread
,
524 * Sets thread's base priority relative to the process' base priority
525 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
533 else if (Increment
> 2)
537 Priority
= ((PETHREAD
)Thread
)->ThreadsProcess
->Pcb
.BasePriority
+ Increment
;
538 if (Priority
< LOW_PRIORITY
)
540 Priority
= LOW_PRIORITY
;
542 else if (Priority
>= MAXIMUM_PRIORITY
)
544 Thread
->BasePriority
= HIGH_PRIORITY
;
546 KeSetPriorityThread(Thread
, Priority
);
555 KeSetPriorityThread (PKTHREAD Thread
, KPRIORITY Priority
)
557 KPRIORITY OldPriority
;
559 PKTHREAD CurrentThread
;
562 if (Priority
< LOW_PRIORITY
|| Priority
>= MAXIMUM_PRIORITY
)
567 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
569 OldPriority
= Thread
->Priority
;
570 Thread
->BasePriority
= Thread
->Priority
= (CHAR
)Priority
;
572 if (OldPriority
!= Priority
)
574 if (Thread
->State
== THREAD_STATE_READY
)
576 PsRemoveFromThreadList((PETHREAD
)Thread
);
577 PsInsertIntoThreadList(Priority
, (PETHREAD
)Thread
);
578 CurrentThread
= ((PIKPCR
) KeGetCurrentKPCR())->CurrentThread
;
579 if (CurrentThread
->Priority
< Priority
)
581 PsDispatchThreadNoLock(THREAD_STATE_READY
);
582 KeLowerIrql(oldIrql
);
583 return (OldPriority
);
586 else if (Thread
->State
== THREAD_STATE_RUNNING
)
588 if (Priority
< OldPriority
)
590 /* Check for threads with a higher priority */
591 Mask
= ~((1 << (Priority
+ 1)) - 1);
592 if (PriorityListMask
& Mask
)
594 PsDispatchThreadNoLock(THREAD_STATE_READY
);
595 KeLowerIrql(oldIrql
);
596 return (OldPriority
);
601 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
609 KeSetAffinityThread(PKTHREAD Thread
,
612 * Sets thread's affinity
615 DPRINT1("KeSetAffinityThread() is a stub returning STATUS_SUCCESS");
616 return STATUS_SUCCESS
; // FIXME: Use function below
617 //return ZwSetInformationThread(handle, ThreadAffinityMask,<pointer to affinity mask>,sizeof(KAFFINITY));
622 NtAlertResumeThread(IN HANDLE ThreadHandle
,
623 OUT PULONG SuspendCount
)
630 NtAlertThread (IN HANDLE ThreadHandle
)
634 NTSTATUS ThreadStatus
;
636 Status
= ObReferenceObjectByHandle(ThreadHandle
,
637 THREAD_SUSPEND_RESUME
,
642 if (Status
!= STATUS_SUCCESS
)
647 ThreadStatus
= STATUS_ALERTED
;
648 (VOID
)PsUnblockThread(Thread
, &ThreadStatus
);
650 ObDereferenceObject(Thread
);
651 return(STATUS_SUCCESS
);
654 /**********************************************************************
660 NtOpenThread(OUT PHANDLE ThreadHandle
,
661 IN ACCESS_MASK DesiredAccess
,
662 IN POBJECT_ATTRIBUTES ObjectAttributes
,
663 IN PCLIENT_ID ClientId
)
665 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
667 if((NULL
!= ThreadHandle
)&&(NULL
!= ObjectAttributes
))
669 PETHREAD EThread
= NULL
;
672 && (ClientId
->UniqueProcess
)
673 && (ClientId
->UniqueThread
))
675 // It is an error to specify both
676 // ObjectAttributes.ObjectName
678 if((ObjectAttributes
)
679 && (ObjectAttributes
->ObjectName
)
680 && (0 < ObjectAttributes
->ObjectName
->Length
))
682 return(STATUS_INVALID_PARAMETER_MIX
);
685 Status
= PsLookupProcessThreadByCid(ClientId
,
689 else if((ObjectAttributes
)
690 && (ObjectAttributes
->ObjectName
)
691 && (0 < ObjectAttributes
->ObjectName
->Length
))
693 // Three Ob attributes are forbidden
694 if(!(ObjectAttributes
->Attributes
&
695 (OBJ_PERMANENT
| OBJ_EXCLUSIVE
| OBJ_OPENIF
)))
697 Status
= ObReferenceObjectByName(ObjectAttributes
->ObjectName
,
698 ObjectAttributes
->Attributes
,
707 // EThread may be OK...
708 if(STATUS_SUCCESS
== Status
)
710 Status
= ObCreateHandle(PsGetCurrentProcess(),
715 ObDereferenceObject(EThread
);
722 NtContinue(IN PCONTEXT Context
,
723 IN BOOLEAN TestAlert
)
725 PKTRAP_FRAME TrapFrame
;
728 * Copy the supplied context over the register information that was saved
729 * on entry to kernel mode, it will then be restored on exit
730 * FIXME: Validate the context
732 TrapFrame
= KeGetCurrentThread()->TrapFrame
;
733 if (TrapFrame
== NULL
)
735 CPRINT("NtContinue called but TrapFrame was NULL\n");
738 KeContextToTrapFrame(Context
, TrapFrame
);
739 return(STATUS_SUCCESS
);
744 NtYieldExecution(VOID
)
746 PsDispatchThread(THREAD_STATE_READY
);
747 return(STATUS_SUCCESS
);
755 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid
,
756 OUT PEPROCESS
*Process OPTIONAL
,
757 OUT PETHREAD
*Thread
)
760 PLIST_ENTRY current_entry
;
763 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
765 current_entry
= PiThreadListHead
.Flink
;
766 while (current_entry
!= &PiThreadListHead
)
768 current
= CONTAINING_RECORD(current_entry
,
770 Tcb
.ThreadListEntry
);
771 if (current
->Cid
.UniqueThread
== Cid
->UniqueThread
&&
772 current
->Cid
.UniqueProcess
== Cid
->UniqueProcess
)
776 *Process
= current
->ThreadsProcess
;
777 ObReferenceObject(current
->ThreadsProcess
);
781 ObReferenceObject(current
);
783 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
784 return(STATUS_SUCCESS
);
787 current_entry
= current_entry
->Flink
;
790 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
792 return(STATUS_INVALID_PARAMETER
);
800 PsLookupThreadByThreadId(IN PVOID ThreadId
,
801 OUT PETHREAD
*Thread
)
804 PLIST_ENTRY current_entry
;
807 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
809 current_entry
= PiThreadListHead
.Flink
;
810 while (current_entry
!= &PiThreadListHead
)
812 current
= CONTAINING_RECORD(current_entry
,
814 Tcb
.ThreadListEntry
);
815 if (current
->Cid
.UniqueThread
== (HANDLE
)ThreadId
)
817 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
819 ObReferenceObject(current
);
820 return(STATUS_SUCCESS
);
823 current_entry
= current_entry
->Flink
;
826 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
828 return(STATUS_INVALID_PARAMETER
);