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