2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/thread.c
5 * PURPOSE: Thread managment
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
11 /* INCLUDES ****************************************************************/
15 #include <internal/debug.h>
17 /* GLOBALS ******************************************************************/
19 extern LIST_ENTRY PsActiveProcessHead
;
20 extern PEPROCESS PsIdleProcess
;
21 extern PVOID PspSystemDllEntryPoint
;
22 extern PHANDLE_TABLE PspCidTable
;
24 POBJECT_TYPE PsThreadType
= NULL
;
26 /* FUNCTIONS ***************************************************************/
30 PspThreadSpecialApc(PKAPC Apc
,
31 PKNORMAL_ROUTINE
* NormalRoutine
,
33 PVOID
* SystemArgument1
,
34 PVOID
* SystemArgument2
)
41 PspUserThreadStartup(PKSTART_ROUTINE StartRoutine
,
45 PETHREAD Thread
= PsGetCurrentThread();
47 DPRINT("I am a new USER thread. This is my start routine: %p. This my context: %p."
48 "This is my IRQL: %d. This is my Thread Pointer: %x.\n", StartRoutine
,
49 StartContext
, KeGetCurrentIrql(), Thread
);
51 if (!Thread
->Terminated
) {
53 /* Allocate the APC */
54 ThreadApc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG('T', 'h', 'r','d'));
57 KeInitializeApc(ThreadApc
,
59 OriginalApcEnvironment
,
62 PspSystemDllEntryPoint
,
66 /* Insert it into the queue */
67 KeInsertQueueApc(ThreadApc
, NULL
, NULL
, IO_NO_INCREMENT
);
68 Thread
->Tcb
.ApcState
.UserApcPending
= TRUE
;
71 /* Go to Passive Level and notify debugger */
72 KeLowerIrql(PASSIVE_LEVEL
);
73 DbgkCreateThread(StartContext
);
78 PspSystemThreadStartup(PKSTART_ROUTINE StartRoutine
,
81 PETHREAD Thread
= PsGetCurrentThread();
83 /* Unlock the dispatcher Database */
84 KeLowerIrql(PASSIVE_LEVEL
);
86 /* Make sure it's not terminated by now */
87 if (!Thread
->Terminated
) {
90 (StartRoutine
)(StartContext
);
94 PspExitThread(STATUS_SUCCESS
);
99 PspCreateThread(OUT PHANDLE ThreadHandle
,
100 IN ACCESS_MASK DesiredAccess
,
101 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
102 IN HANDLE ProcessHandle
,
103 IN PEPROCESS TargetProcess
,
104 OUT PCLIENT_ID ClientId
,
105 IN PCONTEXT ThreadContext
,
106 IN PINITIAL_TEB InitialTeb
,
107 IN BOOLEAN CreateSuspended
,
108 IN PKSTART_ROUTINE StartRoutine OPTIONAL
,
109 IN PVOID StartContext OPTIONAL
)
116 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
118 HANDLE_TABLE_ENTRY CidEntry
;
121 /* Reference the Process by handle or pointer, depending on what we got */
122 DPRINT("PspCreateThread: %x, %x, %x\n", ProcessHandle
, TargetProcess
, ThreadContext
);
125 /* Normal thread or System Thread */
126 DPRINT("Referencing Parent Process\n");
127 Status
= ObReferenceObjectByHandle(ProcessHandle
,
128 PROCESS_CREATE_THREAD
,
135 /* System thread inside System Process, or Normal Thread with a bug */
138 /* Reference the Process by Pointer */
139 DPRINT("Referencing Parent System Process\n");
140 ObReferenceObject(TargetProcess
);
141 Process
= TargetProcess
;
142 Status
= STATUS_SUCCESS
;
146 /* Fake ObReference returning this */
147 Status
= STATUS_INVALID_HANDLE
;
151 /* Check for success */
152 if(!NT_SUCCESS(Status
)) {
154 DPRINT1("Invalid Process Handle, or no handle given\n");
158 /* Create Thread Object */
159 DPRINT("Creating Thread Object\n");
160 Status
= ObCreateObject(PreviousMode
,
170 /* Check for success */
171 if (!NT_SUCCESS(Status
)) {
173 /* Dereference the Process */
174 DPRINT1("Failed to Create Thread Object\n");
175 ObDereferenceObject(Process
);
179 /* Zero the Object entirely */
180 DPRINT("Cleaning Thread Object\n");
181 RtlZeroMemory(Thread
, sizeof(ETHREAD
));
183 /* Create Cid Handle */
184 DPRINT("Creating Thread Handle (CID)\n");
185 CidEntry
.u1
.Object
= Thread
;
186 CidEntry
.u2
.GrantedAccess
= 0;
187 Thread
->Cid
.UniqueThread
= ExCreateHandle(PspCidTable
, &CidEntry
);
188 if (!Thread
->Cid
.UniqueThread
) {
190 DPRINT1("Failed to create Thread Handle (CID)\n");
191 ObDereferenceObject(Process
);
192 ObDereferenceObject(Thread
);
193 return STATUS_INSUFFICIENT_RESOURCES
;
196 /* Initialize Lists */
197 DPRINT("Initialliazing Thread Lists and Locks\n");
198 InitializeListHead(&Thread
->LpcReplyChain
);
199 InitializeListHead(&Thread
->IrpList
);
200 InitializeListHead(&Thread
->ActiveTimerListHead
);
201 KeInitializeSpinLock(&Thread
->ActiveTimerListLock
);
204 DPRINT("Initialliazing Thread Semaphore\n");
205 KeInitializeSemaphore(&Thread
->LpcReplySemaphore
, 0, MAXLONG
);
207 /* Allocate Stack for non-GUI Thread */
208 DPRINT("Initialliazing Thread Stack\n");
209 KernelStack
= MmCreateKernelStack(FALSE
);
211 /* Set the Process CID */
212 DPRINT("Initialliazing Thread PID and Parent Process\n");
213 Thread
->Cid
.UniqueProcess
= Process
->UniqueProcessId
;
214 Thread
->ThreadsProcess
= Process
;
216 /* Now let the kernel initialize the context */
219 /* User-mode Thread */
222 DPRINT("Initialliazing Thread PEB\n");
223 TebBase
= MmCreateTeb(Process
, &Thread
->Cid
, InitialTeb
);
225 /* Set the Start Addresses */
226 DPRINT("Initialliazing Thread Start Addresses :%x, %x\n", ThreadContext
->Eip
, ThreadContext
->Eax
);
227 Thread
->StartAddress
= (PVOID
)ThreadContext
->Eip
;
228 Thread
->Win32StartAddress
= (PVOID
)ThreadContext
->Eax
;
230 /* Let the kernel intialize the Thread */
231 DPRINT("Initialliazing Kernel Thread\n");
232 KeInitializeThread(&Process
->Pcb
,
234 PspUserThreadStartup
,
244 DPRINT("Initialliazing Thread Start Address :%x\n", StartRoutine
);
245 Thread
->StartAddress
= StartRoutine
;
246 Thread
->SystemThread
= TRUE
;
248 /* Let the kernel intialize the Thread */
249 DPRINT("Initialliazing Kernel Thread\n");
250 KeInitializeThread(&Process
->Pcb
,
252 PspSystemThreadStartup
,
261 * Insert the Thread into the Process's Thread List
262 * Note, this is the ETHREAD Thread List. It is removed in
263 * ps/kill.c!PspExitThread.
265 DPRINT("Inserting into Process Thread List \n");
266 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
268 /* Notify Thread Creation */
269 DPRINT("Running Thread Notify \n");
270 PspRunCreateThreadNotifyRoutines(Thread
, TRUE
);
272 /* FIXME: Use Lock */
273 DPRINT("Apcs Queueable: %d \n", Thread
->Tcb
.ApcQueueable
);
274 Thread
->Tcb
.ApcQueueable
= TRUE
;
276 /* Suspend the Thread if we have to */
277 if (CreateSuspended
) {
279 DPRINT("Suspending Thread\n");
280 KeSuspendThread(&Thread
->Tcb
);
283 /* Reference ourselves as a keep-alive */
284 ObReferenceObject(Thread
);
286 /* Insert the Thread into the Object Manager */
287 DPRINT("Inserting Thread\n");
288 Status
= ObInsertObject((PVOID
)Thread
,
295 /* Return Cid and Handle */
296 DPRINT("All worked great!\n");
297 if(NT_SUCCESS(Status
)) {
301 if(ClientId
!= NULL
) {
303 *ClientId
= Thread
->Cid
;
305 *ThreadHandle
= hThread
;
309 Status
= _SEH_GetExceptionCode();
314 /* FIXME: SECURITY */
316 /* Dispatch thread */
317 DPRINT("About to dispatch the thread: %x!\n", &Thread
->Tcb
);
318 OldIrql
= KeAcquireDispatcherDatabaseLock ();
319 KiUnblockThread(&Thread
->Tcb
, NULL
, 0);
320 ObDereferenceObject(Thread
);
321 KeReleaseDispatcherDatabaseLock(OldIrql
);
324 DPRINT("Returning\n");
333 PsCreateSystemThread(PHANDLE ThreadHandle
,
334 ACCESS_MASK DesiredAccess
,
335 POBJECT_ATTRIBUTES ObjectAttributes
,
336 HANDLE ProcessHandle
,
338 PKSTART_ROUTINE StartRoutine
,
341 PEPROCESS TargetProcess
= NULL
;
342 HANDLE Handle
= ProcessHandle
;
344 /* Check if we have a handle. If not, use the System Process */
345 if (!ProcessHandle
) {
348 TargetProcess
= PsInitialSystemProcess
;
351 /* Call the shared function */
352 return PspCreateThread(ThreadHandle
,
370 PsLookupThreadByThreadId(IN HANDLE ThreadId
,
371 OUT PETHREAD
*Thread
)
373 PHANDLE_TABLE_ENTRY CidEntry
;
374 PETHREAD FoundThread
;
375 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
378 KeEnterCriticalRegion();
380 /* Get the CID Handle Entry */
381 if ((CidEntry
= ExMapHandleToPointer(PspCidTable
,
384 /* Get the Process */
385 FoundThread
= CidEntry
->u1
.Object
;
387 /* Make sure it's really a process */
388 if (FoundThread
->Tcb
.DispatcherHeader
.Type
== ThreadObject
)
390 /* Reference and return it */
391 ObReferenceObject(FoundThread
);
392 *Thread
= FoundThread
;
393 Status
= STATUS_SUCCESS
;
396 /* Unlock the Entry */
397 ExUnlockHandleTableEntry(PspCidTable
, CidEntry
);
400 KeLeaveCriticalRegion();
402 /* Return to caller */
411 PsGetCurrentThreadId(VOID
)
413 return(PsGetCurrentThread()->Cid
.UniqueThread
);
421 PsGetThreadFreezeCount(PETHREAD Thread
)
423 return Thread
->Tcb
.FreezeCount
;
431 PsGetThreadHardErrorsAreDisabled(PETHREAD Thread
)
433 return Thread
->HardErrorsAreDisabled
;
441 PsGetThreadId(PETHREAD Thread
)
443 return Thread
->Cid
.UniqueThread
;
451 PsGetThreadProcess(PETHREAD Thread
)
453 return Thread
->ThreadsProcess
;
461 PsGetThreadProcessId(PETHREAD Thread
)
463 return Thread
->Cid
.UniqueProcess
;
471 PsGetThreadSessionId(PETHREAD Thread
)
473 return (HANDLE
)Thread
->ThreadsProcess
->Session
;
481 PsGetThreadTeb(PETHREAD Thread
)
483 return Thread
->Tcb
.Teb
;
491 PsGetThreadWin32Thread(PETHREAD Thread
)
493 return Thread
->Tcb
.Win32Thread
;
501 PsGetCurrentThreadPreviousMode(VOID
)
503 return (KPROCESSOR_MODE
)PsGetCurrentThread()->Tcb
.PreviousMode
;
511 PsGetCurrentThreadStackBase(VOID
)
513 return PsGetCurrentThread()->Tcb
.StackBase
;
521 PsGetCurrentThreadStackLimit(VOID
)
523 return (PVOID
)PsGetCurrentThread()->Tcb
.StackLimit
;
531 PsIsThreadTerminating(IN PETHREAD Thread
)
533 return (Thread
->Terminated
? TRUE
: FALSE
);
541 PsIsSystemThread(PETHREAD Thread
)
543 return (Thread
->SystemThread
? TRUE
: FALSE
);
551 PsIsThreadImpersonating(PETHREAD Thread
)
553 return Thread
->ActiveImpersonationInfo
;
561 PsSetThreadHardErrorsAreDisabled(PETHREAD Thread
,
562 BOOLEAN HardErrorsAreDisabled
)
564 Thread
->HardErrorsAreDisabled
= HardErrorsAreDisabled
;
572 PsSetThreadWin32Thread(PETHREAD Thread
,
575 Thread
->Tcb
.Win32Thread
= Win32Thread
;
580 NtCreateThread(OUT PHANDLE ThreadHandle
,
581 IN ACCESS_MASK DesiredAccess
,
582 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
583 IN HANDLE ProcessHandle
,
584 OUT PCLIENT_ID ClientId
,
585 IN PCONTEXT ThreadContext
,
586 IN PINITIAL_TEB InitialTeb
,
587 IN BOOLEAN CreateSuspended
)
589 INITIAL_TEB SafeInitialTeb
;
591 NTSTATUS Status
= STATUS_SUCCESS
;
595 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
596 ThreadHandle
,ThreadContext
);
598 if(KeGetPreviousMode() != KernelMode
) {
600 if (ThreadContext
== NULL
) {
601 DPRINT1("No context for User-Mode Thread!!\n");
602 return STATUS_INVALID_PARAMETER
;
607 ProbeForWriteHandle(ThreadHandle
);
609 if(ClientId
!= NULL
) {
611 ProbeForWrite(ClientId
,
616 if(ThreadContext
!= NULL
) {
618 ProbeForRead(ThreadContext
,
621 SafeContext
= *ThreadContext
;
622 ThreadContext
= &SafeContext
;
625 ProbeForRead(InitialTeb
,
628 SafeInitialTeb
= *InitialTeb
;
629 InitialTeb
= &SafeInitialTeb
;
633 Status
= _SEH_GetExceptionCode();
637 if (!NT_SUCCESS(Status
)) return Status
;
640 /* Call the shared function */
641 return PspCreateThread(ThreadHandle
,
659 NtOpenThread(OUT PHANDLE ThreadHandle
,
660 IN ACCESS_MASK DesiredAccess
,
661 IN POBJECT_ATTRIBUTES ObjectAttributes
,
662 IN PCLIENT_ID ClientId OPTIONAL
)
664 KPROCESSOR_MODE PreviousMode
;
665 CLIENT_ID SafeClientId
;
666 ULONG Attributes
= 0;
667 HANDLE hThread
= NULL
;
668 NTSTATUS Status
= STATUS_SUCCESS
;
670 BOOLEAN HasObjectName
= FALSE
;
674 PreviousMode
= KeGetPreviousMode();
676 /* Probe the paraemeters */
677 if(PreviousMode
!= KernelMode
)
681 ProbeForWriteHandle(ThreadHandle
);
685 ProbeForRead(ClientId
,
689 SafeClientId
= *ClientId
;
690 ClientId
= &SafeClientId
;
693 /* just probe the object attributes structure, don't capture it
694 completely. This is done later if necessary */
695 ProbeForRead(ObjectAttributes
,
696 sizeof(OBJECT_ATTRIBUTES
),
698 HasObjectName
= (ObjectAttributes
->ObjectName
!= NULL
);
699 Attributes
= ObjectAttributes
->Attributes
;
703 Status
= _SEH_GetExceptionCode();
707 if(!NT_SUCCESS(Status
)) return Status
;
711 HasObjectName
= (ObjectAttributes
->ObjectName
!= NULL
);
712 Attributes
= ObjectAttributes
->Attributes
;
715 if (HasObjectName
&& ClientId
!= NULL
)
717 /* can't pass both, n object name and a client id */
718 return STATUS_INVALID_PARAMETER_MIX
;
721 /* Open by name if one was given */
725 Status
= ObOpenObjectByName(ObjectAttributes
,
733 if (!NT_SUCCESS(Status
))
735 DPRINT1("Could not open object by name\n");
738 else if (ClientId
!= NULL
)
740 /* Open by Thread ID */
741 if (ClientId
->UniqueProcess
)
743 /* Get the Process */
744 DPRINT("Opening by Process ID: %x\n", ClientId
->UniqueProcess
);
745 Status
= PsLookupProcessThreadByCid(ClientId
,
751 /* Get the Process */
752 DPRINT("Opening by Thread ID: %x\n", ClientId
->UniqueThread
);
753 Status
= PsLookupThreadByThreadId(ClientId
->UniqueThread
,
757 if(!NT_SUCCESS(Status
))
759 DPRINT1("Failure to find Thread\n");
763 /* Open the Thread Object */
764 Status
= ObOpenObjectByPointer(Thread
,
771 if(!NT_SUCCESS(Status
))
773 DPRINT1("Failure to open Thread\n");
776 /* Dereference the thread */
777 ObDereferenceObject(Thread
);
781 /* neither an object name nor a client id was passed */
782 return STATUS_INVALID_PARAMETER_MIX
;
785 /* Write back the handle */
786 if(NT_SUCCESS(Status
))
790 *ThreadHandle
= hThread
;
794 Status
= _SEH_GetExceptionCode();
805 NtYieldExecution(VOID
)
807 KiDispatchThread(Ready
);
808 return(STATUS_SUCCESS
);
815 /* Check and Alert Thread if needed */
816 return KeTestAlertThread(ExGetPreviousMode()) ? STATUS_ALERTED
: STATUS_SUCCESS
;
824 ExGetPreviousMode (VOID
)
826 return (KPROCESSOR_MODE
)PsGetCurrentThread()->Tcb
.PreviousMode
;