Merge 13511:13830 from trunk
[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(&KeGetCurrentKPCR()->PrcbData.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 KiDispatcherObjectWake(&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 PiTerminateProcess(CurrentProcess, ExitStatus);
222 }
223
224 oldIrql = KeAcquireDispatcherDatabaseLock();
225
226 #ifdef _ENABLE_THRDEVTPAIR
227 ExpSwapThreadEventPair(CurrentThread, NULL); /* Release the associated eventpair object, if there was one */
228 #endif /* _ENABLE_THRDEVTPAIR */
229
230 ASSERT(CurrentThread->Tcb.WaitBlockList == NULL);
231
232 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1);
233 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
234 KEBUGCHECK(0);
235 }
236
237 VOID STDCALL
238 PiTerminateThreadRundownRoutine(PKAPC Apc)
239 {
240 ExFreePool(Apc);
241 }
242
243 VOID STDCALL
244 PiTerminateThreadKernelRoutine(PKAPC Apc,
245 PKNORMAL_ROUTINE* NormalRoutine,
246 PVOID* NormalContext,
247 PVOID* SystemArgument1,
248 PVOID* SystemArguemnt2)
249 {
250 ExFreePool(Apc);
251 }
252
253 VOID STDCALL
254 PiTerminateThreadNormalRoutine(PVOID NormalContext,
255 PVOID SystemArgument1,
256 PVOID SystemArgument2)
257 {
258 PsTerminateCurrentThread((NTSTATUS)SystemArgument1);
259 }
260
261 VOID
262 PsTerminateOtherThread(PETHREAD Thread,
263 NTSTATUS ExitStatus)
264 /*
265 * FUNCTION: Terminate a thread when calling from another thread's context
266 * NOTES: This function must be called with PiThreadLock held
267 */
268 {
269 PKAPC Apc;
270 KIRQL OldIrql;
271
272 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
273 Thread, ExitStatus);
274
275 OldIrql = KeAcquireDispatcherDatabaseLock();
276 if (Thread->HasTerminated & TERMINATE_APC)
277 {
278 KeReleaseDispatcherDatabaseLock (OldIrql);
279 return;
280 }
281 Thread->HasTerminated |= TERMINATE_APC;
282 KeReleaseDispatcherDatabaseLock (OldIrql);
283 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
284 KeInitializeApc(Apc,
285 &Thread->Tcb,
286 OriginalApcEnvironment,
287 PiTerminateThreadKernelRoutine,
288 PiTerminateThreadRundownRoutine,
289 PiTerminateThreadNormalRoutine,
290 KernelMode,
291 NULL);
292 KeInsertQueueApc(Apc,
293 (PVOID)ExitStatus,
294 NULL,
295 IO_NO_INCREMENT);
296
297 OldIrql = KeAcquireDispatcherDatabaseLock();
298 if (THREAD_STATE_BLOCKED == Thread->Tcb.State && UserMode == Thread->Tcb.WaitMode)
299 {
300 DPRINT("Unblocking thread\n");
301 KiAbortWaitThread((PKTHREAD)Thread, STATUS_THREAD_IS_TERMINATING);
302 }
303 KeReleaseDispatcherDatabaseLock(OldIrql);
304 }
305
306 NTSTATUS STDCALL
307 PiTerminateProcess(PEPROCESS Process,
308 NTSTATUS ExitStatus)
309 {
310 KIRQL OldIrql;
311 PEPROCESS CurrentProcess;
312
313 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
314 Process, ExitStatus, ObGetObjectPointerCount(Process),
315 ObGetObjectHandleCount(Process));
316
317 ObReferenceObject(Process);
318 if (InterlockedExchangeUL(&Process->Pcb.State,
319 PROCESS_STATE_TERMINATED) ==
320 PROCESS_STATE_TERMINATED)
321 {
322 ObDereferenceObject(Process);
323 return(STATUS_SUCCESS);
324 }
325 CurrentProcess = PsGetCurrentProcess();
326 if (Process != CurrentProcess)
327 {
328 KeAttachProcess(&Process->Pcb);
329 }
330 ObCloseAllHandles(Process);
331 if (Process != CurrentProcess)
332 {
333 KeDetachProcess();
334 }
335 OldIrql = KeAcquireDispatcherDatabaseLock ();
336 Process->Pcb.DispatcherHeader.SignalState = TRUE;
337 KiDispatcherObjectWake(&Process->Pcb.DispatcherHeader, IO_NO_INCREMENT);
338 KeReleaseDispatcherDatabaseLock (OldIrql);
339 ObDereferenceObject(Process);
340 return(STATUS_SUCCESS);
341 }
342
343 NTSTATUS STDCALL
344 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
345 IN NTSTATUS ExitStatus)
346 {
347 NTSTATUS Status;
348 PEPROCESS Process;
349
350 PAGED_CODE();
351
352 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
353 ProcessHandle, ExitStatus);
354
355 Status = ObReferenceObjectByHandle(ProcessHandle,
356 PROCESS_TERMINATE,
357 PsProcessType,
358 KeGetCurrentThread()->PreviousMode,
359 (PVOID*)&Process,
360 NULL);
361 if (!NT_SUCCESS(Status))
362 {
363 return(Status);
364 }
365 Process->ExitStatus = ExitStatus;
366 PiTerminateProcessThreads(Process, ExitStatus);
367 if (PsGetCurrentThread()->ThreadsProcess == Process)
368 {
369 ObDereferenceObject(Process);
370 PsTerminateCurrentThread(ExitStatus);
371 /*
372 * We should never get here!
373 */
374 return(STATUS_SUCCESS);
375 }
376 ObDereferenceObject(Process);
377 return(STATUS_SUCCESS);
378 }
379
380
381 NTSTATUS STDCALL
382 NtTerminateThread(IN HANDLE ThreadHandle,
383 IN NTSTATUS ExitStatus)
384 {
385 PETHREAD Thread;
386 NTSTATUS Status;
387
388 PAGED_CODE();
389
390 Status = ObReferenceObjectByHandle(ThreadHandle,
391 THREAD_TERMINATE,
392 PsThreadType,
393 KeGetCurrentThread()->PreviousMode,
394 (PVOID*)&Thread,
395 NULL);
396 if (Status != STATUS_SUCCESS)
397 {
398 return(Status);
399 }
400
401 if (Thread == PsGetCurrentThread())
402 {
403 /* dereference the thread object before we kill our thread */
404 ObDereferenceObject(Thread);
405 PsTerminateCurrentThread(ExitStatus);
406 /*
407 * We should never get here!
408 */
409 }
410 else
411 {
412 PsTerminateOtherThread(Thread, ExitStatus);
413 ObDereferenceObject(Thread);
414 }
415 return(STATUS_SUCCESS);
416 }
417
418
419 /*
420 * @implemented
421 */
422 NTSTATUS STDCALL
423 PsTerminateSystemThread(NTSTATUS ExitStatus)
424 /*
425 * FUNCTION: Terminates the current thread
426 * ARGUMENTS:
427 * ExitStatus = Status to pass to the creater
428 * RETURNS: Doesn't
429 */
430 {
431 PsTerminateCurrentThread(ExitStatus);
432 return(STATUS_SUCCESS);
433 }
434
435 NTSTATUS STDCALL
436 NtCallTerminatePorts(PETHREAD Thread)
437 {
438 KIRQL oldIrql;
439 PLIST_ENTRY current_entry;
440 PEPORT_TERMINATION_REQUEST current;
441
442 PAGED_CODE();
443
444 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
445 while ((current_entry = RemoveHeadList(&Thread->TerminationPortList)) !=
446 &Thread->TerminationPortList);
447 {
448 current = CONTAINING_RECORD(current_entry,
449 EPORT_TERMINATION_REQUEST,
450 ThreadListEntry);
451 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
452 LpcSendTerminationPort(current->Port,
453 Thread->CreateTime);
454 ExFreePool(current);
455 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
456 }
457 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
458 return(STATUS_SUCCESS);
459 }
460
461 NTSTATUS STDCALL
462 NtRegisterThreadTerminatePort(HANDLE PortHandle)
463 {
464 NTSTATUS Status;
465 PEPORT_TERMINATION_REQUEST Request;
466 PEPORT TerminationPort;
467 KIRQL oldIrql;
468 PETHREAD Thread;
469
470 PAGED_CODE();
471
472 Status = ObReferenceObjectByHandle(PortHandle,
473 PORT_ALL_ACCESS,
474 LpcPortObjectType,
475 KeGetCurrentThread()->PreviousMode,
476 (PVOID*)&TerminationPort,
477 NULL);
478 if (!NT_SUCCESS(Status))
479 {
480 return(Status);
481 }
482
483 Request = ExAllocatePool(NonPagedPool, sizeof(EPORT_TERMINATION_REQUEST));
484 if(Request != NULL)
485 {
486 Request->Port = TerminationPort;
487 Thread = PsGetCurrentThread();
488 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
489 InsertTailList(&Thread->TerminationPortList, &Request->ThreadListEntry);
490 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
491
492 return(STATUS_SUCCESS);
493 }
494 else
495 {
496 ObDereferenceObject(TerminationPort);
497 return(STATUS_INSUFFICIENT_RESOURCES);
498 }
499 }