3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/kill.c
6 * PURPOSE: Terminating a thread
8 * PROGRAMMERS: David Welch (welch@cwcom.net)
11 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
17 /* GLOBALS *******************************************************************/
19 VOID
PsTerminateCurrentThread(NTSTATUS ExitStatus
);
20 NTSTATUS STDCALL
NtCallTerminatePorts(PETHREAD Thread
);
22 #define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C')
24 LIST_ENTRY ThreadsToReapHead
;
26 #define TERMINATE_PROC 0x1
27 #define TERMINATE_APC 0x2
29 /* FUNCTIONS *****************************************************************/
32 PsInitializeThreadReaper(VOID
)
34 InitializeListHead(&ThreadsToReapHead
);
42 PLIST_ENTRY ListEntry
;
44 oldlvl
= KeAcquireDispatcherDatabaseLock();
45 while((ListEntry
= RemoveHeadList(&ThreadsToReapHead
)) != &ThreadsToReapHead
)
47 PiNrThreadsAwaitingReaping
--;
48 KeReleaseDispatcherDatabaseLock(oldlvl
);
49 Thread
= CONTAINING_RECORD(ListEntry
, ETHREAD
, TerminationPortList
);
51 ObDereferenceObject(Thread
);
52 oldlvl
= KeAcquireDispatcherDatabaseLock();
54 KeReleaseDispatcherDatabaseLock(oldlvl
);
58 PsQueueThreadReap(PETHREAD Thread
)
60 InsertTailList(&ThreadsToReapHead
, &Thread
->TerminationPortList
);
61 PiNrThreadsAwaitingReaping
++;
65 PiTerminateProcessThreads(PEPROCESS Process
,
69 PLIST_ENTRY current_entry
;
70 PETHREAD current
, CurrentThread
= PsGetCurrentThread();
72 DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n",
75 oldlvl
= KeAcquireDispatcherDatabaseLock();
77 current_entry
= Process
->ThreadListHead
.Flink
;
78 while (current_entry
!= &Process
->ThreadListHead
)
80 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
82 if (current
!= CurrentThread
&& current
->HasTerminated
== 0)
84 DPRINT("Terminating %x, current thread: %x, "
85 "thread's process: %x\n", current
, PsGetCurrentThread(),
86 current
->ThreadsProcess
);
87 KeReleaseDispatcherDatabaseLock(oldlvl
);
88 PsTerminateOtherThread(current
, ExitStatus
);
89 oldlvl
= KeAcquireDispatcherDatabaseLock();
90 current_entry
= Process
->ThreadListHead
.Flink
;
94 current_entry
= current_entry
->Flink
;
97 KeReleaseDispatcherDatabaseLock(oldlvl
);
98 DPRINT("Finished PiTerminateProcessThreads()\n");
102 PsTerminateCurrentThread(NTSTATUS ExitStatus
)
104 * FUNCTION: Terminates the current thread
108 PETHREAD CurrentThread
;
109 PLIST_ENTRY current_entry
;
112 PEPROCESS CurrentProcess
;
113 SIZE_T Length
= PAGE_SIZE
;
116 DPRINT("PsTerminateCurrentThread(ExitStatus %x)\n", ExitStatus
);
118 CurrentThread
= PsGetCurrentThread();
120 oldIrql
= KeAcquireDispatcherDatabaseLock();
121 if (CurrentThread
->HasTerminated
& TERMINATE_PROC
)
123 KeReleaseDispatcherDatabaseLock(oldIrql
);
126 CurrentThread
->HasTerminated
|= TERMINATE_PROC
;
127 KeReleaseDispatcherDatabaseLock(oldIrql
);
129 KeLowerIrql(PASSIVE_LEVEL
);
131 CurrentProcess
= CurrentThread
->ThreadsProcess
;
133 /* Can't terminate a thread if it attached another process */
134 if (AttachedApcEnvironment
== CurrentThread
->Tcb
.ApcStateIndex
)
136 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
, (ULONG
) CurrentProcess
,
137 (ULONG
) CurrentThread
->Tcb
.ApcState
.Process
,
138 (ULONG
) CurrentThread
->Tcb
.ApcStateIndex
,
139 (ULONG
) CurrentThread
);
142 KeCancelTimer(&CurrentThread
->Tcb
.Timer
);
144 oldIrql
= KeAcquireDispatcherDatabaseLock();
146 DPRINT("terminating %x\n",CurrentThread
);
148 CurrentThread
->ExitStatus
= ExitStatus
;
149 KeQuerySystemTime((PLARGE_INTEGER
)&CurrentThread
->ExitTime
);
151 /* If the ProcessoR Control Block's NpxThread points to the current thread
154 InterlockedCompareExchangePointer(&KeGetCurrentKPCR()->PrcbData
.NpxThread
,
155 NULL
, ETHREAD_TO_KTHREAD(CurrentThread
));
157 KeReleaseDispatcherDatabaseLock(oldIrql
);
159 PsLockProcess(CurrentProcess
, FALSE
);
161 /* Remove the thread from the thread list of its process */
162 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
163 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
164 PsUnlockProcess(CurrentProcess
);
166 /* Notify subsystems of the thread termination */
167 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
168 PsTerminateWin32Thread(CurrentThread
);
171 if(CurrentThread
->Tcb
.Teb
)
173 DPRINT("Decommit teb at %p\n", CurrentThread
->Tcb
.Teb
);
174 ExAcquireFastMutex(&CurrentProcess
->TebLock
);
175 TebBlock
= MM_ROUND_DOWN(CurrentThread
->Tcb
.Teb
, MM_VIRTMEM_GRANULARITY
);
176 ZwFreeVirtualMemory(NtCurrentProcess(),
177 (PVOID
*)&CurrentThread
->Tcb
.Teb
,
180 DPRINT("teb %p, TebBlock %p\n", CurrentThread
->Tcb
.Teb
, TebBlock
);
181 if (TebBlock
!= CurrentProcess
->TebBlock
||
182 CurrentProcess
->TebBlock
== CurrentProcess
->TebLastAllocated
)
184 MmLockAddressSpace(&CurrentProcess
->AddressSpace
);
185 MmReleaseMemoryAreaIfDecommitted(CurrentProcess
, &CurrentProcess
->AddressSpace
, TebBlock
);
186 MmUnlockAddressSpace(&CurrentProcess
->AddressSpace
);
188 CurrentThread
->Tcb
.Teb
= NULL
;
189 ExReleaseFastMutex(&CurrentProcess
->TebLock
);
192 /* abandon all owned mutants */
193 current_entry
= CurrentThread
->Tcb
.MutantListHead
.Flink
;
194 while (current_entry
!= &CurrentThread
->Tcb
.MutantListHead
)
196 Mutant
= CONTAINING_RECORD(current_entry
, KMUTANT
,
198 KeReleaseMutant(Mutant
,
202 current_entry
= CurrentThread
->Tcb
.MutantListHead
.Flink
;
205 oldIrql
= KeAcquireDispatcherDatabaseLock();
206 CurrentThread
->Tcb
.DispatcherHeader
.SignalState
= TRUE
;
207 KiDispatcherObjectWake(&CurrentThread
->Tcb
.DispatcherHeader
, IO_NO_INCREMENT
);
208 KeReleaseDispatcherDatabaseLock (oldIrql
);
210 /* The last thread shall close the door on exit */
213 /* save the last thread exit status */
214 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
216 PspRunCreateProcessNotifyRoutines(CurrentProcess
, FALSE
);
217 PsTerminateWin32Process(CurrentProcess
);
218 PiTerminateProcess(CurrentProcess
, ExitStatus
);
221 oldIrql
= KeAcquireDispatcherDatabaseLock();
223 #ifdef _ENABLE_THRDEVTPAIR
224 ExpSwapThreadEventPair(CurrentThread
, NULL
); /* Release the associated eventpair object, if there was one */
225 #endif /* _ENABLE_THRDEVTPAIR */
227 ASSERT(CurrentThread
->Tcb
.WaitBlockList
== NULL
);
229 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1
);
230 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
235 PiTerminateThreadRundownRoutine(PKAPC Apc
)
241 PiTerminateThreadKernelRoutine(PKAPC Apc
,
242 PKNORMAL_ROUTINE
* NormalRoutine
,
243 PVOID
* NormalContext
,
244 PVOID
* SystemArgument1
,
245 PVOID
* SystemArguemnt2
)
251 PiTerminateThreadNormalRoutine(PVOID NormalContext
,
252 PVOID SystemArgument1
,
253 PVOID SystemArgument2
)
255 PsTerminateCurrentThread((NTSTATUS
)SystemArgument1
);
259 PsTerminateOtherThread(PETHREAD Thread
,
262 * FUNCTION: Terminate a thread when calling from another thread's context
263 * NOTES: This function must be called with PiThreadLock held
269 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
272 OldIrql
= KeAcquireDispatcherDatabaseLock();
273 if (Thread
->HasTerminated
& TERMINATE_APC
)
275 KeReleaseDispatcherDatabaseLock (OldIrql
);
278 Thread
->HasTerminated
|= TERMINATE_APC
;
279 KeReleaseDispatcherDatabaseLock (OldIrql
);
280 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
283 OriginalApcEnvironment
,
284 PiTerminateThreadKernelRoutine
,
285 PiTerminateThreadRundownRoutine
,
286 PiTerminateThreadNormalRoutine
,
289 KeInsertQueueApc(Apc
,
294 OldIrql
= KeAcquireDispatcherDatabaseLock();
295 if (THREAD_STATE_BLOCKED
== Thread
->Tcb
.State
&& UserMode
== Thread
->Tcb
.WaitMode
)
297 DPRINT("Unblocking thread\n");
298 KiAbortWaitThread((PKTHREAD
)Thread
, STATUS_THREAD_IS_TERMINATING
);
300 KeReleaseDispatcherDatabaseLock(OldIrql
);
304 PiTerminateProcess(PEPROCESS Process
,
308 PEPROCESS CurrentProcess
;
310 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
311 Process
, ExitStatus
, ObGetObjectPointerCount(Process
),
312 ObGetObjectHandleCount(Process
));
314 ObReferenceObject(Process
);
315 if (InterlockedExchangeUL(&Process
->Pcb
.State
,
316 PROCESS_STATE_TERMINATED
) ==
317 PROCESS_STATE_TERMINATED
)
319 ObDereferenceObject(Process
);
320 return(STATUS_SUCCESS
);
322 CurrentProcess
= PsGetCurrentProcess();
323 if (Process
!= CurrentProcess
)
325 KeAttachProcess(&Process
->Pcb
);
327 ObCloseAllHandles(Process
);
328 if (Process
!= CurrentProcess
)
332 OldIrql
= KeAcquireDispatcherDatabaseLock ();
333 Process
->Pcb
.DispatcherHeader
.SignalState
= TRUE
;
334 KiDispatcherObjectWake(&Process
->Pcb
.DispatcherHeader
, IO_NO_INCREMENT
);
335 KeReleaseDispatcherDatabaseLock (OldIrql
);
336 ObDereferenceObject(Process
);
337 return(STATUS_SUCCESS
);
341 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
342 IN NTSTATUS ExitStatus
)
349 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
350 ProcessHandle
, ExitStatus
);
352 Status
= ObReferenceObjectByHandle(ProcessHandle
,
355 KeGetCurrentThread()->PreviousMode
,
358 if (!NT_SUCCESS(Status
))
362 Process
->ExitStatus
= ExitStatus
;
363 PiTerminateProcessThreads(Process
, ExitStatus
);
364 if (PsGetCurrentThread()->ThreadsProcess
== Process
)
366 ObDereferenceObject(Process
);
367 PsTerminateCurrentThread(ExitStatus
);
369 * We should never get here!
371 return(STATUS_SUCCESS
);
373 ObDereferenceObject(Process
);
374 return(STATUS_SUCCESS
);
379 NtTerminateThread(IN HANDLE ThreadHandle
,
380 IN NTSTATUS ExitStatus
)
387 Status
= ObReferenceObjectByHandle(ThreadHandle
,
390 KeGetCurrentThread()->PreviousMode
,
393 if (Status
!= STATUS_SUCCESS
)
398 if (Thread
== PsGetCurrentThread())
400 /* dereference the thread object before we kill our thread */
401 ObDereferenceObject(Thread
);
402 PsTerminateCurrentThread(ExitStatus
);
404 * We should never get here!
409 PsTerminateOtherThread(Thread
, ExitStatus
);
410 ObDereferenceObject(Thread
);
412 return(STATUS_SUCCESS
);
420 PsTerminateSystemThread(NTSTATUS ExitStatus
)
422 * FUNCTION: Terminates the current thread
424 * ExitStatus = Status to pass to the creater
428 PsTerminateCurrentThread(ExitStatus
);
429 return(STATUS_SUCCESS
);
433 NtCallTerminatePorts(PETHREAD Thread
)
436 PLIST_ENTRY current_entry
;
437 PEPORT_TERMINATION_REQUEST current
;
441 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
442 while ((current_entry
= RemoveHeadList(&Thread
->TerminationPortList
)) !=
443 &Thread
->TerminationPortList
);
445 current
= CONTAINING_RECORD(current_entry
,
446 EPORT_TERMINATION_REQUEST
,
448 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
449 LpcSendTerminationPort(current
->Port
,
452 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
454 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
455 return(STATUS_SUCCESS
);
459 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
462 PEPORT_TERMINATION_REQUEST Request
;
463 PEPORT TerminationPort
;
469 Status
= ObReferenceObjectByHandle(PortHandle
,
472 KeGetCurrentThread()->PreviousMode
,
473 (PVOID
*)&TerminationPort
,
475 if (!NT_SUCCESS(Status
))
480 Request
= ExAllocatePool(NonPagedPool
, sizeof(EPORT_TERMINATION_REQUEST
));
483 Request
->Port
= TerminationPort
;
484 Thread
= PsGetCurrentThread();
485 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
486 InsertTailList(&Thread
->TerminationPortList
, &Request
->ThreadListEntry
);
487 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
489 return(STATUS_SUCCESS
);
493 ObDereferenceObject(TerminationPort
);
494 return(STATUS_INSUFFICIENT_RESOURCES
);