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 /* FUNCTIONS *****************************************************************/
29 PsInitializeThreadReaper(VOID
)
31 InitializeListHead(&ThreadsToReapHead
);
39 PLIST_ENTRY ListEntry
;
41 oldlvl
= KeAcquireDispatcherDatabaseLock();
42 while((ListEntry
= RemoveHeadList(&ThreadsToReapHead
)) != &ThreadsToReapHead
)
44 PiNrThreadsAwaitingReaping
--;
45 KeReleaseDispatcherDatabaseLock(oldlvl
);
46 Thread
= CONTAINING_RECORD(ListEntry
, ETHREAD
, TerminationPortList
);
48 ObDereferenceObject(Thread
);
49 oldlvl
= KeAcquireDispatcherDatabaseLock();
51 KeReleaseDispatcherDatabaseLock(oldlvl
);
55 PsQueueThreadReap(PETHREAD Thread
)
57 InsertTailList(&ThreadsToReapHead
, &Thread
->TerminationPortList
);
58 PiNrThreadsAwaitingReaping
++;
62 PiTerminateProcessThreads(PEPROCESS Process
,
66 PLIST_ENTRY current_entry
;
67 PETHREAD current
, CurrentThread
= PsGetCurrentThread();
69 DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n",
72 oldlvl
= KeAcquireDispatcherDatabaseLock();
74 current_entry
= Process
->ThreadListHead
.Flink
;
75 while (current_entry
!= &Process
->ThreadListHead
)
77 current
= CONTAINING_RECORD(current_entry
, ETHREAD
,
79 if (current
!= CurrentThread
&& current
->HasTerminated
== 0)
81 DPRINT("Terminating %x, current thread: %x, "
82 "thread's process: %x\n", current
, PsGetCurrentThread(),
83 current
->ThreadsProcess
);
84 KeReleaseDispatcherDatabaseLock(oldlvl
);
85 PsTerminateOtherThread(current
, ExitStatus
);
86 oldlvl
= KeAcquireDispatcherDatabaseLock();
87 current_entry
= Process
->ThreadListHead
.Flink
;
91 current_entry
= current_entry
->Flink
;
94 KeReleaseDispatcherDatabaseLock(oldlvl
);
95 DPRINT("Finished PiTerminateProcessThreads()\n");
99 PsTerminateCurrentThread(NTSTATUS ExitStatus
)
101 * FUNCTION: Terminates the current thread
105 PETHREAD CurrentThread
;
106 PLIST_ENTRY current_entry
;
109 PEPROCESS CurrentProcess
;
110 SIZE_T Length
= PAGE_SIZE
;
113 KeLowerIrql(PASSIVE_LEVEL
);
115 CurrentThread
= PsGetCurrentThread();
116 CurrentProcess
= CurrentThread
->ThreadsProcess
;
118 /* Can't terminate a thread if it attached another process */
119 if (AttachedApcEnvironment
== CurrentThread
->Tcb
.ApcStateIndex
)
121 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
, (ULONG
) CurrentProcess
,
122 (ULONG
) CurrentThread
->Tcb
.ApcState
.Process
,
123 (ULONG
) CurrentThread
->Tcb
.ApcStateIndex
,
124 (ULONG
) CurrentThread
);
127 KeCancelTimer(&CurrentThread
->Tcb
.Timer
);
129 oldIrql
= KeAcquireDispatcherDatabaseLock();
131 DPRINT("terminating %x\n",CurrentThread
);
133 CurrentThread
->HasTerminated
= TRUE
;
134 CurrentThread
->ExitStatus
= ExitStatus
;
135 KeQuerySystemTime((PLARGE_INTEGER
)&CurrentThread
->ExitTime
);
137 /* If the ProcessoR Control Block's NpxThread points to the current thread
140 InterlockedCompareExchangePointer(&KeGetCurrentKPCR()->PrcbData
.NpxThread
,
141 NULL
, ETHREAD_TO_KTHREAD(CurrentThread
));
143 KeReleaseDispatcherDatabaseLock(oldIrql
);
145 PsLockProcess(CurrentProcess
, FALSE
);
147 /* Remove the thread from the thread list of its process */
148 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
149 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
150 PsUnlockProcess(CurrentProcess
);
152 /* Notify subsystems of the thread termination */
153 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
154 PsTerminateWin32Thread(CurrentThread
);
157 if(CurrentThread
->Tcb
.Teb
)
159 DPRINT("Decommit teb at %p\n", CurrentThread
->Tcb
.Teb
);
160 ExAcquireFastMutex(&CurrentProcess
->TebLock
);
161 TebBlock
= MM_ROUND_DOWN(CurrentThread
->Tcb
.Teb
, MM_VIRTMEM_GRANULARITY
);
162 ZwFreeVirtualMemory(NtCurrentProcess(),
163 (PVOID
*)&CurrentThread
->Tcb
.Teb
,
166 DPRINT("teb %p, TebBlock %p\n", CurrentThread
->Tcb
.Teb
, TebBlock
);
167 if (TebBlock
!= CurrentProcess
->TebBlock
||
168 CurrentProcess
->TebBlock
== CurrentProcess
->TebLastAllocated
)
170 MmLockAddressSpace(&CurrentProcess
->AddressSpace
);
171 MmReleaseMemoryAreaIfDecommitted(CurrentProcess
, &CurrentProcess
->AddressSpace
, TebBlock
);
172 MmUnlockAddressSpace(&CurrentProcess
->AddressSpace
);
174 CurrentThread
->Tcb
.Teb
= NULL
;
175 ExReleaseFastMutex(&CurrentProcess
->TebLock
);
178 /* abandon all owned mutants */
179 current_entry
= CurrentThread
->Tcb
.MutantListHead
.Flink
;
180 while (current_entry
!= &CurrentThread
->Tcb
.MutantListHead
)
182 Mutant
= CONTAINING_RECORD(current_entry
, KMUTANT
,
184 KeReleaseMutant(Mutant
,
188 current_entry
= CurrentThread
->Tcb
.MutantListHead
.Flink
;
191 oldIrql
= KeAcquireDispatcherDatabaseLock();
192 CurrentThread
->Tcb
.DispatcherHeader
.SignalState
= TRUE
;
193 KiDispatcherObjectWake(&CurrentThread
->Tcb
.DispatcherHeader
, IO_NO_INCREMENT
);
194 KeReleaseDispatcherDatabaseLock (oldIrql
);
196 /* The last thread shall close the door on exit */
199 /* save the last thread exit status */
200 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
202 PspRunCreateProcessNotifyRoutines(CurrentProcess
, FALSE
);
203 PsTerminateWin32Process(CurrentProcess
);
204 PiTerminateProcess(CurrentProcess
, ExitStatus
);
207 oldIrql
= KeAcquireDispatcherDatabaseLock();
209 #ifdef _ENABLE_THRDEVTPAIR
210 ExpSwapThreadEventPair(CurrentThread
, NULL
); /* Release the associated eventpair object, if there was one */
211 #endif /* _ENABLE_THRDEVTPAIR */
213 ASSERT(CurrentThread
->Tcb
.WaitBlockList
== NULL
);
215 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1
);
216 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
221 PiTerminateThreadRundownRoutine(PKAPC Apc
)
227 PiTerminateThreadKernelRoutine(PKAPC Apc
,
228 PKNORMAL_ROUTINE
* NormalRoutine
,
229 PVOID
* NormalContext
,
230 PVOID
* SystemArgument1
,
231 PVOID
* SystemArguemnt2
)
237 PiTerminateThreadNormalRoutine(PVOID NormalContext
,
238 PVOID SystemArgument1
,
239 PVOID SystemArgument2
)
241 PETHREAD EThread
= PsGetCurrentThread();
242 if (EThread
->HasTerminated
)
244 /* Someone else has already called PsTerminateCurrentThread */
247 PsTerminateCurrentThread(PsGetCurrentThread()->ExitStatus
);
251 PsTerminateOtherThread(PETHREAD Thread
,
254 * FUNCTION: Terminate a thread when calling from another thread's context
255 * NOTES: This function must be called with PiThreadLock held
261 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
264 OldIrql
= KeAcquireDispatcherDatabaseLock();
265 if (Thread
->HasTerminated
)
267 KeReleaseDispatcherDatabaseLock (OldIrql
);
270 Thread
->HasTerminated
= TRUE
;
271 KeReleaseDispatcherDatabaseLock (OldIrql
);
272 Thread
->ExitStatus
= ExitStatus
;
273 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
276 OriginalApcEnvironment
,
277 PiTerminateThreadKernelRoutine
,
278 PiTerminateThreadRundownRoutine
,
279 PiTerminateThreadNormalRoutine
,
282 KeInsertQueueApc(Apc
,
287 OldIrql
= KeAcquireDispatcherDatabaseLock();
288 if (THREAD_STATE_BLOCKED
== Thread
->Tcb
.State
&& UserMode
== Thread
->Tcb
.WaitMode
)
290 DPRINT("Unblocking thread\n");
291 KiAbortWaitThread((PKTHREAD
)Thread
, STATUS_THREAD_IS_TERMINATING
);
293 KeReleaseDispatcherDatabaseLock(OldIrql
);
297 PiTerminateProcess(PEPROCESS Process
,
301 PEPROCESS CurrentProcess
;
303 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
304 Process
, ExitStatus
, ObGetObjectPointerCount(Process
),
305 ObGetObjectHandleCount(Process
));
307 ObReferenceObject(Process
);
308 if (InterlockedExchangeUL(&Process
->Pcb
.State
,
309 PROCESS_STATE_TERMINATED
) ==
310 PROCESS_STATE_TERMINATED
)
312 ObDereferenceObject(Process
);
313 return(STATUS_SUCCESS
);
315 CurrentProcess
= PsGetCurrentProcess();
316 if (Process
!= CurrentProcess
)
318 KeAttachProcess(&Process
->Pcb
);
320 ObCloseAllHandles(Process
);
321 if (Process
!= CurrentProcess
)
325 OldIrql
= KeAcquireDispatcherDatabaseLock ();
326 Process
->Pcb
.DispatcherHeader
.SignalState
= TRUE
;
327 KiDispatcherObjectWake(&Process
->Pcb
.DispatcherHeader
, IO_NO_INCREMENT
);
328 KeReleaseDispatcherDatabaseLock (OldIrql
);
329 ObDereferenceObject(Process
);
330 return(STATUS_SUCCESS
);
334 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
335 IN NTSTATUS ExitStatus
)
340 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
341 ProcessHandle
, ExitStatus
);
343 Status
= ObReferenceObjectByHandle(ProcessHandle
,
346 KeGetCurrentThread()->PreviousMode
,
349 if (!NT_SUCCESS(Status
))
353 Process
->ExitStatus
= ExitStatus
;
354 PiTerminateProcessThreads(Process
, ExitStatus
);
355 if (PsGetCurrentThread()->ThreadsProcess
== Process
)
357 ObDereferenceObject(Process
);
358 PsTerminateCurrentThread(ExitStatus
);
360 * We should never get here!
362 return(STATUS_SUCCESS
);
364 ObDereferenceObject(Process
);
365 return(STATUS_SUCCESS
);
370 NtTerminateThread(IN HANDLE ThreadHandle
,
371 IN NTSTATUS ExitStatus
)
376 Status
= ObReferenceObjectByHandle(ThreadHandle
,
379 KeGetCurrentThread()->PreviousMode
,
382 if (Status
!= STATUS_SUCCESS
)
387 if (Thread
== PsGetCurrentThread())
389 /* dereference the thread object before we kill our thread */
390 ObDereferenceObject(Thread
);
391 PsTerminateCurrentThread(ExitStatus
);
393 * We should never get here!
398 PsTerminateOtherThread(Thread
, ExitStatus
);
399 ObDereferenceObject(Thread
);
401 return(STATUS_SUCCESS
);
409 PsTerminateSystemThread(NTSTATUS ExitStatus
)
411 * FUNCTION: Terminates the current thread
413 * ExitStatus = Status to pass to the creater
417 PsTerminateCurrentThread(ExitStatus
);
418 return(STATUS_SUCCESS
);
422 NtCallTerminatePorts(PETHREAD Thread
)
425 PLIST_ENTRY current_entry
;
426 PEPORT_TERMINATION_REQUEST current
;
428 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
429 while ((current_entry
= RemoveHeadList(&Thread
->TerminationPortList
)) !=
430 &Thread
->TerminationPortList
);
432 current
= CONTAINING_RECORD(current_entry
,
433 EPORT_TERMINATION_REQUEST
,
435 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
436 LpcSendTerminationPort(current
->Port
,
439 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
441 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
442 return(STATUS_SUCCESS
);
446 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
449 PEPORT_TERMINATION_REQUEST Request
;
450 PEPORT TerminationPort
;
454 Status
= ObReferenceObjectByHandle(PortHandle
,
457 KeGetCurrentThread()->PreviousMode
,
458 (PVOID
*)&TerminationPort
,
460 if (!NT_SUCCESS(Status
))
465 Request
= ExAllocatePool(NonPagedPool
, sizeof(EPORT_TERMINATION_REQUEST
));
468 Request
->Port
= TerminationPort
;
469 Thread
= PsGetCurrentThread();
470 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
471 InsertTailList(&Thread
->TerminationPortList
, &Request
->ThreadListEntry
);
472 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
474 return(STATUS_SUCCESS
);
478 ObDereferenceObject(TerminationPort
);
479 return(STATUS_INSUFFICIENT_RESOURCES
);