Standardize comment headers. Patch by Trevor McCort
[reactos.git] / reactos / ntoskrnl / ps / kill.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/kill.c
6 * PURPOSE: Terminating a thread
7 *
8 * PROGRAMMERS: David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
20 NTSTATUS STDCALL NtCallTerminatePorts(PETHREAD Thread);
21
22 #define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C')
23
24 LIST_ENTRY ThreadsToReapHead;
25
26 /* FUNCTIONS *****************************************************************/
27
28 VOID
29 PsInitializeThreadReaper(VOID)
30 {
31 InitializeListHead(&ThreadsToReapHead);
32 }
33
34 VOID
35 PsReapThreads(VOID)
36 {
37 KIRQL oldlvl;
38 PETHREAD Thread;
39 PLIST_ENTRY ListEntry;
40
41 oldlvl = KeAcquireDispatcherDatabaseLock();
42 while((ListEntry = RemoveHeadList(&ThreadsToReapHead)) != &ThreadsToReapHead)
43 {
44 PiNrThreadsAwaitingReaping--;
45 KeReleaseDispatcherDatabaseLock(oldlvl);
46 Thread = CONTAINING_RECORD(ListEntry, ETHREAD, TerminationPortList);
47
48 ObDereferenceObject(Thread);
49 oldlvl = KeAcquireDispatcherDatabaseLock();
50 }
51 KeReleaseDispatcherDatabaseLock(oldlvl);
52 }
53
54 VOID
55 PsQueueThreadReap(PETHREAD Thread)
56 {
57 InsertTailList(&ThreadsToReapHead, &Thread->TerminationPortList);
58 PiNrThreadsAwaitingReaping++;
59 }
60
61 VOID
62 PiTerminateProcessThreads(PEPROCESS Process,
63 NTSTATUS ExitStatus)
64 {
65 KIRQL oldlvl;
66 PLIST_ENTRY current_entry;
67 PETHREAD current, CurrentThread = PsGetCurrentThread();
68
69 DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n",
70 Process, ExitStatus);
71
72 oldlvl = KeAcquireDispatcherDatabaseLock();
73
74 current_entry = Process->ThreadListHead.Flink;
75 while (current_entry != &Process->ThreadListHead)
76 {
77 current = CONTAINING_RECORD(current_entry, ETHREAD,
78 ThreadListEntry);
79 if (current != CurrentThread && current->HasTerminated == 0)
80 {
81 DPRINT("Terminating %x, current thread: %x, "
82 "thread's process: %x\n", current, PsGetCurrentThread(),
83 current->ThreadsProcess);
84 KeReleaseDispatcherDatabaseLock(oldlvl);
85 PsTerminateOtherThread(current, ExitStatus);
86 oldlvl = KeAcquireDispatcherDatabaseLock();
87 current_entry = Process->ThreadListHead.Flink;
88 }
89 else
90 {
91 current_entry = current_entry->Flink;
92 }
93 }
94 KeReleaseDispatcherDatabaseLock(oldlvl);
95 DPRINT("Finished PiTerminateProcessThreads()\n");
96 }
97
98 VOID
99 PsTerminateCurrentThread(NTSTATUS ExitStatus)
100 /*
101 * FUNCTION: Terminates the current thread
102 */
103 {
104 KIRQL oldIrql;
105 PETHREAD CurrentThread;
106 PLIST_ENTRY current_entry;
107 PKMUTANT Mutant;
108 BOOLEAN Last;
109 PEPROCESS CurrentProcess;
110 SIZE_T Length = PAGE_SIZE;
111 PVOID TebBlock;
112
113 KeLowerIrql(PASSIVE_LEVEL);
114
115 CurrentThread = PsGetCurrentThread();
116 CurrentProcess = CurrentThread->ThreadsProcess;
117
118 /* Can't terminate a thread if it attached another process */
119 if (AttachedApcEnvironment == CurrentThread->Tcb.ApcStateIndex)
120 {
121 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG) CurrentProcess,
122 (ULONG) CurrentThread->Tcb.ApcState.Process,
123 (ULONG) CurrentThread->Tcb.ApcStateIndex,
124 (ULONG) CurrentThread);
125 }
126
127 KeCancelTimer(&CurrentThread->Tcb.Timer);
128
129 oldIrql = KeAcquireDispatcherDatabaseLock();
130
131 DPRINT("terminating %x\n",CurrentThread);
132
133 CurrentThread->HasTerminated = TRUE;
134 CurrentThread->ExitStatus = ExitStatus;
135 KeQuerySystemTime((PLARGE_INTEGER)&CurrentThread->ExitTime);
136
137 /* If the ProcessoR Control Block's NpxThread points to the current thread
138 * unset it.
139 */
140 InterlockedCompareExchangePointer(&KeGetCurrentKPCR()->PrcbData.NpxThread,
141 NULL, ETHREAD_TO_KTHREAD(CurrentThread));
142
143 KeReleaseDispatcherDatabaseLock(oldIrql);
144
145 PsLockProcess(CurrentProcess, FALSE);
146
147 /* Remove the thread from the thread list of its process */
148 RemoveEntryList(&CurrentThread->ThreadListEntry);
149 Last = IsListEmpty(&CurrentProcess->ThreadListHead);
150 PsUnlockProcess(CurrentProcess);
151
152 /* Notify subsystems of the thread termination */
153 PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
154 PsTerminateWin32Thread(CurrentThread);
155
156 /* Free the TEB */
157 if(CurrentThread->Tcb.Teb)
158 {
159 DPRINT("Decommit teb at %p\n", CurrentThread->Tcb.Teb);
160 ExAcquireFastMutex(&CurrentProcess->TebLock);
161 TebBlock = MM_ROUND_DOWN(CurrentThread->Tcb.Teb, MM_VIRTMEM_GRANULARITY);
162 ZwFreeVirtualMemory(NtCurrentProcess(),
163 (PVOID *)&CurrentThread->Tcb.Teb,
164 &Length,
165 MEM_DECOMMIT);
166 DPRINT("teb %p, TebBlock %p\n", CurrentThread->Tcb.Teb, TebBlock);
167 if (TebBlock != CurrentProcess->TebBlock ||
168 CurrentProcess->TebBlock == CurrentProcess->TebLastAllocated)
169 {
170 MmLockAddressSpace(&CurrentProcess->AddressSpace);
171 MmReleaseMemoryAreaIfDecommitted(CurrentProcess, &CurrentProcess->AddressSpace, TebBlock);
172 MmUnlockAddressSpace(&CurrentProcess->AddressSpace);
173 }
174 CurrentThread->Tcb.Teb = NULL;
175 ExReleaseFastMutex(&CurrentProcess->TebLock);
176 }
177
178 /* abandon all owned mutants */
179 current_entry = CurrentThread->Tcb.MutantListHead.Flink;
180 while (current_entry != &CurrentThread->Tcb.MutantListHead)
181 {
182 Mutant = CONTAINING_RECORD(current_entry, KMUTANT,
183 MutantListEntry);
184 KeReleaseMutant(Mutant,
185 MUTANT_INCREMENT,
186 TRUE,
187 FALSE);
188 current_entry = CurrentThread->Tcb.MutantListHead.Flink;
189 }
190
191 oldIrql = KeAcquireDispatcherDatabaseLock();
192 CurrentThread->Tcb.DispatcherHeader.SignalState = TRUE;
193 KiDispatcherObjectWake(&CurrentThread->Tcb.DispatcherHeader, IO_NO_INCREMENT);
194 KeReleaseDispatcherDatabaseLock (oldIrql);
195
196 /* The last thread shall close the door on exit */
197 if(Last)
198 {
199 /* save the last thread exit status */
200 CurrentProcess->LastThreadExitStatus = ExitStatus;
201
202 PspRunCreateProcessNotifyRoutines(CurrentProcess, FALSE);
203 PsTerminateWin32Process(CurrentProcess);
204 PiTerminateProcess(CurrentProcess, ExitStatus);
205 }
206
207 oldIrql = KeAcquireDispatcherDatabaseLock();
208
209 #ifdef _ENABLE_THRDEVTPAIR
210 ExpSwapThreadEventPair(CurrentThread, NULL); /* Release the associated eventpair object, if there was one */
211 #endif /* _ENABLE_THRDEVTPAIR */
212
213 ASSERT(CurrentThread->Tcb.WaitBlockList == NULL);
214
215 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1);
216 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
217 KEBUGCHECK(0);
218 }
219
220 VOID STDCALL
221 PiTerminateThreadRundownRoutine(PKAPC Apc)
222 {
223 ExFreePool(Apc);
224 }
225
226 VOID STDCALL
227 PiTerminateThreadKernelRoutine(PKAPC Apc,
228 PKNORMAL_ROUTINE* NormalRoutine,
229 PVOID* NormalContext,
230 PVOID* SystemArgument1,
231 PVOID* SystemArguemnt2)
232 {
233 ExFreePool(Apc);
234 }
235
236 VOID STDCALL
237 PiTerminateThreadNormalRoutine(PVOID NormalContext,
238 PVOID SystemArgument1,
239 PVOID SystemArgument2)
240 {
241 PETHREAD EThread = PsGetCurrentThread();
242 if (EThread->HasTerminated)
243 {
244 /* Someone else has already called PsTerminateCurrentThread */
245 return;
246 }
247 PsTerminateCurrentThread(PsGetCurrentThread()->ExitStatus);
248 }
249
250 VOID
251 PsTerminateOtherThread(PETHREAD Thread,
252 NTSTATUS ExitStatus)
253 /*
254 * FUNCTION: Terminate a thread when calling from another thread's context
255 * NOTES: This function must be called with PiThreadLock held
256 */
257 {
258 PKAPC Apc;
259 KIRQL OldIrql;
260
261 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
262 Thread, ExitStatus);
263
264 OldIrql = KeAcquireDispatcherDatabaseLock();
265 if (Thread->HasTerminated)
266 {
267 KeReleaseDispatcherDatabaseLock (OldIrql);
268 return;
269 }
270 Thread->HasTerminated = TRUE;
271 KeReleaseDispatcherDatabaseLock (OldIrql);
272 Thread->ExitStatus = ExitStatus;
273 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
274 KeInitializeApc(Apc,
275 &Thread->Tcb,
276 OriginalApcEnvironment,
277 PiTerminateThreadKernelRoutine,
278 PiTerminateThreadRundownRoutine,
279 PiTerminateThreadNormalRoutine,
280 KernelMode,
281 NULL);
282 KeInsertQueueApc(Apc,
283 NULL,
284 NULL,
285 IO_NO_INCREMENT);
286
287 OldIrql = KeAcquireDispatcherDatabaseLock();
288 if (THREAD_STATE_BLOCKED == Thread->Tcb.State && UserMode == Thread->Tcb.WaitMode)
289 {
290 DPRINT("Unblocking thread\n");
291 KiAbortWaitThread((PKTHREAD)Thread, STATUS_THREAD_IS_TERMINATING);
292 }
293 KeReleaseDispatcherDatabaseLock(OldIrql);
294 }
295
296 NTSTATUS STDCALL
297 PiTerminateProcess(PEPROCESS Process,
298 NTSTATUS ExitStatus)
299 {
300 KIRQL OldIrql;
301 PEPROCESS CurrentProcess;
302
303 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
304 Process, ExitStatus, ObGetObjectPointerCount(Process),
305 ObGetObjectHandleCount(Process));
306
307 ObReferenceObject(Process);
308 if (InterlockedExchangeUL(&Process->Pcb.State,
309 PROCESS_STATE_TERMINATED) ==
310 PROCESS_STATE_TERMINATED)
311 {
312 ObDereferenceObject(Process);
313 return(STATUS_SUCCESS);
314 }
315 CurrentProcess = PsGetCurrentProcess();
316 if (Process != CurrentProcess)
317 {
318 KeAttachProcess(&Process->Pcb);
319 }
320 ObCloseAllHandles(Process);
321 if (Process != CurrentProcess)
322 {
323 KeDetachProcess();
324 }
325 OldIrql = KeAcquireDispatcherDatabaseLock ();
326 Process->Pcb.DispatcherHeader.SignalState = TRUE;
327 KiDispatcherObjectWake(&Process->Pcb.DispatcherHeader, IO_NO_INCREMENT);
328 KeReleaseDispatcherDatabaseLock (OldIrql);
329 ObDereferenceObject(Process);
330 return(STATUS_SUCCESS);
331 }
332
333 NTSTATUS STDCALL
334 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
335 IN NTSTATUS ExitStatus)
336 {
337 NTSTATUS Status;
338 PEPROCESS Process;
339
340 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
341 ProcessHandle, ExitStatus);
342
343 Status = ObReferenceObjectByHandle(ProcessHandle,
344 PROCESS_TERMINATE,
345 PsProcessType,
346 KeGetCurrentThread()->PreviousMode,
347 (PVOID*)&Process,
348 NULL);
349 if (!NT_SUCCESS(Status))
350 {
351 return(Status);
352 }
353 Process->ExitStatus = ExitStatus;
354 PiTerminateProcessThreads(Process, ExitStatus);
355 if (PsGetCurrentThread()->ThreadsProcess == Process)
356 {
357 ObDereferenceObject(Process);
358 PsTerminateCurrentThread(ExitStatus);
359 /*
360 * We should never get here!
361 */
362 return(STATUS_SUCCESS);
363 }
364 ObDereferenceObject(Process);
365 return(STATUS_SUCCESS);
366 }
367
368
369 NTSTATUS STDCALL
370 NtTerminateThread(IN HANDLE ThreadHandle,
371 IN NTSTATUS ExitStatus)
372 {
373 PETHREAD Thread;
374 NTSTATUS Status;
375
376 Status = ObReferenceObjectByHandle(ThreadHandle,
377 THREAD_TERMINATE,
378 PsThreadType,
379 KeGetCurrentThread()->PreviousMode,
380 (PVOID*)&Thread,
381 NULL);
382 if (Status != STATUS_SUCCESS)
383 {
384 return(Status);
385 }
386
387 if (Thread == PsGetCurrentThread())
388 {
389 /* dereference the thread object before we kill our thread */
390 ObDereferenceObject(Thread);
391 PsTerminateCurrentThread(ExitStatus);
392 /*
393 * We should never get here!
394 */
395 }
396 else
397 {
398 PsTerminateOtherThread(Thread, ExitStatus);
399 ObDereferenceObject(Thread);
400 }
401 return(STATUS_SUCCESS);
402 }
403
404
405 /*
406 * @implemented
407 */
408 NTSTATUS STDCALL
409 PsTerminateSystemThread(NTSTATUS ExitStatus)
410 /*
411 * FUNCTION: Terminates the current thread
412 * ARGUMENTS:
413 * ExitStatus = Status to pass to the creater
414 * RETURNS: Doesn't
415 */
416 {
417 PsTerminateCurrentThread(ExitStatus);
418 return(STATUS_SUCCESS);
419 }
420
421 NTSTATUS STDCALL
422 NtCallTerminatePorts(PETHREAD Thread)
423 {
424 KIRQL oldIrql;
425 PLIST_ENTRY current_entry;
426 PEPORT_TERMINATION_REQUEST current;
427
428 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
429 while ((current_entry = RemoveHeadList(&Thread->TerminationPortList)) !=
430 &Thread->TerminationPortList);
431 {
432 current = CONTAINING_RECORD(current_entry,
433 EPORT_TERMINATION_REQUEST,
434 ThreadListEntry);
435 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
436 LpcSendTerminationPort(current->Port,
437 Thread->CreateTime);
438 ExFreePool(current);
439 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
440 }
441 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
442 return(STATUS_SUCCESS);
443 }
444
445 NTSTATUS STDCALL
446 NtRegisterThreadTerminatePort(HANDLE PortHandle)
447 {
448 NTSTATUS Status;
449 PEPORT_TERMINATION_REQUEST Request;
450 PEPORT TerminationPort;
451 KIRQL oldIrql;
452 PETHREAD Thread;
453
454 Status = ObReferenceObjectByHandle(PortHandle,
455 PORT_ALL_ACCESS,
456 LpcPortObjectType,
457 KeGetCurrentThread()->PreviousMode,
458 (PVOID*)&TerminationPort,
459 NULL);
460 if (!NT_SUCCESS(Status))
461 {
462 return(Status);
463 }
464
465 Request = ExAllocatePool(NonPagedPool, sizeof(EPORT_TERMINATION_REQUEST));
466 if(Request != NULL)
467 {
468 Request->Port = TerminationPort;
469 Thread = PsGetCurrentThread();
470 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
471 InsertTailList(&Thread->TerminationPortList, &Request->ThreadListEntry);
472 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
473
474 return(STATUS_SUCCESS);
475 }
476 else
477 {
478 ObDereferenceObject(TerminationPort);
479 return(STATUS_INSUFFICIENT_RESOURCES);
480 }
481 }