3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/create.c
6 * PURPOSE: Thread managment
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
16 * All of the routines that manipulate the thread queue synchronize on
21 /* INCLUDES ****************************************************************/
25 #include <internal/debug.h>
27 /* GLOBAL *******************************************************************/
29 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
31 static ULONG PiThreadNotifyRoutineCount
= 0;
32 static PCREATE_THREAD_NOTIFY_ROUTINE
33 PiThreadNotifyRoutine
[MAX_THREAD_NOTIFY_ROUTINE_COUNT
];
37 KeSuspendThread(PKTHREAD Thread
);
38 /* FUNCTIONS ***************************************************************/
41 PiBeforeBeginThread(CONTEXT c
)
43 KeLowerIrql(PASSIVE_LEVEL
);
47 PsInitializeThread(PEPROCESS Process
,
49 POBJECT_ATTRIBUTES ObjectAttributes
,
50 KPROCESSOR_MODE AccessMode
,
61 Process
= PsInitialSystemProcess
;
65 * Create and initialize thread
67 Status
= ObCreateObject(AccessMode
,
76 if (!NT_SUCCESS(Status
))
84 ObReferenceObjectByPointer(Process
,
85 PROCESS_CREATE_THREAD
,
89 Thread
->ThreadsProcess
= Process
;
90 Thread
->Cid
.UniqueThread
= NULL
;
91 Thread
->Cid
.UniqueProcess
= (HANDLE
)Thread
->ThreadsProcess
->UniqueProcessId
;
93 DPRINT("Thread = %x\n",Thread
);
95 KeInitializeThread(&Process
->Pcb
, &Thread
->Tcb
, First
);
96 InitializeListHead(&Thread
->ActiveTimerListHead
);
97 KeInitializeSpinLock(&Thread
->ActiveTimerListLock
);
98 InitializeListHead(&Thread
->IrpList
);
99 Thread
->DeadThread
= FALSE
;
100 Thread
->HasTerminated
= FALSE
;
101 Thread
->Tcb
.Win32Thread
= NULL
;
102 DPRINT("Thread->Cid.UniqueThread %d\n",Thread
->Cid
.UniqueThread
);
105 Thread
->Tcb
.BasePriority
= (CHAR
)Process
->Pcb
.BasePriority
;
106 Thread
->Tcb
.Priority
= Thread
->Tcb
.BasePriority
;
109 * Local Procedure Call facility (LPC)
111 KeInitializeSemaphore (& Thread
->LpcReplySemaphore
, 0, LONG_MAX
);
112 Thread
->LpcReplyMessage
= NULL
;
113 Thread
->LpcReplyMessageId
= 0; /* not valid */
114 /* Thread->LpcReceiveMessageId = 0; */
115 Thread
->LpcExitThreadCalled
= FALSE
;
116 Thread
->LpcReceivedMsgIdValid
= FALSE
;
118 oldIrql
= KeAcquireDispatcherDatabaseLock();
119 InsertTailList(&Process
->ThreadListHead
,
120 &Thread
->ThreadListEntry
);
121 KeReleaseDispatcherDatabaseLock(oldIrql
);
125 return STATUS_SUCCESS
;
130 PsCreateTeb(HANDLE ProcessHandle
,
133 PINITIAL_TEB InitialTeb
)
147 if (NULL
== Thread
->ThreadsProcess
)
149 /* We'll be allocating a 64k block here and only use 4k of it, but this
150 path should almost never be taken. Actually, I never saw it was taken,
151 so maybe we should just ASSERT(NULL != Thread->ThreadsProcess) and
154 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
158 MEM_RESERVE
| MEM_COMMIT
| MEM_TOP_DOWN
,
160 if (! NT_SUCCESS(Status
))
162 DPRINT1("Failed to allocate virtual memory for TEB\n");
168 Process
= Thread
->ThreadsProcess
;
169 ExAcquireFastMutex(&Process
->TebLock
);
170 if (NULL
== Process
->TebBlock
||
171 Process
->TebBlock
== Process
->TebLastAllocated
)
173 Process
->TebBlock
= NULL
;
174 RegionSize
= MM_VIRTMEM_GRANULARITY
;
175 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
179 MEM_RESERVE
| MEM_TOP_DOWN
,
181 if (! NT_SUCCESS(Status
))
183 ExReleaseFastMutex(&Process
->TebLock
);
184 DPRINT1("Failed to reserve virtual memory for TEB\n");
187 Process
->TebLastAllocated
= (PVOID
) ((char *) Process
->TebBlock
+ RegionSize
);
189 TebBase
= (PVOID
) ((char *) Process
->TebLastAllocated
- PAGE_SIZE
);
190 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
196 if (! NT_SUCCESS(Status
))
198 DPRINT1("Failed to commit virtual memory for TEB\n");
201 Process
->TebLastAllocated
= TebBase
;
202 ExReleaseFastMutex(&Process
->TebLock
);
205 DPRINT ("TebBase %p TebSize %lu\n", TebBase
, TebSize
);
206 ASSERT(NULL
!= TebBase
&& PAGE_SIZE
<= TebSize
);
208 RtlZeroMemory(&Teb
, sizeof(TEB
));
209 /* set all pointers to and from the TEB */
210 Teb
.Tib
.Self
= TebBase
;
211 if (Thread
->ThreadsProcess
)
213 Teb
.Peb
= Thread
->ThreadsProcess
->Peb
; /* No PEB yet!! */
215 DPRINT("Teb.Peb %x\n", Teb
.Peb
);
217 /* store stack information from InitialTeb */
218 if(InitialTeb
!= NULL
)
220 /* fixed-size stack */
221 if(InitialTeb
->StackBase
&& InitialTeb
->StackLimit
)
223 Teb
.Tib
.StackBase
= InitialTeb
->StackBase
;
224 Teb
.Tib
.StackLimit
= InitialTeb
->StackLimit
;
225 Teb
.DeallocationStack
= InitialTeb
->StackLimit
;
227 /* expandable stack */
230 Teb
.Tib
.StackBase
= InitialTeb
->StackCommit
;
231 Teb
.Tib
.StackLimit
= InitialTeb
->StackCommitMax
;
232 Teb
.DeallocationStack
= InitialTeb
->StackReserved
;
236 /* more initialization */
237 Teb
.Cid
.UniqueThread
= Thread
->Cid
.UniqueThread
;
238 Teb
.Cid
.UniqueProcess
= Thread
->Cid
.UniqueProcess
;
239 Teb
.CurrentLocale
= PsDefaultThreadLocaleId
;
241 /* Terminate the exception handler list */
242 Teb
.Tib
.ExceptionList
= (PVOID
)-1;
244 DPRINT("sizeof(TEB) %x\n", sizeof(TEB
));
246 /* write TEB data into teb page */
247 Status
= NtWriteVirtualMemory(ProcessHandle
,
253 if (!NT_SUCCESS(Status
))
256 DPRINT1 ("Writing TEB failed!\n");
259 NtFreeVirtualMemory(ProcessHandle
,
269 *TebPtr
= (PTEB
)TebBase
;
272 DPRINT("TEB allocated at %p\n", TebBase
);
279 LdrInitApcRundownRoutine(PKAPC Apc
)
286 LdrInitApcKernelRoutine(PKAPC Apc
,
287 PKNORMAL_ROUTINE
* NormalRoutine
,
288 PVOID
* NormalContext
,
289 PVOID
* SystemArgument1
,
290 PVOID
* SystemArgument2
)
297 NtCreateThread(OUT PHANDLE ThreadHandle
,
298 IN ACCESS_MASK DesiredAccess
,
299 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
300 IN HANDLE ProcessHandle
,
301 OUT PCLIENT_ID ClientId
,
302 IN PCONTEXT ThreadContext
,
303 IN PINITIAL_TEB InitialTeb
,
304 IN BOOLEAN CreateSuspended
)
308 INITIAL_TEB SafeInitialTeb
;
314 KPROCESSOR_MODE PreviousMode
;
315 NTSTATUS Status
= STATUS_SUCCESS
;
319 if(ThreadContext
== NULL
)
321 return STATUS_INVALID_PARAMETER
;
324 PreviousMode
= ExGetPreviousMode();
326 if(PreviousMode
!= KernelMode
)
330 ProbeForWrite(ThreadHandle
,
335 ProbeForWrite(ClientId
,
339 ProbeForRead(ThreadContext
,
342 SafeContext
= *ThreadContext
;
343 ThreadContext
= &SafeContext
;
344 ProbeForRead(InitialTeb
,
347 SafeInitialTeb
= *InitialTeb
;
348 InitialTeb
= &SafeInitialTeb
;
352 Status
= _SEH_GetExceptionCode();
356 if(!NT_SUCCESS(Status
))
362 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
363 ThreadHandle
,ThreadContext
);
365 Status
= ObReferenceObjectByHandle(ProcessHandle
,
366 PROCESS_CREATE_THREAD
,
371 if(!NT_SUCCESS(Status
))
376 Status
= PsLockProcess(Process
, FALSE
);
377 if (!NT_SUCCESS(Status
))
379 ObDereferenceObject(Process
);
383 if(Process
->ExitTime
.QuadPart
!= 0)
385 PsUnlockProcess(Process
);
386 return STATUS_PROCESS_IS_TERMINATING
;
389 PsUnlockProcess(Process
);
391 Status
= PsInitializeThread(Process
,
397 ObDereferenceObject(Process
);
399 if (!NT_SUCCESS(Status
))
404 /* create a client id handle */
405 Status
= PsCreateCidHandle(Thread
, PsThreadType
, &Thread
->Cid
.UniqueThread
);
406 if (!NT_SUCCESS(Status
))
408 ObDereferenceObject(Thread
);
412 Status
= KiArchInitThreadWithContext(&Thread
->Tcb
, ThreadContext
);
413 if (!NT_SUCCESS(Status
))
415 PsDeleteCidHandle(Thread
->Cid
.UniqueThread
, PsThreadType
);
416 ObDereferenceObject(Thread
);
420 Status
= PsCreateTeb(ProcessHandle
,
424 if (!NT_SUCCESS(Status
))
426 PsDeleteCidHandle(Thread
->Cid
.UniqueThread
, PsThreadType
);
427 ObDereferenceObject(Thread
);
430 Thread
->Tcb
.Teb
= TebBase
;
432 Thread
->StartAddress
= NULL
;
435 * Maybe send a message to the process's debugger
437 DbgkCreateThread((PVOID
)ThreadContext
->Eip
);
440 * First, force the thread to be non-alertable for user-mode alerts.
442 Thread
->Tcb
.Alertable
= FALSE
;
445 * If the thread is to be created suspended then queue an APC to
446 * do the suspend before we run any userspace code.
450 KeSuspendThread(&Thread
->Tcb
);
454 * Queue an APC to the thread that will execute the ntdll startup
457 LdrInitApc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
458 KeInitializeApc(LdrInitApc
, &Thread
->Tcb
, OriginalApcEnvironment
, LdrInitApcKernelRoutine
,
459 LdrInitApcRundownRoutine
, LdrpGetSystemDllEntryPoint(),
461 KeInsertQueueApc(LdrInitApc
, NULL
, NULL
, IO_NO_INCREMENT
);
464 * The thread is non-alertable, so the APC we added did not set UserApcPending to TRUE.
465 * We must do this manually. Do NOT attempt to set the Thread to Alertable before the call,
466 * doing so is a blatant and erronous hack.
468 Thread
->Tcb
.ApcState
.UserApcPending
= TRUE
;
469 Thread
->Tcb
.Alerted
[KernelMode
] = TRUE
;
471 oldIrql
= KeAcquireDispatcherDatabaseLock ();
472 KiUnblockThread(&Thread
->Tcb
, NULL
, 0);
473 KeReleaseDispatcherDatabaseLock(oldIrql
);
475 Status
= ObInsertObject((PVOID
)Thread
,
481 if(NT_SUCCESS(Status
))
487 *ClientId
= Thread
->Cid
;
489 *ThreadHandle
= hThread
;
493 Status
= _SEH_GetExceptionCode();
506 PsCreateSystemThread(PHANDLE ThreadHandle
,
507 ACCESS_MASK DesiredAccess
,
508 POBJECT_ATTRIBUTES ObjectAttributes
,
509 HANDLE ProcessHandle
,
511 PKSTART_ROUTINE StartRoutine
,
514 * FUNCTION: Creates a thread which executes in kernel mode
516 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
518 * DesiredAccess = Requested access to the thread
519 * ObjectAttributes = Object attributes (optional)
520 * ProcessHandle = Handle of process thread will run in
521 * NULL to use system process
522 * ClientId (OUT) = Caller supplied storage for the returned client id
523 * of the thread (optional)
524 * StartRoutine = Entry point for the thread
525 * StartContext = Argument supplied to the thread when it begins
527 * RETURNS: Success or failure status
536 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
537 ThreadHandle
,ProcessHandle
);
539 Status
= PsInitializeThread(NULL
,
544 if (!NT_SUCCESS(Status
))
549 /* Set the thread as a system thread */
550 Thread
->SystemThread
= TRUE
;
552 Status
= PsCreateCidHandle(Thread
,
554 &Thread
->Cid
.UniqueThread
);
555 if(!NT_SUCCESS(Status
))
557 ObDereferenceObject(Thread
);
561 Thread
->StartAddress
= StartRoutine
;
562 Status
= KiArchInitThread(&Thread
->Tcb
, StartRoutine
, StartContext
);
563 if (!NT_SUCCESS(Status
))
565 ObDereferenceObject(Thread
);
569 if (ClientId
!= NULL
)
571 *ClientId
=Thread
->Cid
;
574 oldIrql
= KeAcquireDispatcherDatabaseLock ();
575 KiUnblockThread(&Thread
->Tcb
, NULL
, 0);
576 KeReleaseDispatcherDatabaseLock(oldIrql
);
578 Status
= ObInsertObject((PVOID
)Thread
,
585 /* don't dereference the thread, the initial reference serves as the keep-alive
586 reference which will be removed by the thread reaper */
593 PspRunCreateThreadNotifyRoutines(PETHREAD CurrentThread
,
597 CLIENT_ID Cid
= CurrentThread
->Cid
;
599 for (i
= 0; i
< PiThreadNotifyRoutineCount
; i
++)
601 PiThreadNotifyRoutine
[i
](Cid
.UniqueProcess
, Cid
.UniqueThread
, Create
);
610 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
)
612 if (PiThreadNotifyRoutineCount
>= MAX_THREAD_NOTIFY_ROUTINE_COUNT
)
614 return(STATUS_INSUFFICIENT_RESOURCES
);
617 PiThreadNotifyRoutine
[PiThreadNotifyRoutineCount
] = NotifyRoutine
;
618 PiThreadNotifyRoutineCount
++;
620 return(STATUS_SUCCESS
);