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 VOID
PsTerminateCurrentThread(NTSTATUS ExitStatus
);
21 NTSTATUS STDCALL
NtCallTerminatePorts(PETHREAD Thread
);
23 #define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C')
25 LIST_ENTRY ThreadsToReapHead
;
27 /* FUNCTIONS *****************************************************************/
30 PsInitializeThreadReaper(VOID
)
32 InitializeListHead(&ThreadsToReapHead
);
40 PLIST_ENTRY ListEntry
;
42 oldlvl
= KeAcquireDispatcherDatabaseLock();
43 while((ListEntry
= RemoveHeadList(&ThreadsToReapHead
)) != &ThreadsToReapHead
)
45 PiNrThreadsAwaitingReaping
--;
46 KeReleaseDispatcherDatabaseLock(oldlvl
);
47 Thread
= CONTAINING_RECORD(ListEntry
, ETHREAD
, TerminationPortList
);
49 ObDereferenceObject(Thread
);
50 oldlvl
= KeAcquireDispatcherDatabaseLock();
52 KeReleaseDispatcherDatabaseLock(oldlvl
);
56 PsQueueThreadReap(PETHREAD Thread
)
58 InsertTailList(&ThreadsToReapHead
, &Thread
->TerminationPortList
);
59 PiNrThreadsAwaitingReaping
++;
63 PiTerminateProcessThreads(PEPROCESS Process
,
67 PLIST_ENTRY current_entry
;
68 PETHREAD current
, CurrentThread
= PsGetCurrentThread();
70 DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n",
73 oldlvl
= KeAcquireDispatcherDatabaseLock();
75 current_entry
= Process
->ThreadListHead
.Flink
;
76 while (current_entry
!= &Process
->ThreadListHead
)
78 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
80 if (current
!= CurrentThread
&& current
->HasTerminated
== 0)
82 DPRINT("Terminating %x, current thread: %x, "
83 "thread's process: %x\n", current
, PsGetCurrentThread(),
84 current
->ThreadsProcess
);
85 KeReleaseDispatcherDatabaseLock(oldlvl
);
86 PsTerminateOtherThread(current
, ExitStatus
);
87 oldlvl
= KeAcquireDispatcherDatabaseLock();
88 current_entry
= Process
->ThreadListHead
.Flink
;
92 current_entry
= current_entry
->Flink
;
95 KeReleaseDispatcherDatabaseLock(oldlvl
);
96 DPRINT("Finished PiTerminateProcessThreads()\n");
100 PsTerminateCurrentThread(NTSTATUS ExitStatus
)
102 * FUNCTION: Terminates the current thread
106 PETHREAD CurrentThread
;
107 PLIST_ENTRY current_entry
;
110 PEPROCESS CurrentProcess
;
111 SIZE_T Length
= PAGE_SIZE
;
114 KeLowerIrql(PASSIVE_LEVEL
);
116 CurrentThread
= PsGetCurrentThread();
117 CurrentProcess
= CurrentThread
->ThreadsProcess
;
119 /* Can't terminate a thread if it attached another process */
120 if (AttachedApcEnvironment
== CurrentThread
->Tcb
.ApcStateIndex
)
122 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
, (ULONG
) CurrentProcess
,
123 (ULONG
) CurrentThread
->Tcb
.ApcState
.Process
,
124 (ULONG
) CurrentThread
->Tcb
.ApcStateIndex
,
125 (ULONG
) CurrentThread
);
128 KeCancelTimer(&CurrentThread
->Tcb
.Timer
);
130 oldIrql
= KeAcquireDispatcherDatabaseLock();
132 DPRINT("terminating %x\n",CurrentThread
);
134 CurrentThread
->HasTerminated
= TRUE
;
135 CurrentThread
->ExitStatus
= ExitStatus
;
136 KeQuerySystemTime((PLARGE_INTEGER
)&CurrentThread
->ExitTime
);
138 /* If the ProcessoR Control Block's NpxThread points to the current thread
141 InterlockedCompareExchangePointer(&KeGetCurrentKPCR()->PrcbData
.NpxThread
,
142 NULL
, ETHREAD_TO_KTHREAD(CurrentThread
));
144 KeReleaseDispatcherDatabaseLock(oldIrql
);
146 PsLockProcess(CurrentProcess
, FALSE
);
148 /* Remove the thread from the thread list of its process */
149 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
150 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
151 PsUnlockProcess(CurrentProcess
);
153 /* Notify subsystems of the thread termination */
154 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
155 PsTerminateWin32Thread(CurrentThread
);
158 if(CurrentThread
->Tcb
.Teb
)
160 DPRINT("Decommit teb at %p\n", CurrentThread
->Tcb
.Teb
);
161 ExAcquireFastMutex(&CurrentProcess
->TebLock
);
162 TebBlock
= MM_ROUND_DOWN(CurrentThread
->Tcb
.Teb
, MM_VIRTMEM_GRANULARITY
);
163 ZwFreeVirtualMemory(NtCurrentProcess(),
164 (PVOID
*)&CurrentThread
->Tcb
.Teb
,
167 DPRINT("teb %p, TebBlock %p\n", CurrentThread
->Tcb
.Teb
, TebBlock
);
168 if (TebBlock
!= CurrentProcess
->TebBlock
||
169 CurrentProcess
->TebBlock
== CurrentProcess
->TebLastAllocated
)
171 MmLockAddressSpace(&CurrentProcess
->AddressSpace
);
172 MmReleaseMemoryAreaIfDecommitted(CurrentProcess
, &CurrentProcess
->AddressSpace
, TebBlock
);
173 MmUnlockAddressSpace(&CurrentProcess
->AddressSpace
);
175 CurrentThread
->Tcb
.Teb
= NULL
;
176 ExReleaseFastMutex(&CurrentProcess
->TebLock
);
179 /* abandon all owned mutants */
180 current_entry
= CurrentThread
->Tcb
.MutantListHead
.Flink
;
181 while (current_entry
!= &CurrentThread
->Tcb
.MutantListHead
)
183 Mutant
= CONTAINING_RECORD(current_entry
, KMUTANT
,
185 KeReleaseMutant(Mutant
,
189 current_entry
= CurrentThread
->Tcb
.MutantListHead
.Flink
;
192 oldIrql
= KeAcquireDispatcherDatabaseLock();
193 CurrentThread
->Tcb
.DispatcherHeader
.SignalState
= TRUE
;
194 KiDispatcherObjectWake(&CurrentThread
->Tcb
.DispatcherHeader
);
195 KeReleaseDispatcherDatabaseLock (oldIrql
);
197 /* The last thread shall close the door on exit */
200 /* save the last thread exit status */
201 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
203 PspRunCreateProcessNotifyRoutines(CurrentProcess
, FALSE
);
204 PsTerminateWin32Process(CurrentProcess
);
205 PiTerminateProcess(CurrentProcess
, ExitStatus
);
208 oldIrql
= KeAcquireDispatcherDatabaseLock();
210 #ifdef _ENABLE_THRDEVTPAIR
211 ExpSwapThreadEventPair(CurrentThread
, NULL
); /* Release the associated eventpair object, if there was one */
212 #endif /* _ENABLE_THRDEVTPAIR */
214 ASSERT(CurrentThread
->Tcb
.WaitBlockList
== NULL
);
216 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1
);
217 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
222 PiTerminateThreadRundownRoutine(PKAPC Apc
)
228 PiTerminateThreadKernelRoutine(PKAPC Apc
,
229 PKNORMAL_ROUTINE
* NormalRoutine
,
230 PVOID
* NormalContext
,
231 PVOID
* SystemArgument1
,
232 PVOID
* SystemArguemnt2
)
238 PiTerminateThreadNormalRoutine(PVOID NormalContext
,
239 PVOID SystemArgument1
,
240 PVOID SystemArgument2
)
242 PETHREAD EThread
= PsGetCurrentThread();
243 if (EThread
->HasTerminated
)
245 /* Someone else has already called PsTerminateCurrentThread */
248 PsTerminateCurrentThread(PsGetCurrentThread()->ExitStatus
);
252 PsTerminateOtherThread(PETHREAD Thread
,
255 * FUNCTION: Terminate a thread when calling from another thread's context
256 * NOTES: This function must be called with PiThreadLock held
262 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
265 OldIrql
= KeAcquireDispatcherDatabaseLock();
266 if (Thread
->HasTerminated
)
268 KeReleaseDispatcherDatabaseLock (OldIrql
);
271 Thread
->HasTerminated
= TRUE
;
272 KeReleaseDispatcherDatabaseLock (OldIrql
);
273 Thread
->ExitStatus
= ExitStatus
;
274 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
277 OriginalApcEnvironment
,
278 PiTerminateThreadKernelRoutine
,
279 PiTerminateThreadRundownRoutine
,
280 PiTerminateThreadNormalRoutine
,
283 KeInsertQueueApc(Apc
,
288 OldIrql
= KeAcquireDispatcherDatabaseLock();
289 if (THREAD_STATE_BLOCKED
== Thread
->Tcb
.State
&& UserMode
== Thread
->Tcb
.WaitMode
)
291 DPRINT("Unblocking thread\n");
292 KiAbortWaitThread((PKTHREAD
)Thread
, STATUS_THREAD_IS_TERMINATING
);
294 KeReleaseDispatcherDatabaseLock(OldIrql
);
298 PiTerminateProcess(PEPROCESS Process
,
302 PEPROCESS CurrentProcess
;
304 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
305 Process
, ExitStatus
, ObGetObjectPointerCount(Process
),
306 ObGetObjectHandleCount(Process
));
308 ObReferenceObject(Process
);
309 if (InterlockedExchangeUL(&Process
->Pcb
.State
,
310 PROCESS_STATE_TERMINATED
) ==
311 PROCESS_STATE_TERMINATED
)
313 ObDereferenceObject(Process
);
314 return(STATUS_SUCCESS
);
316 CurrentProcess
= PsGetCurrentProcess();
317 if (Process
!= CurrentProcess
)
319 KeAttachProcess(&Process
->Pcb
);
321 ObCloseAllHandles(Process
);
322 if (Process
!= CurrentProcess
)
326 OldIrql
= KeAcquireDispatcherDatabaseLock ();
327 Process
->Pcb
.DispatcherHeader
.SignalState
= TRUE
;
328 KiDispatcherObjectWake(&Process
->Pcb
.DispatcherHeader
);
329 KeReleaseDispatcherDatabaseLock (OldIrql
);
330 ObDereferenceObject(Process
);
331 return(STATUS_SUCCESS
);
335 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
336 IN NTSTATUS ExitStatus
)
341 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
342 ProcessHandle
, ExitStatus
);
344 Status
= ObReferenceObjectByHandle(ProcessHandle
,
347 KeGetCurrentThread()->PreviousMode
,
350 if (!NT_SUCCESS(Status
))
354 Process
->ExitStatus
= ExitStatus
;
355 PiTerminateProcessThreads(Process
, ExitStatus
);
356 if (PsGetCurrentThread()->ThreadsProcess
== Process
)
358 ObDereferenceObject(Process
);
359 PsTerminateCurrentThread(ExitStatus
);
361 * We should never get here!
363 return(STATUS_SUCCESS
);
365 ObDereferenceObject(Process
);
366 return(STATUS_SUCCESS
);
371 NtTerminateThread(IN HANDLE ThreadHandle
,
372 IN NTSTATUS ExitStatus
)
377 Status
= ObReferenceObjectByHandle(ThreadHandle
,
380 KeGetCurrentThread()->PreviousMode
,
383 if (Status
!= STATUS_SUCCESS
)
388 if (Thread
== PsGetCurrentThread())
390 /* dereference the thread object before we kill our thread */
391 ObDereferenceObject(Thread
);
392 PsTerminateCurrentThread(ExitStatus
);
394 * We should never get here!
399 PsTerminateOtherThread(Thread
, ExitStatus
);
400 ObDereferenceObject(Thread
);
402 return(STATUS_SUCCESS
);
410 PsTerminateSystemThread(NTSTATUS ExitStatus
)
412 * FUNCTION: Terminates the current thread
414 * ExitStatus = Status to pass to the creater
418 PsTerminateCurrentThread(ExitStatus
);
419 return(STATUS_SUCCESS
);
423 NtCallTerminatePorts(PETHREAD Thread
)
426 PLIST_ENTRY current_entry
;
427 PEPORT_TERMINATION_REQUEST current
;
429 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
430 while ((current_entry
= RemoveHeadList(&Thread
->TerminationPortList
)) !=
431 &Thread
->TerminationPortList
);
433 current
= CONTAINING_RECORD(current_entry
,
434 EPORT_TERMINATION_REQUEST
,
436 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
437 LpcSendTerminationPort(current
->Port
,
440 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
442 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
443 return(STATUS_SUCCESS
);
447 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
450 PEPORT_TERMINATION_REQUEST Request
;
451 PEPORT TerminationPort
;
455 Status
= ObReferenceObjectByHandle(PortHandle
,
458 KeGetCurrentThread()->PreviousMode
,
459 (PVOID
*)&TerminationPort
,
461 if (!NT_SUCCESS(Status
))
466 Request
= ExAllocatePool(NonPagedPool
, sizeof(EPORT_TERMINATION_REQUEST
));
469 Request
->Port
= TerminationPort
;
470 Thread
= PsGetCurrentThread();
471 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
472 InsertTailList(&Thread
->TerminationPortList
, &Request
->ThreadListEntry
);
473 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
475 return(STATUS_SUCCESS
);
479 ObDereferenceObject(TerminationPort
);
480 return(STATUS_INSUFFICIENT_RESOURCES
);