Thread/Process Termination/Repeaing Rewrite + Fixes
[reactos.git] / reactos / ntoskrnl / ps / create.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/create.c
6 * PURPOSE: Thread managment
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 * Phillip Susi
10 * Skywing
11 */
12
13 /*
14 * NOTE:
15 *
16 * All of the routines that manipulate the thread queue synchronize on
17 * a single spinlock
18 *
19 */
20
21 /* INCLUDES ****************************************************************/
22
23 #include <ntoskrnl.h>
24 #define NDEBUG
25 #include <internal/debug.h>
26
27 /* GLOBAL *******************************************************************/
28
29 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
30
31 static ULONG PiThreadNotifyRoutineCount = 0;
32 static PCREATE_THREAD_NOTIFY_ROUTINE
33 PiThreadNotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
34
35 ULONG
36 STDCALL
37 KeSuspendThread(PKTHREAD Thread);
38 /* FUNCTIONS ***************************************************************/
39
40 VOID
41 PiBeforeBeginThread(CONTEXT c)
42 {
43 KeLowerIrql(PASSIVE_LEVEL);
44 }
45
46 NTSTATUS
47 PsInitializeThread(PEPROCESS Process,
48 PETHREAD* ThreadPtr,
49 POBJECT_ATTRIBUTES ObjectAttributes,
50 KPROCESSOR_MODE AccessMode,
51 BOOLEAN First)
52 {
53 PETHREAD Thread;
54 NTSTATUS Status;
55 KIRQL oldIrql;
56
57 PAGED_CODE();
58
59 if (Process == NULL)
60 {
61 Process = PsInitialSystemProcess;
62 }
63
64 /*
65 * Create and initialize thread
66 */
67 Status = ObCreateObject(AccessMode,
68 PsThreadType,
69 ObjectAttributes,
70 KernelMode,
71 NULL,
72 sizeof(ETHREAD),
73 0,
74 0,
75 (PVOID*)&Thread);
76 if (!NT_SUCCESS(Status))
77 {
78 return(Status);
79 }
80
81 /*
82 * Reference process
83 */
84 ObReferenceObjectByPointer(Process,
85 PROCESS_CREATE_THREAD,
86 PsProcessType,
87 KernelMode);
88
89 Thread->ThreadsProcess = Process;
90 Thread->Cid.UniqueThread = NULL;
91 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
92
93 DPRINT("Thread = %x\n",Thread);
94
95 KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
96 InitializeListHead(&Thread->TerminationPortList);
97 InitializeListHead(&Thread->ActiveTimerListHead);
98 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
99 InitializeListHead(&Thread->IrpList);
100 Thread->DeadThread = FALSE;
101 Thread->HasTerminated = FALSE;
102 Thread->Tcb.Win32Thread = NULL;
103 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
104
105
106 Thread->Tcb.BasePriority = (CHAR)Process->Pcb.BasePriority;
107 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
108
109 /*
110 * Local Procedure Call facility (LPC)
111 */
112 KeInitializeSemaphore (& Thread->LpcReplySemaphore, 0, LONG_MAX);
113 Thread->LpcReplyMessage = NULL;
114 Thread->LpcReplyMessageId = 0; /* not valid */
115 /* Thread->LpcReceiveMessageId = 0; */
116 Thread->LpcExitThreadCalled = FALSE;
117 Thread->LpcReceivedMsgIdValid = FALSE;
118
119 oldIrql = KeAcquireDispatcherDatabaseLock();
120 InsertTailList(&Process->ThreadListHead,
121 &Thread->ThreadListEntry);
122 KeReleaseDispatcherDatabaseLock(oldIrql);
123
124 *ThreadPtr = Thread;
125
126 return STATUS_SUCCESS;
127 }
128
129
130 static NTSTATUS
131 PsCreateTeb(HANDLE ProcessHandle,
132 PTEB *TebPtr,
133 PETHREAD Thread,
134 PINITIAL_TEB InitialTeb)
135 {
136 PEPROCESS Process;
137 NTSTATUS Status;
138 ULONG ByteCount;
139 ULONG RegionSize;
140 ULONG TebSize;
141 PVOID TebBase;
142 TEB Teb;
143
144 PAGED_CODE();
145
146 TebSize = PAGE_SIZE;
147
148 if (NULL == Thread->ThreadsProcess)
149 {
150 /* We'll be allocating a 64k block here and only use 4k of it, but this
151 path should almost never be taken. Actually, I never saw it was taken,
152 so maybe we should just ASSERT(NULL != Thread->ThreadsProcess) and
153 move on */
154 TebBase = NULL;
155 Status = ZwAllocateVirtualMemory(ProcessHandle,
156 &TebBase,
157 0,
158 &TebSize,
159 MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
160 PAGE_READWRITE);
161 if (! NT_SUCCESS(Status))
162 {
163 DPRINT1("Failed to allocate virtual memory for TEB\n");
164 return Status;
165 }
166 }
167 else
168 {
169 Process = Thread->ThreadsProcess;
170 ExAcquireFastMutex(&Process->TebLock);
171 if (NULL == Process->TebBlock ||
172 Process->TebBlock == Process->TebLastAllocated)
173 {
174 Process->TebBlock = NULL;
175 RegionSize = MM_VIRTMEM_GRANULARITY;
176 Status = ZwAllocateVirtualMemory(ProcessHandle,
177 &Process->TebBlock,
178 0,
179 &RegionSize,
180 MEM_RESERVE | MEM_TOP_DOWN,
181 PAGE_READWRITE);
182 if (! NT_SUCCESS(Status))
183 {
184 ExReleaseFastMutex(&Process->TebLock);
185 DPRINT1("Failed to reserve virtual memory for TEB\n");
186 return Status;
187 }
188 Process->TebLastAllocated = (PVOID) ((char *) Process->TebBlock + RegionSize);
189 }
190 TebBase = (PVOID) ((char *) Process->TebLastAllocated - PAGE_SIZE);
191 Status = ZwAllocateVirtualMemory(ProcessHandle,
192 &TebBase,
193 0,
194 &TebSize,
195 MEM_COMMIT,
196 PAGE_READWRITE);
197 if (! NT_SUCCESS(Status))
198 {
199 DPRINT1("Failed to commit virtual memory for TEB\n");
200 return Status;
201 }
202 Process->TebLastAllocated = TebBase;
203 ExReleaseFastMutex(&Process->TebLock);
204 }
205
206 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
207 ASSERT(NULL != TebBase && PAGE_SIZE <= TebSize);
208
209 RtlZeroMemory(&Teb, sizeof(TEB));
210 /* set all pointers to and from the TEB */
211 Teb.Tib.Self = TebBase;
212 if (Thread->ThreadsProcess)
213 {
214 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
215 }
216 DPRINT("Teb.Peb %x\n", Teb.Peb);
217
218 /* store stack information from InitialTeb */
219 if(InitialTeb != NULL)
220 {
221 /* fixed-size stack */
222 if(InitialTeb->StackBase && InitialTeb->StackLimit)
223 {
224 Teb.Tib.StackBase = InitialTeb->StackBase;
225 Teb.Tib.StackLimit = InitialTeb->StackLimit;
226 Teb.DeallocationStack = InitialTeb->StackLimit;
227 }
228 /* expandable stack */
229 else
230 {
231 Teb.Tib.StackBase = InitialTeb->StackCommit;
232 Teb.Tib.StackLimit = InitialTeb->StackCommitMax;
233 Teb.DeallocationStack = InitialTeb->StackReserved;
234 }
235 }
236
237 /* more initialization */
238 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
239 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
240 Teb.CurrentLocale = PsDefaultThreadLocaleId;
241
242 /* Terminate the exception handler list */
243 Teb.Tib.ExceptionList = (PVOID)-1;
244
245 DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
246
247 /* write TEB data into teb page */
248 Status = NtWriteVirtualMemory(ProcessHandle,
249 TebBase,
250 &Teb,
251 sizeof(TEB),
252 &ByteCount);
253
254 if (!NT_SUCCESS(Status))
255 {
256 /* free TEB */
257 DPRINT1 ("Writing TEB failed!\n");
258
259 RegionSize = 0;
260 NtFreeVirtualMemory(ProcessHandle,
261 TebBase,
262 &RegionSize,
263 MEM_RELEASE);
264
265 return Status;
266 }
267
268 if (TebPtr != NULL)
269 {
270 *TebPtr = (PTEB)TebBase;
271 }
272
273 DPRINT("TEB allocated at %p\n", TebBase);
274
275 return Status;
276 }
277
278
279 VOID STDCALL
280 LdrInitApcRundownRoutine(PKAPC Apc)
281 {
282 ExFreePool(Apc);
283 }
284
285
286 VOID STDCALL
287 LdrInitApcKernelRoutine(PKAPC Apc,
288 PKNORMAL_ROUTINE* NormalRoutine,
289 PVOID* NormalContext,
290 PVOID* SystemArgument1,
291 PVOID* SystemArgument2)
292 {
293 ExFreePool(Apc);
294 }
295
296
297 NTSTATUS STDCALL
298 NtCreateThread(OUT PHANDLE ThreadHandle,
299 IN ACCESS_MASK DesiredAccess,
300 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
301 IN HANDLE ProcessHandle,
302 OUT PCLIENT_ID ClientId,
303 IN PCONTEXT ThreadContext,
304 IN PINITIAL_TEB InitialTeb,
305 IN BOOLEAN CreateSuspended)
306 {
307 HANDLE hThread;
308 CONTEXT SafeContext;
309 INITIAL_TEB SafeInitialTeb;
310 PEPROCESS Process;
311 PETHREAD Thread;
312 PTEB TebBase;
313 PKAPC LdrInitApc;
314 KIRQL oldIrql;
315 KPROCESSOR_MODE PreviousMode;
316 NTSTATUS Status = STATUS_SUCCESS;
317
318 PAGED_CODE();
319
320 if(ThreadContext == NULL)
321 {
322 return STATUS_INVALID_PARAMETER;
323 }
324
325 PreviousMode = ExGetPreviousMode();
326
327 if(PreviousMode != KernelMode)
328 {
329 _SEH_TRY
330 {
331 ProbeForWrite(ThreadHandle,
332 sizeof(HANDLE),
333 sizeof(ULONG));
334 if(ClientId != NULL)
335 {
336 ProbeForWrite(ClientId,
337 sizeof(CLIENT_ID),
338 sizeof(ULONG));
339 }
340 ProbeForRead(ThreadContext,
341 sizeof(CONTEXT),
342 sizeof(ULONG));
343 SafeContext = *ThreadContext;
344 ThreadContext = &SafeContext;
345 ProbeForRead(InitialTeb,
346 sizeof(INITIAL_TEB),
347 sizeof(ULONG));
348 SafeInitialTeb = *InitialTeb;
349 InitialTeb = &SafeInitialTeb;
350 }
351 _SEH_HANDLE
352 {
353 Status = _SEH_GetExceptionCode();
354 }
355 _SEH_END;
356
357 if(!NT_SUCCESS(Status))
358 {
359 return Status;
360 }
361 }
362
363 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
364 ThreadHandle,ThreadContext);
365
366 Status = ObReferenceObjectByHandle(ProcessHandle,
367 PROCESS_CREATE_THREAD,
368 PsProcessType,
369 PreviousMode,
370 (PVOID*)&Process,
371 NULL);
372 if(!NT_SUCCESS(Status))
373 {
374 return(Status);
375 }
376
377 Status = PsInitializeThread(Process,
378 &Thread,
379 ObjectAttributes,
380 PreviousMode,
381 FALSE);
382
383 ObDereferenceObject(Process);
384
385 if (!NT_SUCCESS(Status))
386 {
387 return(Status);
388 }
389
390 /* create a client id handle */
391 Status = PsCreateCidHandle(Thread, PsThreadType, &Thread->Cid.UniqueThread);
392 if (!NT_SUCCESS(Status))
393 {
394 ObDereferenceObject(Thread);
395 return Status;
396 }
397
398 Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
399 if (!NT_SUCCESS(Status))
400 {
401 PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType);
402 ObDereferenceObject(Thread);
403 return(Status);
404 }
405
406 Status = PsCreateTeb(ProcessHandle,
407 &TebBase,
408 Thread,
409 InitialTeb);
410 if (!NT_SUCCESS(Status))
411 {
412 PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType);
413 ObDereferenceObject(Thread);
414 return(Status);
415 }
416 Thread->Tcb.Teb = TebBase;
417
418 Thread->StartAddress = NULL;
419
420 /*
421 * Maybe send a message to the process's debugger
422 */
423 DbgkCreateThread((PVOID)ThreadContext->Eip);
424
425 /*
426 * First, force the thread to be non-alertable for user-mode alerts.
427 */
428 Thread->Tcb.Alertable = FALSE;
429
430 /*
431 * If the thread is to be created suspended then queue an APC to
432 * do the suspend before we run any userspace code.
433 */
434 if (CreateSuspended)
435 {
436 KeSuspendThread(&Thread->Tcb);
437 }
438
439 /*
440 * Queue an APC to the thread that will execute the ntdll startup
441 * routine.
442 */
443 LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
444 KeInitializeApc(LdrInitApc, &Thread->Tcb, OriginalApcEnvironment, LdrInitApcKernelRoutine,
445 LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(),
446 UserMode, NULL);
447 KeInsertQueueApc(LdrInitApc, NULL, NULL, IO_NO_INCREMENT);
448
449 /*
450 * The thread is non-alertable, so the APC we added did not set UserApcPending to TRUE.
451 * We must do this manually. Do NOT attempt to set the Thread to Alertable before the call,
452 * doing so is a blatant and erronous hack.
453 */
454 Thread->Tcb.ApcState.UserApcPending = TRUE;
455 Thread->Tcb.Alerted[KernelMode] = TRUE;
456
457 oldIrql = KeAcquireDispatcherDatabaseLock ();
458 KiUnblockThread(&Thread->Tcb, NULL, 0);
459 KeReleaseDispatcherDatabaseLock(oldIrql);
460
461 Status = ObInsertObject((PVOID)Thread,
462 NULL,
463 DesiredAccess,
464 0,
465 NULL,
466 &hThread);
467 if(NT_SUCCESS(Status))
468 {
469 _SEH_TRY
470 {
471 if(ClientId != NULL)
472 {
473 *ClientId = Thread->Cid;
474 }
475 *ThreadHandle = hThread;
476 }
477 _SEH_HANDLE
478 {
479 Status = _SEH_GetExceptionCode();
480 }
481 _SEH_END;
482 }
483
484 return Status;
485 }
486
487
488 /*
489 * @implemented
490 */
491 NTSTATUS STDCALL
492 PsCreateSystemThread(PHANDLE ThreadHandle,
493 ACCESS_MASK DesiredAccess,
494 POBJECT_ATTRIBUTES ObjectAttributes,
495 HANDLE ProcessHandle,
496 PCLIENT_ID ClientId,
497 PKSTART_ROUTINE StartRoutine,
498 PVOID StartContext)
499 /*
500 * FUNCTION: Creates a thread which executes in kernel mode
501 * ARGUMENTS:
502 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
503 * handle
504 * DesiredAccess = Requested access to the thread
505 * ObjectAttributes = Object attributes (optional)
506 * ProcessHandle = Handle of process thread will run in
507 * NULL to use system process
508 * ClientId (OUT) = Caller supplied storage for the returned client id
509 * of the thread (optional)
510 * StartRoutine = Entry point for the thread
511 * StartContext = Argument supplied to the thread when it begins
512 * execution
513 * RETURNS: Success or failure status
514 */
515 {
516 PETHREAD Thread;
517 NTSTATUS Status;
518 KIRQL oldIrql;
519
520 PAGED_CODE();
521
522 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
523 ThreadHandle,ProcessHandle);
524
525 Status = PsInitializeThread(NULL,
526 &Thread,
527 ObjectAttributes,
528 KernelMode,
529 FALSE);
530 if (!NT_SUCCESS(Status))
531 {
532 return(Status);
533 }
534
535 /* Set the thread as a system thread */
536 Thread->SystemThread = TRUE;
537
538 Status = PsCreateCidHandle(Thread,
539 PsThreadType,
540 &Thread->Cid.UniqueThread);
541 if(!NT_SUCCESS(Status))
542 {
543 ObDereferenceObject(Thread);
544 return Status;
545 }
546
547 Thread->StartAddress = StartRoutine;
548 Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
549 if (!NT_SUCCESS(Status))
550 {
551 ObDereferenceObject(Thread);
552 return(Status);
553 }
554
555 if (ClientId != NULL)
556 {
557 *ClientId=Thread->Cid;
558 }
559
560 oldIrql = KeAcquireDispatcherDatabaseLock ();
561 KiUnblockThread(&Thread->Tcb, NULL, 0);
562 KeReleaseDispatcherDatabaseLock(oldIrql);
563
564 Status = ObInsertObject((PVOID)Thread,
565 NULL,
566 DesiredAccess,
567 0,
568 NULL,
569 ThreadHandle);
570
571 /* don't dereference the thread, the initial reference serves as the keep-alive
572 reference which will be removed by the thread reaper */
573
574 return Status;
575 }
576
577
578 VOID STDCALL
579 PspRunCreateThreadNotifyRoutines(PETHREAD CurrentThread,
580 BOOLEAN Create)
581 {
582 ULONG i;
583 CLIENT_ID Cid = CurrentThread->Cid;
584
585 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
586 {
587 PiThreadNotifyRoutine[i](Cid.UniqueProcess, Cid.UniqueThread, Create);
588 }
589 }
590
591
592 /*
593 * @implemented
594 */
595 NTSTATUS STDCALL
596 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
597 {
598 if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
599 {
600 return(STATUS_INSUFFICIENT_RESOURCES);
601 }
602
603 PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
604 PiThreadNotifyRoutineCount++;
605
606 return(STATUS_SUCCESS);
607 }
608
609 /* EOF */