1 /* $Id: thread.c,v 1.126 2004/07/02 00:47:57 royce 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>
31 #include <rosrtl/string.h>
34 #include <internal/debug.h>
36 /* TYPES *******************************************************************/
38 /* GLOBALS ******************************************************************/
40 POBJECT_TYPE EXPORTED PsThreadType
= NULL
;
42 KSPIN_LOCK PiThreadListLock
;
45 * PURPOSE: List of threads associated with each priority level
47 LIST_ENTRY PiThreadListHead
;
48 static LIST_ENTRY PriorityListHead
[MAXIMUM_PRIORITY
];
49 static ULONG PriorityListMask
= 0;
50 static BOOLEAN DoneInitYet
= FALSE
;
51 static PETHREAD IdleThreads
[MAXIMUM_PROCESSORS
];
52 ULONG PiNrThreads
= 0;
53 ULONG PiNrReadyThreads
= 0;
54 static HANDLE PiReaperThreadHandle
;
55 static KEVENT PiReaperThreadEvent
;
56 static BOOLEAN PiReaperThreadShouldTerminate
= FALSE
;
57 ULONG PiNrThreadsAwaitingReaping
= 0;
59 static GENERIC_MAPPING PiThreadMapping
= {THREAD_READ
,
64 /* FUNCTIONS ***************************************************************/
69 PKTHREAD STDCALL
KeGetCurrentThread(VOID
)
71 return(((PIKPCR
) KeGetCurrentKPCR())->CurrentThread
);
77 HANDLE STDCALL
PsGetCurrentThreadId(VOID
)
79 return(PsGetCurrentThread()->Cid
.UniqueThread
);
83 PsInsertIntoThreadList(KPRIORITY Priority
, PETHREAD Thread
)
85 assert(THREAD_STATE_READY
== Thread
->Tcb
.State
);
86 if (Priority
>= MAXIMUM_PRIORITY
|| Priority
< LOW_PRIORITY
)
88 DPRINT1("Invalid thread priority (%d)\n", Priority
);
91 InsertTailList(&PriorityListHead
[Priority
], &Thread
->Tcb
.QueueListEntry
);
92 PriorityListMask
|= (1 << Priority
);
96 static VOID
PsRemoveFromThreadList(PETHREAD Thread
)
98 assert(THREAD_STATE_READY
== Thread
->Tcb
.State
);
99 RemoveEntryList(&Thread
->Tcb
.QueueListEntry
);
100 if (IsListEmpty(&PriorityListHead
[(ULONG
)Thread
->Tcb
.Priority
]))
102 PriorityListMask
&= ~(1 << Thread
->Tcb
.Priority
);
108 VOID
PsDumpThreads(BOOLEAN IncludeSystem
)
110 PLIST_ENTRY current_entry
;
115 current_entry
= PiThreadListHead
.Flink
;
118 while (current_entry
!= &PiThreadListHead
)
123 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
124 Tcb
.ThreadListEntry
);
128 DbgPrint("Too many threads on list\n");
131 if (IncludeSystem
|| current
->ThreadsProcess
->UniqueProcessId
>= 6)
133 DbgPrint("current->Tcb.State %d PID.TID %d.%d Name %.8s Stack: \n",
135 current
->ThreadsProcess
->UniqueProcessId
,
136 current
->Cid
.UniqueThread
,
137 current
->ThreadsProcess
->ImageFileName
);
138 if (current
->Tcb
.State
== THREAD_STATE_READY
||
139 current
->Tcb
.State
== THREAD_STATE_SUSPENDED
||
140 current
->Tcb
.State
== THREAD_STATE_BLOCKED
)
142 Esp
= (PULONG
)current
->Tcb
.KernelStack
;
143 Ebp
= (PULONG
)Esp
[3];
144 DbgPrint("Ebp 0x%.8X\n", Ebp
);
146 while (Ebp
!= 0 && Ebp
>= (PULONG
)current
->Tcb
.StackLimit
)
148 DbgPrint("%.8X %.8X%s", Ebp
[0], Ebp
[1],
149 (i
% 8) == 7 ? "\n" : " ");
150 Ebp
= (PULONG
)Ebp
[0];
159 current_entry
= current_entry
->Flink
;
163 static PETHREAD
PsScanThreadList (KPRIORITY Priority
, ULONG Affinity
)
165 PLIST_ENTRY current_entry
;
169 Mask
= (1 << Priority
);
170 if (PriorityListMask
& Mask
)
172 current_entry
= PriorityListHead
[Priority
].Flink
;
173 while (current_entry
!= &PriorityListHead
[Priority
])
175 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
177 if (current
->Tcb
.State
!= THREAD_STATE_READY
)
179 DPRINT1("%d/%d\n", current
->Cid
.UniqueThread
, current
->Tcb
.State
);
181 assert(current
->Tcb
.State
== THREAD_STATE_READY
);
182 DPRINT("current->Tcb.UserAffinity %x Affinity %x PID %d %d\n",
183 current
->Tcb
.UserAffinity
, Affinity
, current
->Cid
.UniqueThread
,
185 if (current
->Tcb
.UserAffinity
& Affinity
)
187 PsRemoveFromThreadList(current
);
190 current_entry
= current_entry
->Flink
;
197 PiWakeupReaperThread(VOID
)
199 KeSetEvent(&PiReaperThreadEvent
, 0, FALSE
);
203 PiReaperThreadMain(PVOID Ignored
)
207 KeWaitForSingleObject(&PiReaperThreadEvent
,
212 if (PiReaperThreadShouldTerminate
)
214 PsTerminateSystemThread(0);
220 VOID
PsDispatchThreadNoLock (ULONG NewThreadStatus
)
222 KPRIORITY CurrentPriority
;
225 PKTHREAD KCurrentThread
= ((PIKPCR
) KeGetCurrentKPCR())->CurrentThread
;
226 PETHREAD CurrentThread
= CONTAINING_RECORD(KCurrentThread
, ETHREAD
, Tcb
);
228 DPRINT("PsDispatchThread() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
229 CurrentThread
->Cid
.UniqueThread
, NewThreadStatus
, CurrentThread
->Tcb
.State
);
231 CurrentThread
->Tcb
.State
= (UCHAR
)NewThreadStatus
;
232 if (CurrentThread
->Tcb
.State
== THREAD_STATE_READY
)
234 PsInsertIntoThreadList(CurrentThread
->Tcb
.Priority
,
237 if (CurrentThread
->Tcb
.State
== THREAD_STATE_TERMINATED_1
)
239 PiNrThreadsAwaitingReaping
++;
242 Affinity
= 1 << KeGetCurrentProcessorNumber();
243 for (CurrentPriority
= HIGH_PRIORITY
;
244 CurrentPriority
>= LOW_PRIORITY
;
247 Candidate
= PsScanThreadList(CurrentPriority
, Affinity
);
248 if (Candidate
== CurrentThread
)
250 Candidate
->Tcb
.State
= THREAD_STATE_RUNNING
;
251 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock
);
254 if (Candidate
!= NULL
)
258 DPRINT("Scheduling %x(%d)\n",Candidate
, CurrentPriority
);
260 Candidate
->Tcb
.State
= THREAD_STATE_RUNNING
;
262 OldThread
= CurrentThread
;
263 CurrentThread
= Candidate
;
266 * This code is moved to the end of KiArchContextSwitch.
267 * It should be execute after the context switch.
269 KeReleaseSpinLockFromDpcLevel(&PiThreadListLock
);
270 if (PiNrThreadsAwaitingReaping
> 0)
272 PiWakeupReaperThread();
275 KiArchContextSwitch(&CurrentThread
->Tcb
, &OldThread
->Tcb
);
279 CPRINT("CRITICAL: No threads are ready\n");
284 PsDispatchThread(ULONG NewThreadStatus
)
293 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
297 ((PIKPCR
) KeGetCurrentKPCR())->CurrentThread
->WaitIrql
= oldIrql
;
298 PsDispatchThreadNoLock(NewThreadStatus
);
299 KeLowerIrql(oldIrql
);
303 PsUnblockThread(PETHREAD Thread
, PNTSTATUS WaitStatus
)
307 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
308 if (THREAD_STATE_TERMINATED_1
== Thread
->Tcb
.State
||
309 THREAD_STATE_TERMINATED_2
== Thread
->Tcb
.State
)
311 DPRINT("Can't unblock thread %d because it's terminating\n",
312 Thread
->Cid
.UniqueThread
);
314 else if (THREAD_STATE_READY
== Thread
->Tcb
.State
||
315 THREAD_STATE_RUNNING
== Thread
->Tcb
.State
)
317 DPRINT("Can't unblock thread %d because it's ready or running\n",
318 Thread
->Cid
.UniqueThread
);
322 if (WaitStatus
!= NULL
)
324 Thread
->Tcb
.WaitStatus
= *WaitStatus
;
326 Thread
->Tcb
.State
= THREAD_STATE_READY
;
327 PsInsertIntoThreadList(Thread
->Tcb
.Priority
, Thread
);
329 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
333 PsBlockThread(PNTSTATUS Status
, UCHAR Alertable
, ULONG WaitMode
,
334 BOOLEAN DispatcherLock
, KIRQL WaitIrql
, UCHAR WaitReason
)
339 PKWAIT_BLOCK WaitBlock
;
341 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
343 KThread
= ((PIKPCR
) KeGetCurrentKPCR())->CurrentThread
;
344 Thread
= CONTAINING_RECORD (KThread
, ETHREAD
, Tcb
);
345 if (KThread
->ApcState
.KernelApcPending
)
349 KeAcquireDispatcherDatabaseLockAtDpcLevel();
351 WaitBlock
= (PKWAIT_BLOCK
)Thread
->Tcb
.WaitBlockList
;
354 RemoveEntryList (&WaitBlock
->WaitListEntry
);
355 WaitBlock
= WaitBlock
->NextWaitBlock
;
357 Thread
->Tcb
.WaitBlockList
= NULL
;
358 KeReleaseDispatcherDatabaseLockFromDpcLevel();
359 PsDispatchThreadNoLock (THREAD_STATE_READY
);
362 *Status
= STATUS_KERNEL_APC
;
369 KeReleaseDispatcherDatabaseLockFromDpcLevel();
371 Thread
->Tcb
.Alertable
= Alertable
;
372 Thread
->Tcb
.WaitMode
= (UCHAR
)WaitMode
;
373 Thread
->Tcb
.WaitIrql
= WaitIrql
;
374 Thread
->Tcb
.WaitReason
= WaitReason
;
375 PsDispatchThreadNoLock(THREAD_STATE_BLOCKED
);
379 *Status
= Thread
->Tcb
.WaitStatus
;
382 KeLowerIrql(WaitIrql
);
386 PsFreezeAllThreads(PEPROCESS Process
)
388 * Used by the debugging code to freeze all the process's threads
389 * while the debugger is examining their state.
393 PLIST_ENTRY current_entry
;
396 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
398 current_entry
= Process
->ThreadListHead
.Flink
;
399 while (current_entry
!= &Process
->ThreadListHead
)
401 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
402 Tcb
.ProcessThreadListEntry
);
405 * We have to be careful here, we can't just set the freeze the
406 * thread inside kernel mode since it may be holding a lock.
409 current_entry
= current_entry
->Flink
;
412 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
416 PsApplicationProcessorInit(VOID
)
418 ((PIKPCR
) KeGetCurrentKPCR())->CurrentThread
=
419 (PVOID
)IdleThreads
[KeGetCurrentProcessorNumber()];
423 PsPrepareForApplicationProcessorInit(ULONG Id
)
426 HANDLE IdleThreadHandle
;
428 PsInitializeThread(NULL
,
434 IdleThread
->Tcb
.State
= THREAD_STATE_RUNNING
;
435 IdleThread
->Tcb
.FreezeCount
= 0;
436 IdleThread
->Tcb
.UserAffinity
= 1 << Id
;
437 IdleThread
->Tcb
.Priority
= LOW_PRIORITY
;
438 IdleThreads
[Id
] = IdleThread
;
440 NtClose(IdleThreadHandle
);
441 DPRINT("IdleThread for Processor %d has PID %d\n",
442 Id
, IdleThread
->Cid
.UniqueThread
);
446 PsInitThreadManagment(VOID
)
448 * FUNCTION: Initialize thread managment
451 PETHREAD FirstThread
;
453 HANDLE FirstThreadHandle
;
456 KeInitializeSpinLock(&PiThreadListLock
);
457 for (i
=0; i
< MAXIMUM_PRIORITY
; i
++)
459 InitializeListHead(&PriorityListHead
[i
]);
462 InitializeListHead(&PiThreadListHead
);
464 PsThreadType
= ExAllocatePool(NonPagedPool
,sizeof(OBJECT_TYPE
));
466 PsThreadType
->Tag
= TAG('T', 'H', 'R', 'T');
467 PsThreadType
->TotalObjects
= 0;
468 PsThreadType
->TotalHandles
= 0;
469 PsThreadType
->MaxObjects
= 0;
470 PsThreadType
->MaxHandles
= 0;
471 PsThreadType
->PagedPoolCharge
= 0;
472 PsThreadType
->NonpagedPoolCharge
= sizeof(ETHREAD
);
473 PsThreadType
->Mapping
= &PiThreadMapping
;
474 PsThreadType
->Dump
= NULL
;
475 PsThreadType
->Open
= NULL
;
476 PsThreadType
->Close
= NULL
;
477 PsThreadType
->Delete
= PiDeleteThread
;
478 PsThreadType
->Parse
= NULL
;
479 PsThreadType
->Security
= NULL
;
480 PsThreadType
->QueryName
= NULL
;
481 PsThreadType
->OkayToClose
= NULL
;
482 PsThreadType
->Create
= NULL
;
483 PsThreadType
->DuplicationNotify
= NULL
;
485 RtlRosInitUnicodeStringFromLiteral(&PsThreadType
->TypeName
, L
"Thread");
487 ObpCreateTypeObject(PsThreadType
);
489 PsInitializeThread(NULL
,&FirstThread
,&FirstThreadHandle
,
490 THREAD_ALL_ACCESS
,NULL
, TRUE
);
491 FirstThread
->Tcb
.State
= THREAD_STATE_RUNNING
;
492 FirstThread
->Tcb
.FreezeCount
= 0;
493 ((PIKPCR
) KeGetCurrentKPCR())->CurrentThread
= (PVOID
)FirstThread
;
494 NtClose(FirstThreadHandle
);
496 DPRINT("FirstThread %x\n",FirstThread
);
501 * Create the reaper thread
503 KeInitializeEvent(&PiReaperThreadEvent
, SynchronizationEvent
, FALSE
);
504 Status
= PsCreateSystemThread(&PiReaperThreadHandle
,
511 if (!NT_SUCCESS(Status
))
513 DPRINT1("PS: Failed to create reaper thread.\n");
522 KeSetBasePriorityThread (PKTHREAD Thread
,
525 * Sets thread's base priority relative to the process' base priority
526 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
534 else if (Increment
> 2)
538 Priority
= ((PETHREAD
)Thread
)->ThreadsProcess
->Pcb
.BasePriority
+ Increment
;
539 if (Priority
< LOW_PRIORITY
)
541 Priority
= LOW_PRIORITY
;
543 else if (Priority
>= MAXIMUM_PRIORITY
)
545 Thread
->BasePriority
= HIGH_PRIORITY
;
547 KeSetPriorityThread(Thread
, Priority
);
556 KeSetPriorityThread (PKTHREAD Thread
, KPRIORITY Priority
)
558 KPRIORITY OldPriority
;
560 PKTHREAD CurrentThread
;
563 if (Priority
< LOW_PRIORITY
|| Priority
>= MAXIMUM_PRIORITY
)
568 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
570 OldPriority
= Thread
->Priority
;
571 Thread
->BasePriority
= Thread
->Priority
= (CHAR
)Priority
;
573 if (OldPriority
!= Priority
)
575 if (Thread
->State
== THREAD_STATE_READY
)
577 PsRemoveFromThreadList((PETHREAD
)Thread
);
578 PsInsertIntoThreadList(Priority
, (PETHREAD
)Thread
);
579 CurrentThread
= ((PIKPCR
) KeGetCurrentKPCR())->CurrentThread
;
580 if (CurrentThread
->Priority
< Priority
)
582 PsDispatchThreadNoLock(THREAD_STATE_READY
);
583 KeLowerIrql(oldIrql
);
584 return (OldPriority
);
587 else if (Thread
->State
== THREAD_STATE_RUNNING
)
589 if (Priority
< OldPriority
)
591 /* Check for threads with a higher priority */
592 Mask
= ~((1 << (Priority
+ 1)) - 1);
593 if (PriorityListMask
& Mask
)
595 PsDispatchThreadNoLock(THREAD_STATE_READY
);
596 KeLowerIrql(oldIrql
);
597 return (OldPriority
);
602 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
610 KeSetAffinityThread(PKTHREAD Thread
,
613 * Sets thread's affinity
616 DPRINT1("KeSetAffinityThread() is a stub returning STATUS_SUCCESS");
617 return STATUS_SUCCESS
; // FIXME: Use function below
618 //return ZwSetInformationThread(handle, ThreadAffinityMask,<pointer to affinity mask>,sizeof(KAFFINITY));
623 NtAlertResumeThread(IN HANDLE ThreadHandle
,
624 OUT PULONG SuspendCount
)
627 return(STATUS_NOT_IMPLEMENTED
);
633 NtAlertThread (IN HANDLE ThreadHandle
)
637 NTSTATUS ThreadStatus
;
639 Status
= ObReferenceObjectByHandle(ThreadHandle
,
640 THREAD_SUSPEND_RESUME
,
645 if (Status
!= STATUS_SUCCESS
)
650 ThreadStatus
= STATUS_ALERTED
;
651 (VOID
)PsUnblockThread(Thread
, &ThreadStatus
);
653 ObDereferenceObject(Thread
);
654 return(STATUS_SUCCESS
);
657 /**********************************************************************
663 NtOpenThread(OUT PHANDLE ThreadHandle
,
664 IN ACCESS_MASK DesiredAccess
,
665 IN POBJECT_ATTRIBUTES ObjectAttributes
,
666 IN PCLIENT_ID ClientId
)
668 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
670 if((NULL
!= ThreadHandle
)&&(NULL
!= ObjectAttributes
))
672 PETHREAD EThread
= NULL
;
675 && (ClientId
->UniqueProcess
)
676 && (ClientId
->UniqueThread
))
678 // It is an error to specify both
679 // ObjectAttributes.ObjectName
681 if((ObjectAttributes
)
682 && (ObjectAttributes
->ObjectName
)
683 && (0 < ObjectAttributes
->ObjectName
->Length
))
685 return(STATUS_INVALID_PARAMETER_MIX
);
688 Status
= PsLookupProcessThreadByCid(ClientId
,
692 else if((ObjectAttributes
)
693 && (ObjectAttributes
->ObjectName
)
694 && (0 < ObjectAttributes
->ObjectName
->Length
))
696 // Three Ob attributes are forbidden
697 if(!(ObjectAttributes
->Attributes
&
698 (OBJ_PERMANENT
| OBJ_EXCLUSIVE
| OBJ_OPENIF
)))
700 Status
= ObReferenceObjectByName(ObjectAttributes
->ObjectName
,
701 ObjectAttributes
->Attributes
,
710 // EThread may be OK...
711 if(STATUS_SUCCESS
== Status
)
713 Status
= ObCreateHandle(PsGetCurrentProcess(),
718 ObDereferenceObject(EThread
);
725 NtYieldExecution(VOID
)
727 PsDispatchThread(THREAD_STATE_READY
);
728 return(STATUS_SUCCESS
);
736 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid
,
737 OUT PEPROCESS
*Process OPTIONAL
,
738 OUT PETHREAD
*Thread
)
741 PLIST_ENTRY current_entry
;
744 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
746 current_entry
= PiThreadListHead
.Flink
;
747 while (current_entry
!= &PiThreadListHead
)
749 current
= CONTAINING_RECORD(current_entry
,
751 Tcb
.ThreadListEntry
);
752 if (current
->Cid
.UniqueThread
== Cid
->UniqueThread
&&
753 current
->Cid
.UniqueProcess
== Cid
->UniqueProcess
)
757 *Process
= current
->ThreadsProcess
;
758 ObReferenceObject(current
->ThreadsProcess
);
762 ObReferenceObject(current
);
764 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
765 return(STATUS_SUCCESS
);
768 current_entry
= current_entry
->Flink
;
771 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
773 return(STATUS_INVALID_PARAMETER
);
781 PsLookupThreadByThreadId(IN PVOID ThreadId
,
782 OUT PETHREAD
*Thread
)
785 PLIST_ENTRY current_entry
;
788 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
790 current_entry
= PiThreadListHead
.Flink
;
791 while (current_entry
!= &PiThreadListHead
)
793 current
= CONTAINING_RECORD(current_entry
,
795 Tcb
.ThreadListEntry
);
796 if (current
->Cid
.UniqueThread
== (HANDLE
)ThreadId
)
798 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
800 ObReferenceObject(current
);
801 return(STATUS_SUCCESS
);
804 current_entry
= current_entry
->Flink
;
807 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
809 return(STATUS_INVALID_PARAMETER
);