Fixed ps code to terminate threads correctly
[reactos.git] / reactos / ntoskrnl / ps / kill.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/kill.c
5 * PURPOSE: Terminating a thread
6 * PROGRAMMER: David Welch (welch@cwcom.net)
7 * UPDATE HISTORY:
8 * Created 22/05/98
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ddk/ntddk.h>
14 #include <internal/ps.h>
15 #include <internal/ke.h>
16 #include <internal/mm.h>
17 #include <internal/ob.h>
18 #include <internal/port.h>
19
20 #define NDEBUG
21 #include <internal/debug.h>
22
23 /* GLOBALS *******************************************************************/
24
25 extern ULONG PiNrThreads;
26 extern ULONG PiNrRunnableThreads;
27 extern KSPIN_LOCK PiThreadListLock;
28 extern LIST_ENTRY PiThreadListHead;
29 extern KSPIN_LOCK PiApcLock;
30
31 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
32
33 /* FUNCTIONS *****************************************************************/
34
35 VOID PiTerminateProcessThreads(PEPROCESS Process, NTSTATUS ExitStatus)
36 {
37 KIRQL oldlvl;
38 PLIST_ENTRY current_entry;
39 PETHREAD current;
40
41 DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n",
42 Process, ExitStatus);
43
44 KeAcquireSpinLock(&PiThreadListLock, &oldlvl);
45
46 current_entry = Process->ThreadListHead.Flink;
47 while (current_entry != &Process->ThreadListHead)
48 {
49 current = CONTAINING_RECORD(current_entry,ETHREAD,Tcb.ProcessThreadListEntry);
50 if (current != PsGetCurrentThread())
51 {
52 DPRINT("Terminating %x, current thread: %x, thread's process: %x\n", current, PsGetCurrentThread(), current->ThreadsProcess );
53 PsTerminateOtherThread(current, ExitStatus);
54 }
55 current_entry = current_entry->Flink;
56 }
57 KeReleaseSpinLock(&PiThreadListLock, oldlvl);
58 DPRINT("Finished PiTerminateProcessThreads()\n");
59 }
60
61 VOID PsReapThreads(VOID)
62 {
63 PLIST_ENTRY current_entry;
64 PETHREAD current;
65 KIRQL oldIrql;
66
67 // DPRINT1("PsReapThreads()\n");
68
69 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
70
71 current_entry = PiThreadListHead.Flink;
72
73 while (current_entry != &PiThreadListHead)
74 {
75 current = CONTAINING_RECORD(current_entry, ETHREAD,
76 Tcb.ThreadListEntry);
77
78 current_entry = current_entry->Flink;
79
80 if (current->Tcb.State == THREAD_STATE_TERMINATED_1)
81 {
82 PEPROCESS Process = current->ThreadsProcess;
83 NTSTATUS Status = current->ExitStatus;
84
85 DPRINT("PsProcessType %x\n", PsProcessType);
86 DPRINT("Reaping thread %x\n", current);
87 DPRINT("Ref count %d\n", ObGetReferenceCount(Process));
88 current->Tcb.State = THREAD_STATE_TERMINATED_2;
89 RemoveEntryList(&current->Tcb.ProcessThreadListEntry);
90 if (IsListEmpty(&Process->ThreadListHead))
91 {
92 DPRINT("Last thread terminated, terminating process\n");
93 KeReleaseSpinLock( &PiThreadListLock, oldIrql );
94 PiTerminateProcess(Process, Status);
95 KeAcquireSpinLock( &PiThreadListLock, &oldIrql );
96 }
97 DPRINT("Ref count %d\n", ObGetReferenceCount(Process));
98 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
99 ObDereferenceObject(current);
100 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
101 current_entry = PiThreadListHead.Flink;
102 }
103 }
104 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
105 }
106
107 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus)
108 /*
109 * FUNCTION: Terminates the current thread
110 */
111 {
112 KIRQL oldIrql;
113 PETHREAD CurrentThread;
114
115 CurrentThread = PsGetCurrentThread();
116
117 DPRINT("terminating %x\n",CurrentThread);
118 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
119
120 CurrentThread->ExitStatus = ExitStatus;
121 KeAcquireDispatcherDatabaseLock(FALSE);
122 CurrentThread->Tcb.DispatcherHeader.SignalState = TRUE;
123 KeDispatcherObjectWake(&CurrentThread->Tcb.DispatcherHeader);
124 KeReleaseDispatcherDatabaseLock(FALSE);
125
126 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1);
127 KeBugCheck(0);
128 }
129
130 VOID PsTerminateOtherThread(PETHREAD Thread, NTSTATUS ExitStatus)
131 /*
132 * FUNCTION: Terminate a thread when calling from another thread's context
133 */
134 {
135 KIRQL oldIrql;
136
137 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
138 Thread, ExitStatus);
139
140 KeAcquireSpinLock( &PiThreadListLock, &oldIrql );
141 Thread->DeadThread = 1;
142 Thread->ExitStatus = ExitStatus;
143 if( Thread->Tcb.State == THREAD_STATE_FROZEN && (Thread->Tcb.Alertable || Thread->Tcb.WaitMode == UserMode) )
144 KeRemoveAllWaitsThread( Thread, STATUS_ALERTED );
145 KeReleaseSpinLock( &PiThreadListLock, oldIrql );
146 }
147
148 NTSTATUS STDCALL PiTerminateProcess(PEPROCESS Process,
149 NTSTATUS ExitStatus)
150 {
151 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) RC %d HC %d\n",
152 Process, ExitStatus, ObGetReferenceCount(Process),
153 ObGetHandleCount(Process));
154
155 if (Process->Pcb.ProcessState == PROCESS_STATE_TERMINATED)
156 {
157 return(STATUS_SUCCESS);
158 }
159
160 ObCloseAllHandles(Process);
161 KeAcquireDispatcherDatabaseLock(FALSE);
162 Process->Pcb.ProcessState = PROCESS_STATE_TERMINATED;
163 Process->Pcb.DispatcherHeader.SignalState = TRUE;
164 KeDispatcherObjectWake(&Process->Pcb.DispatcherHeader);
165 KeReleaseDispatcherDatabaseLock(FALSE);
166 DPRINT("RC %d\n", ObGetReferenceCount(Process));
167 return(STATUS_SUCCESS);
168 }
169
170 NTSTATUS STDCALL NtTerminateProcess(IN HANDLE ProcessHandle,
171 IN NTSTATUS ExitStatus)
172 {
173 NTSTATUS Status;
174 PEPROCESS Process;
175
176 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
177 ProcessHandle, ExitStatus);
178
179 Status = ObReferenceObjectByHandle(ProcessHandle,
180 PROCESS_TERMINATE,
181 PsProcessType,
182 UserMode,
183 (PVOID*)&Process,
184 NULL);
185 if (!NT_SUCCESS(Status))
186 {
187 return(Status);
188 }
189
190 PiTerminateProcessThreads(Process, ExitStatus);
191 if( PsGetCurrentThread()->ThreadsProcess == Process )
192 {
193 ObDereferenceObject( Process );
194 PsTerminateCurrentThread( ExitStatus );
195 }
196 ObDereferenceObject(Process);
197 return(STATUS_SUCCESS);
198 }
199
200
201 NTSTATUS STDCALL NtTerminateThread(IN HANDLE ThreadHandle,
202 IN NTSTATUS ExitStatus)
203 {
204 PETHREAD Thread;
205 NTSTATUS Status;
206
207 Status = ObReferenceObjectByHandle(ThreadHandle,
208 THREAD_TERMINATE,
209 PsThreadType,
210 UserMode,
211 (PVOID*)&Thread,
212 NULL);
213 if (Status != STATUS_SUCCESS)
214 {
215 return(Status);
216 }
217
218 ObDereferenceObject(Thread);
219
220 if (Thread == PsGetCurrentThread())
221 {
222 PsTerminateCurrentThread(ExitStatus);
223 }
224 else
225 {
226 PsTerminateOtherThread(Thread, ExitStatus);
227 }
228 return(STATUS_SUCCESS);
229 }
230
231
232 NTSTATUS STDCALL PsTerminateSystemThread(NTSTATUS ExitStatus)
233 /*
234 * FUNCTION: Terminates the current thread
235 * ARGUMENTS:
236 * ExitStatus = Status to pass to the creater
237 * RETURNS: Doesn't
238 */
239 {
240 PsTerminateCurrentThread(ExitStatus);
241 return(STATUS_SUCCESS);
242 }
243
244 NTSTATUS STDCALL NtCallTerminatePorts(PETHREAD Thread)
245 {
246 KIRQL oldIrql;
247 PLIST_ENTRY current_entry;
248 PEPORT_TERMINATION_REQUEST current;
249
250 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
251 while ((current_entry = RemoveHeadList(&Thread->TerminationPortList)) !=
252 &Thread->TerminationPortList);
253 {
254 current = CONTAINING_RECORD(current_entry,
255 EPORT_TERMINATION_REQUEST,
256 ThreadListEntry);
257 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
258 LpcSendTerminationPort(current->Port,
259 Thread->CreateTime);
260 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
261 }
262 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
263 return(STATUS_SUCCESS);
264 }
265
266 NTSTATUS STDCALL NtRegisterThreadTerminatePort(HANDLE TerminationPortHandle)
267 {
268 NTSTATUS Status;
269 PEPORT_TERMINATION_REQUEST Request;
270 PEPORT TerminationPort;
271 KIRQL oldIrql;
272 PETHREAD Thread;
273
274 Status = ObReferenceObjectByHandle(TerminationPortHandle,
275 PORT_ALL_ACCESS,
276 ExPortType,
277 UserMode,
278 (PVOID*)&TerminationPort,
279 NULL);
280 if (!NT_SUCCESS(Status))
281 {
282 return(Status);
283 }
284
285 Request = ExAllocatePool(NonPagedPool, sizeof(Request));
286 Request->Port = TerminationPort;
287 Thread = PsGetCurrentThread();
288 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
289 InsertTailList(&Thread->TerminationPortList, &Request->ThreadListEntry);
290 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
291
292 return(STATUS_SUCCESS);
293 }