1 /* $Id: kill.c,v 1.86 2004/11/21 18:42:58 gdalsnes Exp $
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)
12 /* INCLUDES *****************************************************************/
16 #include <internal/debug.h>
18 /* GLOBALS *******************************************************************/
20 extern KSPIN_LOCK PiThreadLock
;
22 VOID
PsTerminateCurrentThread(NTSTATUS ExitStatus
);
23 NTSTATUS STDCALL
NtCallTerminatePorts(PETHREAD Thread
);
25 #define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C')
27 LIST_ENTRY ThreadsToReapHead
;
29 /* FUNCTIONS *****************************************************************/
32 PsInitializeThreadReaper(VOID
)
34 InitializeListHead(&ThreadsToReapHead
);
42 PLIST_ENTRY ListEntry
;
44 KeAcquireSpinLock(&PiThreadLock
, &oldlvl
);
45 while((ListEntry
= RemoveHeadList(&ThreadsToReapHead
)) != &ThreadsToReapHead
)
47 PiNrThreadsAwaitingReaping
--;
48 KeReleaseSpinLock(&PiThreadLock
, oldlvl
);
50 Thread
= CONTAINING_RECORD(ListEntry
, ETHREAD
, TerminationPortList
);
52 ObDereferenceObject(Thread
);
54 KeAcquireSpinLock(&PiThreadLock
, &oldlvl
);
56 KeReleaseSpinLock(&PiThreadLock
, oldlvl
);
60 PsQueueThreadReap(PETHREAD Thread
)
62 InsertTailList(&ThreadsToReapHead
, &Thread
->TerminationPortList
);
63 PiNrThreadsAwaitingReaping
++;
67 PiTerminateProcessThreads(PEPROCESS Process
,
71 PLIST_ENTRY current_entry
;
72 PETHREAD current
, CurrentThread
= PsGetCurrentThread();
74 DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n",
77 KeAcquireSpinLock(&PiThreadLock
, &oldlvl
);
79 current_entry
= Process
->ThreadListHead
.Flink
;
80 while (current_entry
!= &Process
->ThreadListHead
)
82 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
84 if (current
!= CurrentThread
&& current
->HasTerminated
== 0)
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
;
96 current_entry
= current_entry
->Flink
;
99 KeReleaseSpinLock(&PiThreadLock
, oldlvl
);
100 DPRINT("Finished PiTerminateProcessThreads()\n");
104 PsTerminateCurrentThread(NTSTATUS ExitStatus
)
106 * FUNCTION: Terminates the current thread
110 PETHREAD CurrentThread
;
111 PLIST_ENTRY current_entry
;
114 PEPROCESS CurrentProcess
;
115 SIZE_T Length
= PAGE_SIZE
;
118 KeLowerIrql(PASSIVE_LEVEL
);
120 CurrentThread
= PsGetCurrentThread();
121 CurrentProcess
= CurrentThread
->ThreadsProcess
;
123 /* Can't terminate a thread if it attached another process */
124 if (AttachedApcEnvironment
== CurrentThread
->Tcb
.ApcStateIndex
)
126 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
, (ULONG
) CurrentProcess
,
127 (ULONG
) CurrentThread
->Tcb
.ApcState
.Process
,
128 (ULONG
) CurrentThread
->Tcb
.ApcStateIndex
,
129 (ULONG
) CurrentThread
);
132 KeAcquireSpinLock(&PiThreadLock
, &oldIrql
);
134 DPRINT("terminating %x\n",CurrentThread
);
136 CurrentThread
->HasTerminated
= TRUE
;
137 CurrentThread
->ExitStatus
= ExitStatus
;
138 KeQuerySystemTime((PLARGE_INTEGER
)&CurrentThread
->ExitTime
);
139 KeCancelTimer(&CurrentThread
->Tcb
.Timer
);
141 /* If the ProcessoR Control Block's NpxThread points to the current thread
144 InterlockedCompareExchange((LONG
*)&KeGetCurrentKPCR()->PrcbData
.NpxThread
,
145 (LONG
)NULL
, (LONG
)ETHREAD_TO_KTHREAD(CurrentThread
));
147 KeReleaseSpinLock(&PiThreadLock
, oldIrql
);
149 PsLockProcess(CurrentProcess
, FALSE
);
151 /* Remove the thread from the thread list of its process */
152 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
153 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
154 PsUnlockProcess(CurrentProcess
);
156 /* Notify subsystems of the thread termination */
157 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
158 PsTerminateWin32Thread(CurrentThread
);
161 if(CurrentThread
->Tcb
.Teb
)
163 DPRINT("Decommit teb at %p\n", CurrentThread
->Tcb
.Teb
);
164 ExAcquireFastMutex(&CurrentProcess
->TebLock
);
165 TebBlock
= MM_ROUND_DOWN(CurrentThread
->Tcb
.Teb
, MM_VIRTMEM_GRANULARITY
);
166 ZwFreeVirtualMemory(NtCurrentProcess(),
167 (PVOID
*)&CurrentThread
->Tcb
.Teb
,
170 DPRINT("teb %p, TebBlock %p\n", CurrentThread
->Tcb
.Teb
, TebBlock
);
171 if (TebBlock
!= CurrentProcess
->TebBlock
||
172 CurrentProcess
->TebBlock
== CurrentProcess
->TebLastAllocated
)
174 MmLockAddressSpace(&CurrentProcess
->AddressSpace
);
175 MmReleaseMemoryAreaIfDecommitted(CurrentProcess
, &CurrentProcess
->AddressSpace
, TebBlock
);
176 MmUnlockAddressSpace(&CurrentProcess
->AddressSpace
);
178 ExReleaseFastMutex(&CurrentProcess
->TebLock
);
181 /* abandon all owned mutants */
182 current_entry
= CurrentThread
->Tcb
.MutantListHead
.Flink
;
183 while (current_entry
!= &CurrentThread
->Tcb
.MutantListHead
)
185 Mutant
= CONTAINING_RECORD(current_entry
, KMUTANT
,
187 KeReleaseMutant(Mutant
,
191 current_entry
= CurrentThread
->Tcb
.MutantListHead
.Flink
;
194 oldIrql
= KeAcquireDispatcherDatabaseLock();
195 CurrentThread
->Tcb
.DispatcherHeader
.SignalState
= TRUE
;
196 KiDispatcherObjectWake(&CurrentThread
->Tcb
.DispatcherHeader
);
197 KeReleaseDispatcherDatabaseLock (oldIrql
);
199 /* The last thread shall close the door on exit */
202 /* save the last thread exit status */
203 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
205 PspRunCreateProcessNotifyRoutines(CurrentProcess
, FALSE
);
206 PsTerminateWin32Process(CurrentProcess
);
207 PiTerminateProcess(CurrentProcess
, ExitStatus
);
210 KeAcquireSpinLock(&PiThreadLock
, &oldIrql
);
212 #ifdef _ENABLE_THRDEVTPAIR
213 ExpSwapThreadEventPair(CurrentThread
, NULL
); /* Release the associated eventpair object, if there was one */
214 #endif /* _ENABLE_THRDEVTPAIR */
216 ASSERT(CurrentThread
->Tcb
.WaitBlockList
== NULL
);
218 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1
);
219 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
224 PiTerminateThreadRundownRoutine(PKAPC Apc
)
230 PiTerminateThreadKernelRoutine(PKAPC Apc
,
231 PKNORMAL_ROUTINE
* NormalRoutine
,
232 PVOID
* NormalContext
,
233 PVOID
* SystemArgument1
,
234 PVOID
* SystemArguemnt2
)
240 PiTerminateThreadNormalRoutine(PVOID NormalContext
,
241 PVOID SystemArgument1
,
242 PVOID SystemArgument2
)
244 PsTerminateCurrentThread(PsGetCurrentThread()->ExitStatus
);
248 PsTerminateOtherThread(PETHREAD Thread
,
251 * FUNCTION: Terminate a thread when calling from another thread's context
252 * NOTES: This function must be called with PiThreadLock held
258 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
261 Thread
->HasTerminated
= TRUE
;
262 Thread
->ExitStatus
= ExitStatus
;
263 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
266 OriginalApcEnvironment
,
267 PiTerminateThreadKernelRoutine
,
268 PiTerminateThreadRundownRoutine
,
269 PiTerminateThreadNormalRoutine
,
272 KeInsertQueueApc(Apc
,
277 OldIrql
= KeAcquireDispatcherDatabaseLock();
278 if (THREAD_STATE_BLOCKED
== Thread
->Tcb
.State
&& UserMode
== Thread
->Tcb
.WaitMode
)
280 DPRINT("Unblocking thread\n");
281 KiAbortWaitThread((PKTHREAD
)Thread
, STATUS_THREAD_IS_TERMINATING
);
283 KeReleaseDispatcherDatabaseLock(OldIrql
);
287 PiTerminateProcess(PEPROCESS Process
,
291 PEPROCESS CurrentProcess
;
293 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
294 Process
, ExitStatus
, ObGetObjectPointerCount(Process
),
295 ObGetObjectHandleCount(Process
));
297 ObReferenceObject(Process
);
298 if (InterlockedExchange((PLONG
)&Process
->Pcb
.State
,
299 PROCESS_STATE_TERMINATED
) ==
300 PROCESS_STATE_TERMINATED
)
302 ObDereferenceObject(Process
);
303 return(STATUS_SUCCESS
);
305 CurrentProcess
= PsGetCurrentProcess();
306 if (Process
!= CurrentProcess
)
308 KeAttachProcess(&Process
->Pcb
);
310 ObCloseAllHandles(Process
);
311 if (Process
!= CurrentProcess
)
315 OldIrql
= KeAcquireDispatcherDatabaseLock ();
316 Process
->Pcb
.DispatcherHeader
.SignalState
= TRUE
;
317 KiDispatcherObjectWake(&Process
->Pcb
.DispatcherHeader
);
318 KeReleaseDispatcherDatabaseLock (OldIrql
);
319 ObDereferenceObject(Process
);
320 return(STATUS_SUCCESS
);
324 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
325 IN NTSTATUS ExitStatus
)
330 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
331 ProcessHandle
, ExitStatus
);
333 Status
= ObReferenceObjectByHandle(ProcessHandle
,
336 KeGetCurrentThread()->PreviousMode
,
339 if (!NT_SUCCESS(Status
))
343 Process
->ExitStatus
= ExitStatus
;
344 PiTerminateProcessThreads(Process
, ExitStatus
);
345 if (PsGetCurrentThread()->ThreadsProcess
== Process
)
347 ObDereferenceObject(Process
);
348 PsTerminateCurrentThread(ExitStatus
);
350 * We should never get here!
352 return(STATUS_SUCCESS
);
354 ObDereferenceObject(Process
);
355 return(STATUS_SUCCESS
);
360 NtTerminateThread(IN HANDLE ThreadHandle
,
361 IN NTSTATUS ExitStatus
)
366 Status
= ObReferenceObjectByHandle(ThreadHandle
,
369 KeGetCurrentThread()->PreviousMode
,
372 if (Status
!= STATUS_SUCCESS
)
377 if (Thread
== PsGetCurrentThread())
379 /* dereference the thread object before we kill our thread */
380 ObDereferenceObject(Thread
);
381 PsTerminateCurrentThread(ExitStatus
);
383 * We should never get here!
388 PsTerminateOtherThread(Thread
, ExitStatus
);
389 ObDereferenceObject(Thread
);
391 return(STATUS_SUCCESS
);
399 PsTerminateSystemThread(NTSTATUS ExitStatus
)
401 * FUNCTION: Terminates the current thread
403 * ExitStatus = Status to pass to the creater
407 PsTerminateCurrentThread(ExitStatus
);
408 return(STATUS_SUCCESS
);
412 NtCallTerminatePorts(PETHREAD Thread
)
415 PLIST_ENTRY current_entry
;
416 PEPORT_TERMINATION_REQUEST current
;
418 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
419 while ((current_entry
= RemoveHeadList(&Thread
->TerminationPortList
)) !=
420 &Thread
->TerminationPortList
);
422 current
= CONTAINING_RECORD(current_entry
,
423 EPORT_TERMINATION_REQUEST
,
425 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
426 LpcSendTerminationPort(current
->Port
,
429 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
431 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
432 return(STATUS_SUCCESS
);
436 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
439 PEPORT_TERMINATION_REQUEST Request
;
440 PEPORT TerminationPort
;
444 Status
= ObReferenceObjectByHandle(PortHandle
,
447 KeGetCurrentThread()->PreviousMode
,
448 (PVOID
*)&TerminationPort
,
450 if (!NT_SUCCESS(Status
))
455 Request
= ExAllocatePool(NonPagedPool
, sizeof(EPORT_TERMINATION_REQUEST
));
458 Request
->Port
= TerminationPort
;
459 Thread
= PsGetCurrentThread();
460 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
461 InsertTailList(&Thread
->TerminationPortList
, &Request
->ThreadListEntry
);
462 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
464 return(STATUS_SUCCESS
);
468 ObDereferenceObject(TerminationPort
);
469 return(STATUS_INSUFFICIENT_RESOURCES
);