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 /* Cancel I/O for the thread. */
162 IoCancelThreadIo(CurrentThread
);
164 /* Remove the thread from the thread list of its process */
165 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
166 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
167 PsUnlockProcess(CurrentProcess
);
169 /* Notify subsystems of the thread termination */
170 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
171 PsTerminateWin32Thread(CurrentThread
);
174 if(CurrentThread
->Tcb
.Teb
)
176 DPRINT("Decommit teb at %p\n", CurrentThread
->Tcb
.Teb
);
177 ExAcquireFastMutex(&CurrentProcess
->TebLock
);
178 TebBlock
= MM_ROUND_DOWN(CurrentThread
->Tcb
.Teb
, MM_VIRTMEM_GRANULARITY
);
179 ZwFreeVirtualMemory(NtCurrentProcess(),
180 (PVOID
*)&CurrentThread
->Tcb
.Teb
,
183 DPRINT("teb %p, TebBlock %p\n", CurrentThread
->Tcb
.Teb
, TebBlock
);
184 if (TebBlock
!= CurrentProcess
->TebBlock
||
185 CurrentProcess
->TebBlock
== CurrentProcess
->TebLastAllocated
)
187 MmLockAddressSpace(&CurrentProcess
->AddressSpace
);
188 MmReleaseMemoryAreaIfDecommitted(CurrentProcess
, &CurrentProcess
->AddressSpace
, TebBlock
);
189 MmUnlockAddressSpace(&CurrentProcess
->AddressSpace
);
191 CurrentThread
->Tcb
.Teb
= NULL
;
192 ExReleaseFastMutex(&CurrentProcess
->TebLock
);
195 /* abandon all owned mutants */
196 current_entry
= CurrentThread
->Tcb
.MutantListHead
.Flink
;
197 while (current_entry
!= &CurrentThread
->Tcb
.MutantListHead
)
199 Mutant
= CONTAINING_RECORD(current_entry
, KMUTANT
,
201 KeReleaseMutant(Mutant
,
205 current_entry
= CurrentThread
->Tcb
.MutantListHead
.Flink
;
208 oldIrql
= KeAcquireDispatcherDatabaseLock();
209 CurrentThread
->Tcb
.DispatcherHeader
.SignalState
= TRUE
;
210 KiDispatcherObjectWake(&CurrentThread
->Tcb
.DispatcherHeader
, IO_NO_INCREMENT
);
211 KeReleaseDispatcherDatabaseLock (oldIrql
);
213 /* The last thread shall close the door on exit */
216 /* save the last thread exit status */
217 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
219 PspRunCreateProcessNotifyRoutines(CurrentProcess
, FALSE
);
220 PsTerminateWin32Process(CurrentProcess
);
221 PiTerminateProcess(CurrentProcess
, ExitStatus
);
224 oldIrql
= KeAcquireDispatcherDatabaseLock();
226 #ifdef _ENABLE_THRDEVTPAIR
227 ExpSwapThreadEventPair(CurrentThread
, NULL
); /* Release the associated eventpair object, if there was one */
228 #endif /* _ENABLE_THRDEVTPAIR */
230 ASSERT(CurrentThread
->Tcb
.WaitBlockList
== NULL
);
232 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1
);
233 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
238 PiTerminateThreadRundownRoutine(PKAPC Apc
)
244 PiTerminateThreadKernelRoutine(PKAPC Apc
,
245 PKNORMAL_ROUTINE
* NormalRoutine
,
246 PVOID
* NormalContext
,
247 PVOID
* SystemArgument1
,
248 PVOID
* SystemArguemnt2
)
254 PiTerminateThreadNormalRoutine(PVOID NormalContext
,
255 PVOID SystemArgument1
,
256 PVOID SystemArgument2
)
258 PsTerminateCurrentThread((NTSTATUS
)SystemArgument1
);
262 PsTerminateOtherThread(PETHREAD Thread
,
265 * FUNCTION: Terminate a thread when calling from another thread's context
266 * NOTES: This function must be called with PiThreadLock held
272 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
275 OldIrql
= KeAcquireDispatcherDatabaseLock();
276 if (Thread
->HasTerminated
& TERMINATE_APC
)
278 KeReleaseDispatcherDatabaseLock (OldIrql
);
281 Thread
->HasTerminated
|= TERMINATE_APC
;
282 KeReleaseDispatcherDatabaseLock (OldIrql
);
283 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
286 OriginalApcEnvironment
,
287 PiTerminateThreadKernelRoutine
,
288 PiTerminateThreadRundownRoutine
,
289 PiTerminateThreadNormalRoutine
,
292 KeInsertQueueApc(Apc
,
297 OldIrql
= KeAcquireDispatcherDatabaseLock();
298 if (THREAD_STATE_BLOCKED
== Thread
->Tcb
.State
&& UserMode
== Thread
->Tcb
.WaitMode
)
300 DPRINT("Unblocking thread\n");
301 KiAbortWaitThread((PKTHREAD
)Thread
, STATUS_THREAD_IS_TERMINATING
);
303 KeReleaseDispatcherDatabaseLock(OldIrql
);
307 PiTerminateProcess(PEPROCESS Process
,
311 PEPROCESS CurrentProcess
;
313 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
314 Process
, ExitStatus
, ObGetObjectPointerCount(Process
),
315 ObGetObjectHandleCount(Process
));
317 ObReferenceObject(Process
);
318 if (InterlockedExchangeUL(&Process
->Pcb
.State
,
319 PROCESS_STATE_TERMINATED
) ==
320 PROCESS_STATE_TERMINATED
)
322 ObDereferenceObject(Process
);
323 return(STATUS_SUCCESS
);
325 CurrentProcess
= PsGetCurrentProcess();
326 if (Process
!= CurrentProcess
)
328 KeAttachProcess(&Process
->Pcb
);
330 ObCloseAllHandles(Process
);
331 if (Process
!= CurrentProcess
)
335 OldIrql
= KeAcquireDispatcherDatabaseLock ();
336 Process
->Pcb
.DispatcherHeader
.SignalState
= TRUE
;
337 KiDispatcherObjectWake(&Process
->Pcb
.DispatcherHeader
, IO_NO_INCREMENT
);
338 KeReleaseDispatcherDatabaseLock (OldIrql
);
339 ObDereferenceObject(Process
);
340 return(STATUS_SUCCESS
);
344 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
345 IN NTSTATUS ExitStatus
)
352 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
353 ProcessHandle
, ExitStatus
);
355 Status
= ObReferenceObjectByHandle(ProcessHandle
,
358 KeGetCurrentThread()->PreviousMode
,
361 if (!NT_SUCCESS(Status
))
365 Process
->ExitStatus
= ExitStatus
;
366 PiTerminateProcessThreads(Process
, ExitStatus
);
367 if (PsGetCurrentThread()->ThreadsProcess
== Process
)
369 ObDereferenceObject(Process
);
370 PsTerminateCurrentThread(ExitStatus
);
372 * We should never get here!
374 return(STATUS_SUCCESS
);
376 ObDereferenceObject(Process
);
377 return(STATUS_SUCCESS
);
382 NtTerminateThread(IN HANDLE ThreadHandle
,
383 IN NTSTATUS ExitStatus
)
390 Status
= ObReferenceObjectByHandle(ThreadHandle
,
393 KeGetCurrentThread()->PreviousMode
,
396 if (Status
!= STATUS_SUCCESS
)
401 if (Thread
== PsGetCurrentThread())
403 /* dereference the thread object before we kill our thread */
404 ObDereferenceObject(Thread
);
405 PsTerminateCurrentThread(ExitStatus
);
407 * We should never get here!
412 PsTerminateOtherThread(Thread
, ExitStatus
);
413 ObDereferenceObject(Thread
);
415 return(STATUS_SUCCESS
);
423 PsTerminateSystemThread(NTSTATUS ExitStatus
)
425 * FUNCTION: Terminates the current thread
427 * ExitStatus = Status to pass to the creater
431 PsTerminateCurrentThread(ExitStatus
);
432 return(STATUS_SUCCESS
);
436 NtCallTerminatePorts(PETHREAD Thread
)
439 PLIST_ENTRY current_entry
;
440 PEPORT_TERMINATION_REQUEST current
;
444 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
445 while ((current_entry
= RemoveHeadList(&Thread
->TerminationPortList
)) !=
446 &Thread
->TerminationPortList
);
448 current
= CONTAINING_RECORD(current_entry
,
449 EPORT_TERMINATION_REQUEST
,
451 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
452 LpcSendTerminationPort(current
->Port
,
455 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
457 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
458 return(STATUS_SUCCESS
);
462 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
465 PEPORT_TERMINATION_REQUEST Request
;
466 PEPORT TerminationPort
;
472 Status
= ObReferenceObjectByHandle(PortHandle
,
475 KeGetCurrentThread()->PreviousMode
,
476 (PVOID
*)&TerminationPort
,
478 if (!NT_SUCCESS(Status
))
483 Request
= ExAllocatePool(NonPagedPool
, sizeof(EPORT_TERMINATION_REQUEST
));
486 Request
->Port
= TerminationPort
;
487 Thread
= PsGetCurrentThread();
488 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
489 InsertTailList(&Thread
->TerminationPortList
, &Request
->ThreadListEntry
);
490 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
492 return(STATUS_SUCCESS
);
496 ObDereferenceObject(TerminationPort
);
497 return(STATUS_INSUFFICIENT_RESOURCES
);