4945b92f01f152357d80f2a67a7bd99791e12678
[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 PAGED_CODE();
341
342 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
343 ProcessHandle, ExitStatus);
344
345 Status = ObReferenceObjectByHandle(ProcessHandle,
346 PROCESS_TERMINATE,
347 PsProcessType,
348 KeGetCurrentThread()->PreviousMode,
349 (PVOID*)&Process,
350 NULL);
351 if (!NT_SUCCESS(Status))
352 {
353 return(Status);
354 }
355 Process->ExitStatus = ExitStatus;
356 PiTerminateProcessThreads(Process, ExitStatus);
357 if (PsGetCurrentThread()->ThreadsProcess == Process)
358 {
359 ObDereferenceObject(Process);
360 PsTerminateCurrentThread(ExitStatus);
361 /*
362 * We should never get here!
363 */
364 return(STATUS_SUCCESS);
365 }
366 ObDereferenceObject(Process);
367 return(STATUS_SUCCESS);
368 }
369
370
371 NTSTATUS STDCALL
372 NtTerminateThread(IN HANDLE ThreadHandle,
373 IN NTSTATUS ExitStatus)
374 {
375 PETHREAD Thread;
376 NTSTATUS Status;
377
378 PAGED_CODE();
379
380 Status = ObReferenceObjectByHandle(ThreadHandle,
381 THREAD_TERMINATE,
382 PsThreadType,
383 KeGetCurrentThread()->PreviousMode,
384 (PVOID*)&Thread,
385 NULL);
386 if (Status != STATUS_SUCCESS)
387 {
388 return(Status);
389 }
390
391 if (Thread == PsGetCurrentThread())
392 {
393 /* dereference the thread object before we kill our thread */
394 ObDereferenceObject(Thread);
395 PsTerminateCurrentThread(ExitStatus);
396 /*
397 * We should never get here!
398 */
399 }
400 else
401 {
402 PsTerminateOtherThread(Thread, ExitStatus);
403 ObDereferenceObject(Thread);
404 }
405 return(STATUS_SUCCESS);
406 }
407
408
409 /*
410 * @implemented
411 */
412 NTSTATUS STDCALL
413 PsTerminateSystemThread(NTSTATUS ExitStatus)
414 /*
415 * FUNCTION: Terminates the current thread
416 * ARGUMENTS:
417 * ExitStatus = Status to pass to the creater
418 * RETURNS: Doesn't
419 */
420 {
421 PsTerminateCurrentThread(ExitStatus);
422 return(STATUS_SUCCESS);
423 }
424
425 NTSTATUS STDCALL
426 NtCallTerminatePorts(PETHREAD Thread)
427 {
428 KIRQL oldIrql;
429 PLIST_ENTRY current_entry;
430 PEPORT_TERMINATION_REQUEST current;
431
432 PAGED_CODE();
433
434 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
435 while ((current_entry = RemoveHeadList(&Thread->TerminationPortList)) !=
436 &Thread->TerminationPortList);
437 {
438 current = CONTAINING_RECORD(current_entry,
439 EPORT_TERMINATION_REQUEST,
440 ThreadListEntry);
441 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
442 LpcSendTerminationPort(current->Port,
443 Thread->CreateTime);
444 ExFreePool(current);
445 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
446 }
447 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
448 return(STATUS_SUCCESS);
449 }
450
451 NTSTATUS STDCALL
452 NtRegisterThreadTerminatePort(HANDLE PortHandle)
453 {
454 NTSTATUS Status;
455 PEPORT_TERMINATION_REQUEST Request;
456 PEPORT TerminationPort;
457 KIRQL oldIrql;
458 PETHREAD Thread;
459
460 PAGED_CODE();
461
462 Status = ObReferenceObjectByHandle(PortHandle,
463 PORT_ALL_ACCESS,
464 LpcPortObjectType,
465 KeGetCurrentThread()->PreviousMode,
466 (PVOID*)&TerminationPort,
467 NULL);
468 if (!NT_SUCCESS(Status))
469 {
470 return(Status);
471 }
472
473 Request = ExAllocatePool(NonPagedPool, sizeof(EPORT_TERMINATION_REQUEST));
474 if(Request != NULL)
475 {
476 Request->Port = TerminationPort;
477 Thread = PsGetCurrentThread();
478 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
479 InsertTailList(&Thread->TerminationPortList, &Request->ThreadListEntry);
480 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
481
482 return(STATUS_SUCCESS);
483 }
484 else
485 {
486 ObDereferenceObject(TerminationPort);
487 return(STATUS_INSUFFICIENT_RESOURCES);
488 }
489 }