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(&KeGetCurrentPrcb()->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
);
222 PiTerminateProcess(CurrentProcess
, ExitStatus
);
225 oldIrql
= KeAcquireDispatcherDatabaseLock();
227 #ifdef _ENABLE_THRDEVTPAIR
228 ExpSwapThreadEventPair(CurrentThread
, NULL
); /* Release the associated eventpair object, if there was one */
229 #endif /* _ENABLE_THRDEVTPAIR */
231 ASSERT(CurrentThread
->Tcb
.WaitBlockList
== NULL
);
233 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1
);
234 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
239 PiTerminateThreadRundownRoutine(PKAPC Apc
)
245 PiTerminateThreadKernelRoutine(PKAPC Apc
,
246 PKNORMAL_ROUTINE
* NormalRoutine
,
247 PVOID
* NormalContext
,
248 PVOID
* SystemArgument1
,
249 PVOID
* SystemArguemnt2
)
255 PiTerminateThreadNormalRoutine(PVOID NormalContext
,
256 PVOID SystemArgument1
,
257 PVOID SystemArgument2
)
259 PsTerminateCurrentThread((NTSTATUS
)SystemArgument1
);
263 PsTerminateOtherThread(PETHREAD Thread
,
266 * FUNCTION: Terminate a thread when calling from another thread's context
267 * NOTES: This function must be called with PiThreadLock held
273 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
276 OldIrql
= KeAcquireDispatcherDatabaseLock();
277 if (Thread
->HasTerminated
& TERMINATE_APC
)
279 KeReleaseDispatcherDatabaseLock (OldIrql
);
282 Thread
->HasTerminated
|= TERMINATE_APC
;
283 KeReleaseDispatcherDatabaseLock (OldIrql
);
284 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
287 OriginalApcEnvironment
,
288 PiTerminateThreadKernelRoutine
,
289 PiTerminateThreadRundownRoutine
,
290 PiTerminateThreadNormalRoutine
,
293 KeInsertQueueApc(Apc
,
298 OldIrql
= KeAcquireDispatcherDatabaseLock();
299 if (THREAD_STATE_BLOCKED
== Thread
->Tcb
.State
&& UserMode
== Thread
->Tcb
.WaitMode
)
301 DPRINT("Unblocking thread\n");
302 KiAbortWaitThread((PKTHREAD
)Thread
, STATUS_THREAD_IS_TERMINATING
);
304 KeReleaseDispatcherDatabaseLock(OldIrql
);
308 PiTerminateProcess(PEPROCESS Process
,
312 PEPROCESS CurrentProcess
;
314 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
315 Process
, ExitStatus
, ObGetObjectPointerCount(Process
),
316 ObGetObjectHandleCount(Process
));
318 ObReferenceObject(Process
);
319 if (Process
->Pcb
.State
== PROCESS_STATE_TERMINATED
)
321 ObDereferenceObject(Process
);
322 return(STATUS_SUCCESS
);
325 Process
->Pcb
.State
= PROCESS_STATE_TERMINATED
;
327 CurrentProcess
= PsGetCurrentProcess();
328 if (Process
!= CurrentProcess
)
330 KeAttachProcess(&Process
->Pcb
);
333 ObDeleteHandleTable(Process
);
334 if(Process
->UniqueProcessId
!= NULL
)
336 PsDeleteCidHandle(Process
->UniqueProcessId
, PsProcessType
);
339 if (Process
!= CurrentProcess
)
343 OldIrql
= KeAcquireDispatcherDatabaseLock ();
344 Process
->Pcb
.DispatcherHeader
.SignalState
= TRUE
;
345 KiDispatcherObjectWake(&Process
->Pcb
.DispatcherHeader
, IO_NO_INCREMENT
);
346 KeReleaseDispatcherDatabaseLock (OldIrql
);
347 ObDereferenceObject(Process
);
348 return(STATUS_SUCCESS
);
352 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
353 IN NTSTATUS ExitStatus
)
360 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
361 ProcessHandle
, ExitStatus
);
363 Status
= ObReferenceObjectByHandle(ProcessHandle
,
366 KeGetCurrentThread()->PreviousMode
,
369 if (!NT_SUCCESS(Status
))
373 Process
->ExitStatus
= ExitStatus
;
374 PiTerminateProcessThreads(Process
, ExitStatus
);
375 if (PsGetCurrentThread()->ThreadsProcess
== Process
)
377 ObDereferenceObject(Process
);
378 PsTerminateCurrentThread(ExitStatus
);
380 * We should never get here!
382 return(STATUS_SUCCESS
);
384 ObDereferenceObject(Process
);
385 return(STATUS_SUCCESS
);
390 NtTerminateThread(IN HANDLE ThreadHandle
,
391 IN NTSTATUS ExitStatus
)
398 Status
= ObReferenceObjectByHandle(ThreadHandle
,
401 KeGetCurrentThread()->PreviousMode
,
404 if (Status
!= STATUS_SUCCESS
)
409 if (Thread
== PsGetCurrentThread())
411 /* dereference the thread object before we kill our thread */
412 ObDereferenceObject(Thread
);
413 PsTerminateCurrentThread(ExitStatus
);
415 * We should never get here!
420 PsTerminateOtherThread(Thread
, ExitStatus
);
421 ObDereferenceObject(Thread
);
423 return(STATUS_SUCCESS
);
431 PsTerminateSystemThread(NTSTATUS ExitStatus
)
433 * FUNCTION: Terminates the current thread
435 * ExitStatus = Status to pass to the creater
439 PsTerminateCurrentThread(ExitStatus
);
440 return(STATUS_SUCCESS
);
444 NtCallTerminatePorts(PETHREAD Thread
)
447 PLIST_ENTRY current_entry
;
448 PEPORT_TERMINATION_REQUEST current
;
452 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
453 while ((current_entry
= RemoveHeadList(&Thread
->TerminationPortList
)) !=
454 &Thread
->TerminationPortList
);
456 current
= CONTAINING_RECORD(current_entry
,
457 EPORT_TERMINATION_REQUEST
,
459 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
460 LpcSendTerminationPort(current
->Port
,
463 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
465 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
466 return(STATUS_SUCCESS
);
470 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
473 PEPORT_TERMINATION_REQUEST Request
;
474 PEPORT TerminationPort
;
480 Status
= ObReferenceObjectByHandle(PortHandle
,
483 KeGetCurrentThread()->PreviousMode
,
484 (PVOID
*)&TerminationPort
,
486 if (!NT_SUCCESS(Status
))
491 Request
= ExAllocatePool(NonPagedPool
, sizeof(EPORT_TERMINATION_REQUEST
));
494 Request
->Port
= TerminationPort
;
495 Thread
= PsGetCurrentThread();
496 KeAcquireSpinLock(&Thread
->ActiveTimerListLock
, &oldIrql
);
497 InsertTailList(&Thread
->TerminationPortList
, &Request
->ThreadListEntry
);
498 KeReleaseSpinLock(&Thread
->ActiveTimerListLock
, oldIrql
);
500 return(STATUS_SUCCESS
);
504 ObDereferenceObject(TerminationPort
);
505 return(STATUS_INSUFFICIENT_RESOURCES
);