update for HEAD-2003021201
[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              BOOLEAN Last;
100              
101              PiNrThreadsAwaitingReaping--;
102              current->Tcb.State = THREAD_STATE_TERMINATED_2;
103              RemoveEntryList(&current->Tcb.ProcessThreadListEntry);
104              Last = IsListEmpty(&Process->ThreadListHead);
105              KeReleaseSpinLock(&PiThreadListLock, oldIrql);
106
107              if (Last)
108              {
109                   PiTerminateProcess(Process, Status);
110              }
111              else
112              {
113                 if (current->Tcb.Teb)
114                 {
115                   /* If this is not the last thread for the process than free the memory
116                      from user stack and teb. */
117                   NTSTATUS Status;
118                   ULONG Length;
119                   ULONG Offset;
120                   PVOID DeallocationStack;
121                   HANDLE ProcessHandle;
122                   Status = ObCreateHandle(PsGetCurrentProcess(), Process, PROCESS_ALL_ACCESS, FALSE, &ProcessHandle);
123                   if (!NT_SUCCESS(Status))
124                   {
125                      DPRINT1("ObCreateHandle failed, status = %x\n", Status);
126                      KeBugCheck(0);
127                   }
128                   Offset = FIELD_OFFSET(TEB, DeallocationStack);
129                   Length = 0;
130                   NtReadVirtualMemory(ProcessHandle, (PVOID)current->Tcb.Teb + Offset, 
131                                       (PVOID)&DeallocationStack, sizeof(PVOID), &Length);
132                   if (DeallocationStack && Length == sizeof(PVOID))
133                   {
134                      NtFreeVirtualMemory(ProcessHandle, &DeallocationStack, &Length, MEM_RELEASE); 
135                   }
136                   Length = PAGE_SIZE;
137                   NtFreeVirtualMemory(ProcessHandle, (PVOID*)&current->Tcb.Teb, &Length, MEM_RELEASE); 
138                   NtClose(ProcessHandle);
139                 }
140              }
141              ObDereferenceObject(current);
142              KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
143              current_entry = PiThreadListHead.Flink;
144           }
145      }
146    KeReleaseSpinLock(&PiThreadListLock, oldIrql);
147 }
148
149 VOID
150 PsTerminateCurrentThread(NTSTATUS ExitStatus)
151 /*
152  * FUNCTION: Terminates the current thread
153  */
154 {
155    KIRQL oldIrql;
156    PETHREAD CurrentThread;
157    PKTHREAD Thread;
158    PLIST_ENTRY current_entry;
159    PKMUTANT Mutant;
160    
161    CurrentThread = PsGetCurrentThread();
162    
163    DPRINT("terminating %x\n",CurrentThread);
164    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
165    
166    CurrentThread->ExitStatus = ExitStatus;
167    Thread = KeGetCurrentThread();
168    KeCancelTimer(&Thread->Timer);
169    KeReleaseSpinLock(&PiThreadListLock, oldIrql);
170    
171    /* abandon all owned mutants */
172    current_entry = Thread->MutantListHead.Flink;
173    while (current_entry != &Thread->MutantListHead)
174      {
175         Mutant = CONTAINING_RECORD(current_entry, KMUTANT,
176                                    MutantListEntry);
177         KeReleaseMutant(Mutant,
178                         MUTANT_INCREMENT,
179                         TRUE,
180                         FALSE);
181         current_entry = Thread->MutantListHead.Flink;
182      }
183    
184    KeAcquireDispatcherDatabaseLock(FALSE);
185    CurrentThread->Tcb.DispatcherHeader.SignalState = TRUE;
186    KeDispatcherObjectWake(&CurrentThread->Tcb.DispatcherHeader);
187    KeReleaseDispatcherDatabaseLock(FALSE);
188
189    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);   
190    PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1);
191    KeBugCheck(0);
192 }
193
194 VOID STDCALL
195 PiTerminateThreadRundownRoutine(PKAPC Apc)
196 {
197   ExFreePool(Apc);
198 }
199
200 VOID STDCALL
201 PiTerminateThreadKernelRoutine(PKAPC Apc,
202                                PKNORMAL_ROUTINE* NormalRoutine,
203                                PVOID* NormalContext,
204                                PVOID* SystemArgument1,
205                                PVOID* SystemArguemnt2)
206 {
207   ExFreePool(Apc);
208 }
209
210 VOID STDCALL
211 PiTerminateThreadNormalRoutine(PVOID NormalContext,
212                              PVOID SystemArgument1,
213                              PVOID SystemArgument2)
214 {
215   PsTerminateCurrentThread(PsGetCurrentThread()->ExitStatus);
216 }
217
218 VOID
219 PsTerminateOtherThread(PETHREAD Thread,
220                        NTSTATUS ExitStatus)
221 /*
222  * FUNCTION: Terminate a thread when calling from another thread's context
223  * NOTES: This function must be called with PiThreadListLock held
224  */
225 {
226   PKAPC Apc;
227
228   DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
229          Thread, ExitStatus);
230   
231   Thread->DeadThread = 1;
232   Thread->ExitStatus = ExitStatus;
233   Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
234   KeInitializeApc(Apc,
235                   &Thread->Tcb,
236                   0,
237                   PiTerminateThreadKernelRoutine,
238                   PiTerminateThreadRundownRoutine,
239                   PiTerminateThreadNormalRoutine,
240                   KernelMode,
241                   NULL);
242   KeInsertQueueApc(Apc,
243                    NULL,
244                    NULL,
245                    KernelMode);
246 }
247
248 NTSTATUS STDCALL
249 PiTerminateProcess(PEPROCESS Process,
250                    NTSTATUS ExitStatus)
251 {
252    DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
253            Process, ExitStatus, ObGetObjectPointerCount(Process),
254            ObGetObjectHandleCount(Process));
255    
256    ObReferenceObject(Process);
257    if (InterlockedExchange((PLONG)&Process->Pcb.State, 
258                            PROCESS_STATE_TERMINATED) == 
259        PROCESS_STATE_TERMINATED)
260      {
261         ObDereferenceObject(Process);
262         return(STATUS_SUCCESS);
263      }
264    KeAttachProcess( Process );
265    ObCloseAllHandles(Process);
266    KeDetachProcess();
267    KeAcquireDispatcherDatabaseLock(FALSE);
268    Process->Pcb.DispatcherHeader.SignalState = TRUE;
269    KeDispatcherObjectWake(&Process->Pcb.DispatcherHeader);
270    KeReleaseDispatcherDatabaseLock(FALSE);
271    ObDereferenceObject(Process);
272    return(STATUS_SUCCESS);
273 }
274
275 NTSTATUS STDCALL
276 NtTerminateProcess(IN   HANDLE          ProcessHandle,
277                    IN   NTSTATUS        ExitStatus)
278 {
279    NTSTATUS Status;
280    PEPROCESS Process;
281    
282    DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
283            ProcessHandle, ExitStatus);
284    
285    Status = ObReferenceObjectByHandle(ProcessHandle,
286                                       PROCESS_TERMINATE,
287                                       PsProcessType,
288                                       UserMode,
289                                       (PVOID*)&Process,
290                                       NULL);
291    if (!NT_SUCCESS(Status))
292      {
293        return(Status);
294      }
295    Process->ExitStatus = ExitStatus;
296    PiTerminateProcessThreads(Process, ExitStatus);
297    if (PsGetCurrentThread()->ThreadsProcess == Process)
298      {
299        ObDereferenceObject(Process);
300        PsTerminateCurrentThread(ExitStatus);
301      }
302    ObDereferenceObject(Process);
303    return(STATUS_SUCCESS);
304 }
305
306
307 NTSTATUS STDCALL
308 NtTerminateThread(IN    HANDLE          ThreadHandle,
309                   IN    NTSTATUS        ExitStatus)
310 {
311    PETHREAD Thread;
312    NTSTATUS Status;
313    
314    Status = ObReferenceObjectByHandle(ThreadHandle,
315                                       THREAD_TERMINATE,
316                                       PsThreadType,
317                                       UserMode,
318                                       (PVOID*)&Thread,
319                                       NULL);
320    if (Status != STATUS_SUCCESS)
321      {
322         return(Status);
323      }
324    
325    ObDereferenceObject(Thread);
326    
327    if (Thread == PsGetCurrentThread())
328      {
329         PsTerminateCurrentThread(ExitStatus);
330      }
331    else
332      {
333         PsTerminateOtherThread(Thread, ExitStatus);
334      }
335    return(STATUS_SUCCESS);
336 }
337
338
339 NTSTATUS STDCALL
340 PsTerminateSystemThread(NTSTATUS ExitStatus)
341 /*
342  * FUNCTION: Terminates the current thread
343  * ARGUMENTS:
344  *         ExitStatus = Status to pass to the creater
345  * RETURNS: Doesn't
346  */
347 {
348    PsTerminateCurrentThread(ExitStatus);
349    return(STATUS_SUCCESS);
350 }
351
352 NTSTATUS STDCALL
353 NtCallTerminatePorts(PETHREAD Thread)
354 {
355    KIRQL oldIrql;
356    PLIST_ENTRY current_entry;
357    PEPORT_TERMINATION_REQUEST current;
358    
359    KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
360    while ((current_entry = RemoveHeadList(&Thread->TerminationPortList)) !=
361           &Thread->TerminationPortList);
362      {
363         current = CONTAINING_RECORD(current_entry,
364                                     EPORT_TERMINATION_REQUEST,
365                                     ThreadListEntry);
366         KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
367         LpcSendTerminationPort(current->Port, 
368                                Thread->CreateTime);
369         KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
370      }
371    KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
372    return(STATUS_SUCCESS);
373 }
374
375 NTSTATUS STDCALL
376 NtRegisterThreadTerminatePort(HANDLE TerminationPortHandle)
377 {
378    NTSTATUS Status;
379    PEPORT_TERMINATION_REQUEST Request;
380    PEPORT TerminationPort;
381    KIRQL oldIrql;
382    PETHREAD Thread;
383    
384    Status = ObReferenceObjectByHandle(TerminationPortHandle,
385                                       PORT_ALL_ACCESS,
386                                       ExPortType,
387                                       UserMode,
388                                       (PVOID*)&TerminationPort,
389                                       NULL);   
390    if (!NT_SUCCESS(Status))
391      {
392         return(Status);
393      }
394    
395    Request = ExAllocatePool(NonPagedPool, sizeof(EPORT_TERMINATION_REQUEST));
396    Request->Port = TerminationPort;
397    Thread = PsGetCurrentThread();
398    KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
399    InsertTailList(&Thread->TerminationPortList, &Request->ThreadListEntry);
400    KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
401    
402    return(STATUS_SUCCESS);
403 }