2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/kill.c
5 * PURPOSE: Thread Termination and Reaping
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
11 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
17 /* GLOBALS *******************************************************************/
19 #define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C')
21 PETHREAD PspReaperList
= NULL
;
22 WORK_QUEUE_ITEM PspReaperWorkItem
;
23 BOOLEAN PspReaping
= FALSE
;
24 extern LIST_ENTRY PsActiveProcessHead
;
25 extern FAST_MUTEX PspActiveProcessMutex
;
27 /* FUNCTIONS *****************************************************************/
31 PspReapRoutine(PVOID Context
)
34 PETHREAD Thread
, NewThread
;
37 DPRINT("Evil reaper running!!\n");
38 OldIrql
= KeAcquireDispatcherDatabaseLock();
40 /* Get the first Thread Entry */
41 Thread
= PspReaperList
;
43 DPRINT("PspReaperList: %x\n", Thread
);
45 /* Check to see if the list is empty */
48 /* Unlock the Dispatcher */
49 KeReleaseDispatcherDatabaseLock(OldIrql
);
51 /* Is there a thread on the list? */
54 /* Get the next Thread */
55 DPRINT("Thread: %x\n", Thread
);
56 DPRINT("Thread: %x\n", Thread
->ReaperLink
);
57 NewThread
= Thread
->ReaperLink
;
59 /* Remove reference to current thread */
60 ObDereferenceObject(Thread
);
62 /* Move to next Thread */
66 /* No more linked threads... Reacquire the Lock */
67 OldIrql
= KeAcquireDispatcherDatabaseLock();
69 /* Now try to get a new thread from the list */
70 Thread
= PspReaperList
;
72 DPRINT("PspReaperList: %x\n", Thread
);
74 /* Loop again if there is a new thread */
78 DPRINT("Done reaping\n");
79 KeReleaseDispatcherDatabaseLock(OldIrql
);
84 PspTerminateProcessThreads(PEPROCESS Process
,
87 PLIST_ENTRY CurrentEntry
;
88 PETHREAD Thread
, CurrentThread
= PsGetCurrentThread();
90 CurrentEntry
= Process
->ThreadListHead
.Flink
;
91 while (CurrentEntry
!= &Process
->ThreadListHead
) {
93 /* Get the Current Thread */
94 Thread
= CONTAINING_RECORD(CurrentEntry
, ETHREAD
, ThreadListEntry
);
96 /* Move to the Next Thread */
97 CurrentEntry
= CurrentEntry
->Flink
;
99 /* Make sure it's not the one we're in */
100 if (Thread
!= CurrentThread
) {
102 /* Make sure it didn't already terminate */
103 if (!Thread
->HasTerminated
) {
105 Thread
->HasTerminated
= TRUE
;
107 /* Terminate it by APC */
108 PspTerminateThreadByPointer(Thread
, ExitStatus
);
116 PspDeleteProcess(PVOID ObjectBody
)
118 PEPROCESS Process
= (PEPROCESS
)ObjectBody
;
120 DPRINT("PiDeleteProcess(ObjectBody %x)\n",Process
);
122 /* Delete the CID Handle */
123 if(Process
->UniqueProcessId
!= NULL
) {
125 PsDeleteCidHandle(Process
->UniqueProcessId
, PsProcessType
);
129 KDB_DELETEPROCESS_HOOK(Process
);
131 /* Dereference the Token and release Memory Information */
132 ObDereferenceObject(Process
->Token
);
133 MmReleaseMmInfo(Process
);
135 /* Delete the W32PROCESS structure if there's one associated */
136 if(Process
->Win32Process
!= NULL
) ExFreePool(Process
->Win32Process
);
141 PspDeleteThread(PVOID ObjectBody
)
143 PETHREAD Thread
= (PETHREAD
)ObjectBody
;
144 PEPROCESS Process
= Thread
->ThreadsProcess
;
146 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody
);
148 /* Deassociate the Process */
149 Thread
->ThreadsProcess
= NULL
;
151 /* Delete the CID Handle */
152 if(Thread
->Cid
.UniqueThread
!= NULL
) {
154 PsDeleteCidHandle(Thread
->Cid
.UniqueThread
, PsThreadType
);
157 /* Free the W32THREAD structure if present */
158 if(Thread
->Tcb
.Win32Thread
!= NULL
) ExFreePool (Thread
->Tcb
.Win32Thread
);
160 /* Release the Thread */
161 KeReleaseThread(ETHREAD_TO_KTHREAD(Thread
));
163 /* Dereference the Process */
164 ObDereferenceObject(Process
);
168 * FUNCTION: Terminates the current thread
169 * See "Windows Internals" - Chapter 13, Page 50-53
173 PspExitThread(NTSTATUS ExitStatus
)
175 PETHREAD CurrentThread
;
177 PEPROCESS CurrentProcess
;
178 SIZE_T Length
= PAGE_SIZE
;
180 PTERMINATION_PORT TerminationPort
;
182 DPRINT("PsTerminateCurrentThread(ExitStatus %x), Current: 0x%x\n", ExitStatus
, PsGetCurrentThread());
184 /* Get the Current Thread and Process */
185 CurrentThread
= PsGetCurrentThread();
186 CurrentThread
->HasTerminated
= TRUE
;
187 CurrentProcess
= CurrentThread
->ThreadsProcess
;
189 /* Can't terminate a thread if it attached another process */
190 if (KeIsAttachedProcess()) {
192 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
, (ULONG
) CurrentProcess
,
193 (ULONG
) CurrentThread
->Tcb
.ApcState
.Process
,
194 (ULONG
) CurrentThread
->Tcb
.ApcStateIndex
,
195 (ULONG
) CurrentThread
);
198 /* Lower to Passive Level */
199 KeLowerIrql(PASSIVE_LEVEL
);
201 /* Run Thread Notify Routines before we desintegrate the thread */
202 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
204 /* Set the Exit Status and Exit Time */
205 CurrentThread
->ExitStatus
= ExitStatus
;
206 KeQuerySystemTime(&CurrentThread
->ExitTime
);
208 /* Lock the Process before we modify its thread entries */
209 PsLockProcess(CurrentProcess
, FALSE
);
211 /* Remove the thread from the thread list of its process */
212 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
213 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
215 /* Set the last Thread Exit Status */
216 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
220 /* Save the Exit Time if not already done by NtTerminateProcess. This
221 happens when the last thread just terminates without explicitly
222 terminating the process. */
223 CurrentProcess
->ExitTime
= CurrentThread
->ExitTime
;
226 /* Check if the process has a debug port */
227 if (CurrentProcess
->DebugPort
) {
229 /* Notify the Debug API. TODO */
230 //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
233 /* Process the Termination Ports */
234 TerminationPort
= CurrentThread
->TerminationPort
;
235 DPRINT("TerminationPort: %p\n", TerminationPort
);
236 while (TerminationPort
) {
238 /* Send the LPC Message */
239 LpcSendTerminationPort(TerminationPort
->Port
, CurrentThread
->CreateTime
);
242 ExFreePool(TerminationPort
);
244 /* Get the next one */
245 TerminationPort
= TerminationPort
->Next
;
246 DPRINT("TerminationPort: %p\n", TerminationPort
);
249 /* Rundown Win32 Structures */
250 PsTerminateWin32Thread(CurrentThread
);
251 if (Last
) PsTerminateWin32Process(CurrentProcess
);
253 /* Cancel I/O for the thread. */
254 IoCancelThreadIo(CurrentThread
);
256 /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
257 //CmNotifyRunDown(CurrentThread);
260 if(CurrentThread
->Tcb
.Teb
) {
262 DPRINT("Decommit teb at %p\n", CurrentThread
->Tcb
.Teb
);
263 ExAcquireFastMutex(&CurrentProcess
->TebLock
);
264 TebBlock
= MM_ROUND_DOWN(CurrentThread
->Tcb
.Teb
, MM_VIRTMEM_GRANULARITY
);
266 ZwFreeVirtualMemory(NtCurrentProcess(),
267 (PVOID
*)&CurrentThread
->Tcb
.Teb
,
271 DPRINT("teb %p, TebBlock %p\n", CurrentThread
->Tcb
.Teb
, TebBlock
);
273 if (TebBlock
!= CurrentProcess
->TebBlock
||
274 CurrentProcess
->TebBlock
== CurrentProcess
->TebLastAllocated
) {
276 MmLockAddressSpace(&CurrentProcess
->AddressSpace
);
277 MmReleaseMemoryAreaIfDecommitted(CurrentProcess
, &CurrentProcess
->AddressSpace
, TebBlock
);
278 MmUnlockAddressSpace(&CurrentProcess
->AddressSpace
);
281 CurrentThread
->Tcb
.Teb
= NULL
;
282 ExReleaseFastMutex(&CurrentProcess
->TebLock
);
285 /* The last Thread shuts down the Process */
286 if (Last
) PspExitProcess(CurrentProcess
);
288 /* Unlock the Process */
289 PsUnlockProcess(CurrentProcess
);
293 KeCancelTimer(&CurrentThread
->Tcb
.Timer
);
295 /* If the Processor Control Block's NpxThread points to the current thread
298 InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread
,
300 (PKPROCESS
)CurrentThread
);
302 /* Rundown Mutexes */
305 /* Terminate the Thread from the Scheduler */
306 KeTerminateThread(0);
307 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
313 PsExitSpecialApc(PKAPC Apc
,
314 PKNORMAL_ROUTINE
* NormalRoutine
,
315 PVOID
* NormalContext
,
316 PVOID
* SystemArgument1
,
317 PVOID
* SystemArguemnt2
)
319 NTSTATUS ExitStatus
= (NTSTATUS
)Apc
->NormalContext
;
321 DPRINT("PsExitSpecialApc called: 0x%x\n", PsGetCurrentThread());
326 /* Terminate the Thread */
327 PspExitThread(ExitStatus
);
329 /* we should never reach this point! */
335 PspExitNormalApc(PVOID NormalContext
,
336 PVOID SystemArgument1
,
337 PVOID SystemArgument2
)
339 /* Not fully supported yet... must work out some issues that
340 * I don't understand yet -- Alex
343 PspExitThread((NTSTATUS
)NormalContext
);
345 /* we should never reach this point! */
350 * See "Windows Internals" - Chapter 13, Page 49
354 PspTerminateThreadByPointer(PETHREAD Thread
,
359 DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
362 /* Check if we are already in the right context */
363 if (PsGetCurrentThread() == Thread
) {
365 /* Directly terminate the thread */
366 PspExitThread(ExitStatus
);
368 /* we should never reach this point! */
372 /* Allocate the APC */
373 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
375 /* Initialize a Kernel Mode APC to Kill the Thread */
378 OriginalApcEnvironment
,
385 /* Insert it into the APC Queue */
386 KeInsertQueueApc(Apc
,
391 /* Forcefully resume the thread */
392 KeForceResumeThread(&Thread
->Tcb
);
397 PspExitProcess(PEPROCESS Process
)
399 DPRINT("PspExitProcess\n");
401 PspRunCreateProcessNotifyRoutines(Process
, FALSE
);
403 /* Remove it from the Active List */
404 ExAcquireFastMutex(&PspActiveProcessMutex
);
405 RemoveEntryList(&Process
->ProcessListEntry
);
406 ExReleaseFastMutex(&PspActiveProcessMutex
);
408 /* close all handles associated with our process, this needs to be done
409 when the last thread still runs */
410 ObKillProcess(Process
);
412 KeSetProcess(&Process
->Pcb
, IO_NO_INCREMENT
);
414 return(STATUS_SUCCESS
);
419 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
420 IN NTSTATUS ExitStatus
)
427 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
428 ProcessHandle
, ExitStatus
);
430 /* Get the Process Object */
431 Status
= ObReferenceObjectByHandle(ProcessHandle
,
437 if (!NT_SUCCESS(Status
)) {
439 DPRINT1("Invalid handle to Process\n");
443 PsLockProcess(Process
, FALSE
);
445 if(Process
->ExitTime
.QuadPart
!= 0)
447 PsUnlockProcess(Process
);
448 return STATUS_PROCESS_IS_TERMINATING
;
451 /* Terminate all the Process's Threads */
452 PspTerminateProcessThreads(Process
, ExitStatus
);
454 /* Only master thread remains... kill it off */
455 if (PsGetCurrentThread()->ThreadsProcess
== Process
) {
457 /* set the exit time as we're about to release the process lock before
458 we kill ourselves to prevent threads outside of our process trying
460 KeQuerySystemTime(&Process
->ExitTime
);
462 PsUnlockProcess(Process
);
464 /* we can safely dereference the process because the current thread
465 holds a reference to it until it gets reaped */
466 ObDereferenceObject(Process
);
468 /* now the other threads get a chance to terminate, we don't wait but
469 just kill ourselves right now. The process will be run down when the
470 last thread terminates */
472 PspExitThread(ExitStatus
);
474 /* we should never reach this point! */
479 /* unlock and dereference the process so the threads can kill themselves */
480 PsUnlockProcess(Process
);
481 ObDereferenceObject(Process
);
484 return(STATUS_SUCCESS
);
489 NtTerminateThread(IN HANDLE ThreadHandle
,
490 IN NTSTATUS ExitStatus
)
497 /* Get the Thread Object */
498 Status
= ObReferenceObjectByHandle(ThreadHandle
,
504 if (Status
!= STATUS_SUCCESS
) {
506 DPRINT1("Could not reference thread object\n");
510 /* Make sure this is not a system thread */
511 if (PsIsSystemThread(Thread
)) {
513 DPRINT1("Trying to Terminate a system thread!\n");
514 ObDereferenceObject(Thread
);
515 return STATUS_INVALID_PARAMETER
;
518 /* Check to see if we're running in the same thread */
519 if (Thread
!= PsGetCurrentThread()) {
521 /* This isn't our thread, check if it's terminated already */
522 if (!Thread
->HasTerminated
) {
525 PspTerminateThreadByPointer(Thread
, ExitStatus
);
530 /* it's safe to dereference thread, there's at least the keep-alive
531 reference which will be removed by the thread reaper causing the
532 thread to be finally destroyed */
533 ObDereferenceObject(Thread
);
535 /* Terminate him, he's ours */
536 PspExitThread(ExitStatus
);
538 /* We do never reach this point */
542 /* Dereference the Thread and return */
543 ObDereferenceObject(Thread
);
544 return(STATUS_SUCCESS
);
552 PsTerminateSystemThread(NTSTATUS ExitStatus
)
554 PETHREAD Thread
= PsGetCurrentThread();
556 /* Make sure this is a system thread */
557 if (!PsIsSystemThread(Thread
)) {
559 DPRINT1("Trying to Terminate a non-system thread!\n");
560 return STATUS_INVALID_PARAMETER
;
563 /* Terminate it for real */
564 PspExitThread(ExitStatus
);
566 /* we should never reach this point! */
569 return(STATUS_SUCCESS
);
574 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
577 PTERMINATION_PORT TerminationPort
;
578 PVOID TerminationLpcPort
;
584 Status
= ObReferenceObjectByHandle(PortHandle
,
590 if (!NT_SUCCESS(Status
)) {
592 DPRINT1("Failed to reference Port\n");
596 /* Allocate the Port and make sure it suceeded */
597 if((TerminationPort
= ExAllocatePoolWithTag(NonPagedPool
,
598 sizeof(PTERMINATION_PORT
),
599 TAG('P', 's', 'T', '=')))) {
601 /* Associate the Port */
602 Thread
= PsGetCurrentThread();
603 TerminationPort
->Port
= TerminationLpcPort
;
604 DPRINT("TerminationPort: %p\n", TerminationPort
);
605 TerminationPort
->Next
= Thread
->TerminationPort
;
606 Thread
->TerminationPort
= TerminationPort
;
607 DPRINT("TerminationPort: %p\n", Thread
->TerminationPort
);
610 return(STATUS_SUCCESS
);
614 /* Dereference and Fail */
615 ObDereferenceObject(TerminationPort
);
616 return(STATUS_INSUFFICIENT_RESOURCES
);