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