7d9fa12140377f0f0312842d15217211ae1a18d5
[reactos.git] / 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  * PROGRAMMER:      David Welch (welch@cwcom.net)
8  * UPDATE HISTORY:
9  *                  Created 22/05/98
10  */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <internal/ps.h>
16 #include <internal/ke.h>
17 #include <internal/mm.h>
18 #include <internal/ob.h>
19 #include <internal/port.h>
20 #include <internal/pool.h>
21
22 #define NDEBUG
23 #include <internal/debug.h>
24
25 /* GLOBALS *******************************************************************/
26
27 extern ULONG PiNrThreads;
28 extern ULONG PiNrRunnableThreads;
29 extern KSPIN_LOCK PiThreadListLock;
30 extern LIST_ENTRY PiThreadListHead;
31 extern KSPIN_LOCK PiApcLock;
32
33 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
34
35 #define TAG_TERMINATE_APC   TAG('T', 'A', 'P', 'C')
36
37 /* FUNCTIONS *****************************************************************/
38
39 VOID
40 PiTerminateProcessThreads(PEPROCESS Process,
41                           NTSTATUS ExitStatus)
42 {
43    KIRQL oldlvl;
44    PLIST_ENTRY current_entry;
45    PETHREAD current;
46    
47    DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n",
48           Process, ExitStatus);
49    
50    KeAcquireSpinLock(&PiThreadListLock, &oldlvl);
51
52    current_entry = Process->ThreadListHead.Flink;
53    while (current_entry != &Process->ThreadListHead)
54      {
55         current = CONTAINING_RECORD(current_entry, ETHREAD,
56                                     Tcb.ProcessThreadListEntry);
57         if (current != PsGetCurrentThread() &&
58             current->DeadThread == 0)
59           {
60              DPRINT("Terminating %x, current thread: %x, "
61                     "thread's process: %x\n", current, PsGetCurrentThread(), 
62                     current->ThreadsProcess);
63              KeReleaseSpinLock(&PiThreadListLock, oldlvl);
64              PsTerminateOtherThread(current, ExitStatus);
65              KeAcquireSpinLock(&PiThreadListLock, &oldlvl);
66              current_entry = Process->ThreadListHead.Flink;
67           }
68         else
69           {
70              current_entry = current_entry->Flink;
71           }
72      }
73    KeReleaseSpinLock(&PiThreadListLock, oldlvl);
74    DPRINT("Finished PiTerminateProcessThreads()\n");
75 }
76
77 VOID
78 PsReapThreads(VOID)
79 {
80    PLIST_ENTRY current_entry;
81    PETHREAD current;
82    KIRQL oldIrql;
83    
84    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
85    
86    current_entry = PiThreadListHead.Flink;
87    
88    while (current_entry != &PiThreadListHead)
89      {
90         current = CONTAINING_RECORD(current_entry, ETHREAD, 
91                                     Tcb.ThreadListEntry);
92         
93         current_entry = current_entry->Flink;
94         
95         if (current->Tcb.State == THREAD_STATE_TERMINATED_1)
96           {
97              PEPROCESS Process = current->ThreadsProcess; 
98              NTSTATUS Status = current->ExitStatus;
99              
100              PiNrThreadsAwaitingReaping--;
101              current->Tcb.State = THREAD_STATE_TERMINATED_2;
102              RemoveEntryList(&current->Tcb.ProcessThreadListEntry);
103              if (IsListEmpty(&Process->ThreadListHead))
104                {
105                   KeReleaseSpinLock( &PiThreadListLock, oldIrql );
106                   PiTerminateProcess(Process, Status);
107                   KeAcquireSpinLock( &PiThreadListLock, &oldIrql );
108                }
109              KeReleaseSpinLock(&PiThreadListLock, oldIrql);
110              ObDereferenceObject(current);
111              KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
112              current_entry = PiThreadListHead.Flink;
113           }
114      }
115    KeReleaseSpinLock(&PiThreadListLock, oldIrql);
116 }
117
118 VOID
119 PsTerminateCurrentThread(NTSTATUS ExitStatus)
120 /*
121  * FUNCTION: Terminates the current thread
122  */
123 {
124    KIRQL oldIrql;
125    PETHREAD CurrentThread;
126    PKTHREAD Thread;
127    PLIST_ENTRY current_entry;
128    PKMUTANT Mutant;
129    
130    CurrentThread = PsGetCurrentThread();
131    
132    DPRINT("terminating %x\n",CurrentThread);
133    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
134    
135    CurrentThread->ExitStatus = ExitStatus;
136    Thread = KeGetCurrentThread();
137    KeCancelTimer(&Thread->Timer);
138    KeReleaseSpinLock(&PiThreadListLock, oldIrql);
139    
140    /* abandon all owned mutants */
141    current_entry = Thread->MutantListHead.Flink;
142    while (current_entry != &Thread->MutantListHead)
143      {
144         Mutant = CONTAINING_RECORD(current_entry, KMUTANT,
145                                    MutantListEntry);
146         KeReleaseMutant(Mutant,
147                         MUTANT_INCREMENT,
148                         TRUE,
149                         FALSE);
150         current_entry = Thread->MutantListHead.Flink;
151      }
152    
153    KeAcquireDispatcherDatabaseLock(FALSE);
154    CurrentThread->Tcb.DispatcherHeader.SignalState = TRUE;
155    KeDispatcherObjectWake(&CurrentThread->Tcb.DispatcherHeader);
156    KeReleaseDispatcherDatabaseLock(FALSE);
157
158    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);   
159    PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1);
160    KeBugCheck(0);
161 }
162
163 VOID STDCALL
164 PiTerminateThreadRundownRoutine(PKAPC Apc)
165 {
166   ExFreePool(Apc);
167 }
168
169 VOID STDCALL
170 PiTerminateThreadKernelRoutine(PKAPC Apc,
171                                PKNORMAL_ROUTINE* NormalRoutine,
172                                PVOID* NormalContext,
173                                PVOID* SystemArgument1,
174                                PVOID* SystemArguemnt2)
175 {
176   ExFreePool(Apc);
177 }
178
179 VOID STDCALL
180 PiTerminateThreadNormalRoutine(PVOID NormalContext,
181                              PVOID SystemArgument1,
182                              PVOID SystemArgument2)
183 {
184   PsTerminateCurrentThread(PsGetCurrentThread()->ExitStatus);
185 }
186
187 VOID
188 PsTerminateOtherThread(PETHREAD Thread,
189                        NTSTATUS ExitStatus)
190 /*
191  * FUNCTION: Terminate a thread when calling from another thread's context
192  * NOTES: This function must be called with PiThreadListLock held
193  */
194 {
195   PKAPC Apc;
196
197   DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
198          Thread, ExitStatus);
199   
200   Thread->DeadThread = 1;
201   Thread->ExitStatus = ExitStatus;
202   Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
203   KeInitializeApc(Apc,
204                   &Thread->Tcb,
205                   0,
206                   PiTerminateThreadKernelRoutine,
207                   PiTerminateThreadRundownRoutine,
208                   PiTerminateThreadNormalRoutine,
209                   KernelMode,
210                   NULL);
211   KeInsertQueueApc(Apc,
212                    NULL,
213                    NULL,
214                    KernelMode);
215 }
216
217 NTSTATUS STDCALL
218 PiTerminateProcess(PEPROCESS Process,
219                    NTSTATUS ExitStatus)
220 {
221    DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
222            Process, ExitStatus, ObGetObjectPointerCount(Process),
223            ObGetObjectHandleCount(Process));
224    
225    ObReferenceObject(Process);
226    if (InterlockedExchange((PLONG)&Process->Pcb.State, 
227                            PROCESS_STATE_TERMINATED) == 
228        PROCESS_STATE_TERMINATED)
229      {
230         ObDereferenceObject(Process);
231         return(STATUS_SUCCESS);
232      }
233    KeAttachProcess( Process );
234    ObCloseAllHandles(Process);
235    KeDetachProcess();
236    KeAcquireDispatcherDatabaseLock(FALSE);
237    Process->Pcb.DispatcherHeader.SignalState = TRUE;
238    KeDispatcherObjectWake(&Process->Pcb.DispatcherHeader);
239    KeReleaseDispatcherDatabaseLock(FALSE);
240    ObDereferenceObject(Process);
241    return(STATUS_SUCCESS);
242 }
243
244 NTSTATUS STDCALL
245 NtTerminateProcess(IN   HANDLE          ProcessHandle,
246                    IN   NTSTATUS        ExitStatus)
247 {
248    NTSTATUS Status;
249    PEPROCESS Process;
250    
251    DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
252            ProcessHandle, ExitStatus);
253    
254    Status = ObReferenceObjectByHandle(ProcessHandle,
255                                       PROCESS_TERMINATE,
256                                       PsProcessType,
257                                       UserMode,
258                                       (PVOID*)&Process,
259                                       NULL);
260    if (!NT_SUCCESS(Status))
261      {
262        return(Status);
263      }
264    Process->ExitStatus = ExitStatus;
265    PiTerminateProcessThreads(Process, ExitStatus);
266    if (PsGetCurrentThread()->ThreadsProcess == Process)
267      {
268        ObDereferenceObject(Process);
269        PsTerminateCurrentThread(ExitStatus);
270      }
271    ObDereferenceObject(Process);
272    return(STATUS_SUCCESS);
273 }
274
275
276 NTSTATUS STDCALL
277 NtTerminateThread(IN    HANDLE          ThreadHandle,
278                   IN    NTSTATUS        ExitStatus)
279 {
280    PETHREAD Thread;
281    NTSTATUS Status;
282    
283    Status = ObReferenceObjectByHandle(ThreadHandle,
284                                       THREAD_TERMINATE,
285                                       PsThreadType,
286                                       UserMode,
287                                       (PVOID*)&Thread,
288                                       NULL);
289    if (Status != STATUS_SUCCESS)
290      {
291         return(Status);
292      }
293    
294    ObDereferenceObject(Thread);
295    
296    if (Thread == PsGetCurrentThread())
297      {
298         PsTerminateCurrentThread(ExitStatus);
299      }
300    else
301      {
302         PsTerminateOtherThread(Thread, ExitStatus);
303      }
304    return(STATUS_SUCCESS);
305 }
306
307
308 NTSTATUS STDCALL
309 PsTerminateSystemThread(NTSTATUS ExitStatus)
310 /*
311  * FUNCTION: Terminates the current thread
312  * ARGUMENTS:
313  *         ExitStatus = Status to pass to the creater
314  * RETURNS: Doesn't
315  */
316 {
317    PsTerminateCurrentThread(ExitStatus);
318    return(STATUS_SUCCESS);
319 }
320
321 NTSTATUS STDCALL
322 NtCallTerminatePorts(PETHREAD Thread)
323 {
324    KIRQL oldIrql;
325    PLIST_ENTRY current_entry;
326    PEPORT_TERMINATION_REQUEST current;
327    
328    KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
329    while ((current_entry = RemoveHeadList(&Thread->TerminationPortList)) !=
330           &Thread->TerminationPortList);
331      {
332         current = CONTAINING_RECORD(current_entry,
333                                     EPORT_TERMINATION_REQUEST,
334                                     ThreadListEntry);
335         KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
336         LpcSendTerminationPort(current->Port, 
337                                Thread->CreateTime);
338         KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
339      }
340    KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
341    return(STATUS_SUCCESS);
342 }
343
344 NTSTATUS STDCALL
345 NtRegisterThreadTerminatePort(HANDLE TerminationPortHandle)
346 {
347    NTSTATUS Status;
348    PEPORT_TERMINATION_REQUEST Request;
349    PEPORT TerminationPort;
350    KIRQL oldIrql;
351    PETHREAD Thread;
352    
353    Status = ObReferenceObjectByHandle(TerminationPortHandle,
354                                       PORT_ALL_ACCESS,
355                                       ExPortType,
356                                       UserMode,
357                                       (PVOID*)&TerminationPort,
358                                       NULL);   
359    if (!NT_SUCCESS(Status))
360      {
361         return(Status);
362      }
363    
364    Request = ExAllocatePool(NonPagedPool, sizeof(EPORT_TERMINATION_REQUEST));
365    Request->Port = TerminationPort;
366    Thread = PsGetCurrentThread();
367    KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
368    InsertTailList(&Thread->TerminationPortList, &Request->ThreadListEntry);
369    KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
370    
371    return(STATUS_SUCCESS);
372 }