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